/* src/vm/jit/verify/typeinfo.h - type system used by the type checker

   Copyright (C) 1996-2005, 2006, 2007, 2008
   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO

   This file is part of CACAO.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, 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
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

*/

#ifndef _TYPEINFO_H
#define _TYPEINFO_H

/* resolve typedef cycles *****************************************************/

typedef struct typeinfo            typeinfo_t;
typedef struct typeinfo_mergedlist typeinfo_mergedlist_t;
typedef struct typedescriptor      typedescriptor_t;

#include "config.h"
#include "vm/types.h"

#include "vm/global.h"

#include "vmcore/references.h"


/* configuration **************************************************************/

/*
 * TYPECHECK_STATISTICS activates gathering statistical information.
 * TYPEINFO_DEBUG activates debug checks and debug helpers in typeinfo.c
 * TYPECHECK_DEBUG activates debug checks in typecheck.c
 * TYPEINFO_DEBUG_TEST activates the typeinfo test at startup.
 * TYPECHECK_VERBOSE_IMPORTANT activates important debug messages
 * TYPECHECK_VERBOSE activates all debug messages
 * TYPEINFO_VERBOSE activates debug prints in typeinfo.c
 */
#ifdef ENABLE_VERIFIER
#ifndef NDEBUG
/*#define TYPECHECK_STATISTICS*/
#define TYPEINFO_DEBUG
/*#define TYPEINFO_VERBOSE*/
#define TYPECHECK_DEBUG
/*#define TYPEINFO_DEBUG_TEST*/
/*#define TYPECHECK_VERBOSE*/
/*#define TYPECHECK_VERBOSE_IMPORTANT*/
#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
#define TYPECHECK_VERBOSE_OPT
#endif
#endif
#endif

#ifdef TYPECHECK_VERBOSE_OPT
extern bool opt_typecheckverbose;
#endif

/* types **********************************************************************/

/* typecheck_result - return type for boolean and tristate  functions     */
/*                    which may also throw exceptions (typecheck_FAIL).   */

/* NOTE: Use the enum values, not the uppercase #define macros!          */
#define TYPECHECK_MAYBE  0x02
#define TYPECHECK_FAIL   0x04

typedef enum {
	typecheck_FALSE = false,
	typecheck_TRUE  = true,
	typecheck_MAYBE = TYPECHECK_MAYBE,
	typecheck_FAIL  = TYPECHECK_FAIL
} typecheck_result;

/* check that typecheck_MAYBE is not ambiguous */
#if TYPECHECK_MAYBE == true
#error "`typecheck_MAYBE` must not be the same as `true`"
#endif
#if TYPECHECK_MAYBE == false
#error "`typecheck_MAYBE` must not be the same as `false`"
#endif

/* check that typecheck_FAIL is not ambiguous */
#if (true & TYPECHECK_FAIL) != 0
#error "`true` must not have bit 0x02 set (conflicts with typecheck_FAIL)"
#endif

/* data structures for the type system ****************************************/

/* The typeinfo structure stores detailed information on address types.
 * (stack elements, variables, etc. with type == TYPE_ADR.)
 *
 * There are two kinds of address types which can be distinguished by
 * the value of the typeclass field:
 *
 * 1) typeclass == NULL: returnAddress type
 *                       use TYPEINFO_IS_PRIMITIVE to test for this
 *
 * 2) typeclass != NULL: reference type
 *                       use TYPEINFO_IS_REFERENCE to test for this
 *
 * Note: For non-address types either there is no typeinfo allocated
 * or the fields of the typeinfo struct contain undefined values!
 * DO NOT access the typeinfo for non-address types!
 *
 * CAUTION: The typeinfo structure should be considered opaque outside of
 *          typeinfo.[ch]. Please use the macros and functions defined here to
 *          access typeinfo structures!
 */

/* At all times *exactly one* of the following conditions is true for
 * a particular typeinfo struct:
 *
 * A) typeclass == NULL
 *
 *        This is a returnAddress type.
 *
 *        Use TYPEINFO_IS_PRIMITIVE to check for this.
 *        Use TYPEINFO_RETURNADDRESS to access the pointer in elementclass.
 *        Don't access other fields of the struct.
 *
 * B) typeclass == pseudo_class_Null
 *
 *        This is the null-reference type. 
 *        Use TYPEINFO_IS_NULLTYPE to check for this.
 *        Don't access other fields of the struct.
 *
 * C) typeclass == pseudo_class_New
 *
 *        This is an 'uninitialized object' type. elementclass can be
 *        cast to instruction* and points to the NEW instruction
 *        responsible for creating this type.
 *
 *        Use TYPEINFO_NEWOBJECT_INSTRUCTION to access the pointer in
 *        elementclass.
 *        Don't access other fields of the struct.
 *
 * D) typeclass == pseudo_class_Arraystub
 *
 *        This type is used to represent the result of merging array types
 *        with incompatible component types. An arraystub allows no access
 *        to its components (since their type is undefined), but it allows
 *        operations which act directly on an arbitrary array type (such as
 *        requesting the array size).
 *
 *        NOTE: An array stub does *not* count as an array. It has dimension
 *              zero.
 *
 *        Otherwise like a normal class reference type.
 *        Don't access other fields of the struct.
 *
 * E) typeclass is an array class
 *
 *        An array reference.
 *            elementclass...typeclass of the element type
 *            dimension......dimension of the array (>=1)
 *            elementtype....element type (ARRAYTYPE_...)
 *            merged.........mergedlist of the element type
 *
 *        Use TYPEINFO_IS_ARRAY to check for this case.
 *
 *        The elementclass may be one of the following:
 *        1) pseudo_class_Arraystub
 *        2) an unresolved type
 *        3) a loaded interface
 *        4) a loaded (non-pseudo-,non-array-)class != (BOOTSTRAP)java.lang.Object
 *                Note: `merged` may be used
 *        5) (BOOTSTRAP)java.lang.Object
 *                Note: `merged` may be used
 *
 *        For the semantics of the merged field in cases 4) and 5) consult the 
 *        corresponding descriptions with `elementclass` replaced by `typeclass`.
 *
 * F) typeclass is an unresolved type (a symbolic class/interface reference)
 *
 *        The type has not been resolved yet. (Meaning it corresponds to an
 *        unloaded class or interface).
 *        Don't access other fields of the struct.
 *
 * G) typeclass is a loaded interface
 *
 *        An interface reference type.
 *        Don't access other fields of the struct.
 *
 * H) typeclass is a loaded (non-pseudo-,non-array-)class != (BOOTSTRAP)java.lang.Object
 *
 *        A loaded class type.
 *        All classref_or_classinfos in u.merged.list (if any) are
 *        loaded subclasses of typeclass (no interfaces, array classes, or
 *        unresolved types).
 *        Don't access other fields of the struct.
 *
 * I) typeclass is (BOOTSTRAP)java.lang.Object
 *
 *        The most general kind of reference type.
 *        In this case u.merged.count and u.merged.list
 *        are valid and may be non-zero.
 *        The classref_or_classinfos in u.merged.list (if any) may be
 *        classes, interfaces, pseudo classes or unresolved types.
 *        Don't access other fields of the struct.
 */

/* The following algorithm is used to determine if the type described
 * by this typeinfo struct supports the interface X:  * XXX add MAYBE *
 *
 *     1) If typeclass is X or a subinterface of X the answer is "yes".
 *     2) If typeclass is a (pseudo) class implementing X the answer is "yes".
 *     3) If typeclass is not an array and u.merged.count>0
 *        and all classes/interfaces in u.merged.list implement X
 *        the answer is "yes".
 *     4) If none of the above is true the answer is "no".
 */

/*
 * CAUTION: The typeinfo structure should be considered opaque outside of
 *          typeinfo.[ch]. Please use the macros and functions defined here to
 *          access typeinfo structures!
 */
struct typeinfo {
	classref_or_classinfo  typeclass;
	classref_or_classinfo  elementclass; /* valid if dimension>0 */ /* various uses! */
	typeinfo_mergedlist_t *merged;
	u1                     dimension;
	u1                     elementtype;  /* valid if dimension>0           */
};

struct typeinfo_mergedlist {
	s4                    count;
	classref_or_classinfo list[1];       /* variable length!                        */
};

/* a type descriptor stores a basic type and the typeinfo                */
/* this is used for storing the type of a local variable, and for        */
/* storing types in the signature of a method                            */

struct typedescriptor {
	typeinfo_t      typeinfo; /* valid if type == TYPE_ADR               */
	u1              type;     /* basic type (TYPE_INT, ...)              */
};

/****************************************************************************/
/* MACROS                                                                   */
/****************************************************************************/

/* NOTE: The TYPEINFO macros take typeinfo *structs*, not pointers as
 *       arguments.  You have to dereference any pointers.
 */

/* typevectors **************************************************************/

#define TYPEVECTOR_SIZE(size)						\
    ((size) * sizeof(varinfo)) 

#define DNEW_TYPEVECTOR(size)						\
    ((varinfo *) DMNEW(uint8_t, TYPEVECTOR_SIZE(size)))

#define DMNEW_TYPEVECTOR(num,size)						\
    ((void *) DMNEW(uint8_t, (num) * TYPEVECTOR_SIZE(size)))

#define MGET_TYPEVECTOR(array,index,size) \
    ((varinfo*) (((u1*)(array)) + TYPEVECTOR_SIZE(size) * (index)))

/* internally used macros ***************************************************/

/* internal, don't use this explicitly! */
#define TYPEINFO_ALLOCMERGED(mergedlist,count)					\
    do {(mergedlist) = (typeinfo_mergedlist_t *) DMNEW(uint8_t,	\
            sizeof(typeinfo_mergedlist_t)                       \
            + ((count)-1)*sizeof(classinfo*));} while(0)

/* internal, don't use this explicitly! */
#define TYPEINFO_FREEMERGED(mergedlist)

/* internal, don't use this explicitly! */
#define TYPEINFO_FREEMERGED_IF_ANY(mergedlist)

/* macros for type queries **************************************************/

#define TYPEINFO_IS_PRIMITIVE(info)                             \
            ((info).typeclass.any == NULL)

#define TYPEINFO_IS_REFERENCE(info)                             \
            ((info).typeclass.any != NULL)

#define TYPEINFO_IS_NULLTYPE(info)                              \
            ((info).typeclass.cls == pseudo_class_Null)

#define TYPEINFO_IS_NEWOBJECT(info)                             \
            ((info).typeclass.cls == pseudo_class_New)

#define TYPEINFO_IS_JAVA_LANG_CLASS(info)                       \
            ((info).typeclass.cls == class_java_lang_Class)

/* only use this if TYPEINFO_IS_PRIMITIVE returned true! */
#define TYPEINFO_RETURNADDRESS(info)                            \
            ((info).elementclass.any)

/* only use this if TYPEINFO_IS_NEWOBJECT returned true! */
#define TYPEINFO_NEWOBJECT_INSTRUCTION(info)                    \
		((info).elementclass.any)

/* only use this if TYPEINFO_IS_JAVA_LANG_CLASS returned true! */
#define TYPEINFO_JAVA_LANG_CLASS_CLASSREF(info)                 \
		((info).elementclass.ref)

/* macros for array type queries ********************************************/

#define TYPEINFO_IS_ARRAY(info)                                 \
            ( TYPEINFO_IS_REFERENCE(info)                       \
              && ((info).dimension != 0) )

#define TYPEINFO_IS_SIMPLE_ARRAY(info)                          \
            ( ((info).dimension == 1) )

#define TYPEINFO_IS_ARRAY_ARRAY(info)                           \
            ( ((info).dimension >= 2) )

#define TYPEINFO_IS_PRIMITIVE_ARRAY(info,arraytype)             \
            ( TYPEINFO_IS_SIMPLE_ARRAY(info)                    \
              && ((info).elementtype == (arraytype)) )

#define TYPEINFO_IS_OBJECT_ARRAY(info)                          \
            ( TYPEINFO_IS_SIMPLE_ARRAY(info)                    \
              && ((info).elementclass.any != NULL) )

/* assumes that info describes an array type */
#define TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info)                 \
            ( ((info).elementclass.any != NULL)                 \
              || ((info).dimension >= 2) )

#define TYPEINFO_IS_ARRAY_OF_REFS(info)                         \
            ( TYPEINFO_IS_ARRAY(info)                           \
              && TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info) )

#define TYPE_IS_RETURNADDRESS(type,info)                        \
            ( ((type)==TYPE_RET)                                \
              && TYPEINFO_IS_PRIMITIVE(info) )

#define TYPE_IS_REFERENCE(type,info)                            \
            ( ((type)==TYPE_ADR)                                \
              && !TYPEINFO_IS_PRIMITIVE(info) )

#define TYPEDESC_IS_RETURNADDRESS(td)                           \
            TYPE_IS_RETURNADDRESS((td).type,(td).typeinfo)

#define TYPEDESC_IS_REFERENCE(td)                               \
            TYPE_IS_REFERENCE((td).type,(td).typeinfo)

/* queries allowing the null type ********************************************/

#define TYPEINFO_MAYBE_ARRAY(info)                              \
    (TYPEINFO_IS_ARRAY(info) || TYPEINFO_IS_NULLTYPE(info))

#define TYPEINFO_MAYBE_PRIMITIVE_ARRAY(info,at)                 \
    (TYPEINFO_IS_PRIMITIVE_ARRAY(info,at) || TYPEINFO_IS_NULLTYPE(info))

#define TYPEINFO_MAYBE_ARRAY_OF_REFS(info)                      \
    (TYPEINFO_IS_ARRAY_OF_REFS(info) || TYPEINFO_IS_NULLTYPE(info))

/* macros for initializing typeinfo structures ******************************/

#define TYPEINFO_INIT_PRIMITIVE(info)                           \
         do {(info).typeclass.any = NULL;                       \
             (info).elementclass.any = NULL;                    \
             (info).merged = NULL;                              \
             (info).dimension = 0;                              \
             (info).elementtype = 0;} while(0)

#define TYPEINFO_INIT_RETURNADDRESS(info,adr)                   \
         do {(info).typeclass.any = NULL;                       \
             (info).elementclass.any = (adr);                   \
             (info).merged = NULL;                              \
             (info).dimension = 0;                              \
             (info).elementtype = 0;} while(0)

#define TYPEINFO_INIT_NON_ARRAY_CLASSINFO(info,cinfo)   \
         do {(info).typeclass.cls = (cinfo);            \
             (info).elementclass.any = NULL;            \
             (info).merged = NULL;                      \
             (info).dimension = 0;                      \
             (info).elementtype = 0;} while(0)

#define TYPEINFO_INIT_JAVA_LANG_CLASS(info,c)                   \
         do {(info).typeclass.any = class_java_lang_Class;      \
             (info).elementclass = (c);                         \
             (info).merged = NULL;                              \
             (info).dimension = 0;                              \
             (info).elementtype = 0;} while(0)

#define TYPEINFO_INIT_NULLTYPE(info)                            \
            TYPEINFO_INIT_NON_ARRAY_CLASSINFO(info,pseudo_class_Null)

#define TYPEINFO_INIT_NEWOBJECT(info,instr)             \
         do {(info).typeclass.cls = pseudo_class_New;   \
             (info).elementclass.any = (instr);         \
             (info).merged = NULL;                      \
             (info).dimension = 0;                      \
             (info).elementtype = 0;} while(0)

#define TYPEINFO_INIT_PRIMITIVE_ARRAY(info,arraytype)                   \
    typeinfo_init_classinfo(&(info),primitivetype_table[arraytype].arrayclass);

/* macros for copying types (destinition is not checked or freed) ***********/

/* TYPEINFO_COPY makes a shallow copy, the merged pointer is simply copied. */
#define TYPEINFO_COPY(src,dst)                                  \
    do {(dst) = (src);} while(0)

/* TYPEINFO_CLONE makes a deep copy, the merged list (if any) is duplicated
 * into a newly allocated array.
 */
#define TYPEINFO_CLONE(src,dst)                                 \
    do {(dst) = (src);                                          \
        if ((dst).merged) typeinfo_clone(&(src),&(dst));} while(0)

/****************************************************************************/
/* FUNCTIONS                                                                */
/****************************************************************************/

/* typevector functions *****************************************************/

/* element read-only access */
bool typevector_checktype(varinfo *set,int index,int type);
bool typevector_checkreference(varinfo *set,int index);
bool typevector_checkretaddr(varinfo *set,int index);

/* element write access */
void typevector_store(varinfo *set,int index,int type,typeinfo_t *info);
void typevector_store_retaddr(varinfo *set,int index,typeinfo_t *info);
bool typevector_init_object(varinfo *set,void *ins,classref_or_classinfo initclass,int size);

/* vector functions */
varinfo *typevector_copy(varinfo *src,int size);
void typevector_copy_inplace(varinfo *src,varinfo *dst,int size);
typecheck_result typevector_merge(methodinfo *m,varinfo *dst,varinfo *y,int size);

/* inquiry functions (read-only) ********************************************/

bool typeinfo_is_array(typeinfo_t *info);
bool typeinfo_is_primitive_array(typeinfo_t *info,int arraytype);
bool typeinfo_is_array_of_refs(typeinfo_t *info);

typecheck_result typeinfo_is_assignable(typeinfo_t *value,typeinfo_t *dest);
typecheck_result typeinfo_is_assignable_to_class(typeinfo_t *value,classref_or_classinfo dest);

/* initialization functions *************************************************/

/* RETURN VALUE (bool):
 *     true.............ok,
 *     false............an exception has been thrown.
 *
 * RETURN VALUE (int):
 *     >= 0.............ok,
 *     -1...............an exception has been thrown.
 */
void typeinfo_init_classinfo(typeinfo_t *info,classinfo *c);
bool typeinfo_init_class(typeinfo_t *info,classref_or_classinfo c);
bool typeinfo_init_component(typeinfo_t *srcarray,typeinfo_t *dst);

bool typeinfo_init_from_typedesc(typedesc *desc,u1 *type,typeinfo_t *info);
bool typeinfos_init_from_methoddesc(methoddesc *desc,u1 *typebuf,
                                   typeinfo_t *infobuf,
                                   int buflen,bool twoword,
                                   u1 *returntype,typeinfo_t *returntypeinfo);
bool  typedescriptor_init_from_typedesc(typedescriptor_t *td,
									    typedesc *desc);
bool  typeinfo_init_varinfo_from_typedesc(varinfo *var,
									    typedesc *desc);
int  typedescriptors_init_from_methoddesc(typedescriptor_t *td,
										  methoddesc *desc,
										  int buflen,bool twoword,int startindex,
										  typedescriptor_t *returntype);
bool typeinfo_init_varinfos_from_methoddesc(varinfo *vars,
										  methoddesc *desc,
										  int buflen, int startindex,
										  s4 *map,
										  typedescriptor_t *returntype);

void typeinfo_clone(typeinfo_t *src,typeinfo_t *dest);

/* freeing memory ***********************************************************/

void typeinfo_free(typeinfo_t *info);

/* functions for merging types **********************************************/

typecheck_result typeinfo_merge(methodinfo *m,typeinfo_t *dest,typeinfo_t* y);

/* debugging helpers ********************************************************/

#ifdef TYPEINFO_DEBUG

#include <stdio.h>

void typeinfo_test();
void typeinfo_print_class(FILE *file,classref_or_classinfo c);
void typeinfo_print(FILE *file,typeinfo_t *info,int indent);
void typeinfo_print_short(FILE *file,typeinfo_t *info);
void typeinfo_print_type(FILE *file,int type,typeinfo_t *info);
void typedescriptor_print(FILE *file,typedescriptor_t *td);
void typevector_print(FILE *file,varinfo *vec,int size);

#endif /* TYPEINFO_DEBUG */

#endif /* _TYPEINFO_H */


/*
 * These are local overrides for various environment variables in Emacs.
 * Please do not remove this and leave it at the end of the file, where
 * Emacs will automagically detect them.
 * ---------------------------------------------------------------------
 * Local variables:
 * mode: c
 * indent-tabs-mode: t
 * c-basic-offset: 4
 * tab-width: 4
 * End:
 */
