preparations for the typechecker
authoredwin <none@none>
Thu, 4 Dec 2003 22:29:54 +0000 (22:29 +0000)
committeredwin <none@none>
Thu, 4 Dec 2003 22:29:54 +0000 (22:29 +0000)
17 files changed:
Makefile.am
global.h
loader.c
main.c
src/cacao/cacao.c
src/vm/global.h
src/vm/jit/verify/typeinfo.c [new file with mode: 0644]
src/vm/jit/verify/typeinfo.h [new file with mode: 0644]
src/vm/jit/verify/typeinfo.tst [new file with mode: 0644]
src/vm/loader.c
src/vm/tables.c
src/vm/tables.h
tables.c
tables.h
typeinfo.c [new file with mode: 0644]
typeinfo.h [new file with mode: 0644]
typeinfo.tst [new file with mode: 0644]

index e0e52ccc979aa3be4f0b8facba959ea61220994b..2d268d5327670928573f4012856128b1042bef82 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-# $Id: Makefile.am 682 2003-12-01 15:33:30Z jowenn $
+# $Id: Makefile.am 687 2003-12-04 22:29:54Z edwin $
 
 @SET_MAKE@
 
@@ -43,7 +43,8 @@ cacao_SOURCES = \
        jni.h \
        tables.c \
        tables.h \
-       unzip.c
+       unzip.c \
+       typeinfo.c
 
 cacao_LDADD = \
        jit/libjit.a \
index bfffb6ebe42a5367e947a9ff70c99a7fc8fcec4f..07849fe28924ca3ad5d91872227bf8e661e0e1f9 100644 (file)
--- a/global.h
+++ b/global.h
@@ -31,7 +31,7 @@
             Philipp Tomsich
                        Edwin Steiner
 
-   $Id: global.h 682 2003-12-01 15:33:30Z jowenn $
+   $Id: global.h 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -707,6 +707,7 @@ struct arraydescriptor {
        short  dimension;        /* dimension of the array (always >= 1)          */
     s4     dataoffset;       /* offset of the array data from object pointer  */
        s4     componentsize;    /* size of a component in bytes                  */
+       short  elementtype;      /* ARRAYTYPE_* constant (XXX optimize away?)     */
 };
 
 /* references to some system classes ******************************************/
index 8ff55a17fa7cf07d47c57625ddbb5e1ba55a5655..25cefc519231bc6fe3dab378dcb2b9e8e0980331 100644 (file)
--- a/loader.c
+++ b/loader.c
@@ -29,7 +29,7 @@
             Roman Obermaiser
             Mark Probst
 
-   $Id: loader.c 682 2003-12-01 15:33:30Z jowenn $
+   $Id: loader.c 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -1714,10 +1714,12 @@ class_link_array(classinfo *c)
                if (compvftbl->arraydesc) {
                        desc->elementvftbl = compvftbl->arraydesc->elementvftbl;
                        desc->dimension = compvftbl->arraydesc->dimension + 1;
+                       desc->elementtype = compvftbl->arraydesc->elementtype;
                }
                else {
                        desc->elementvftbl = compvftbl;
                        desc->dimension = 1;
+                       desc->elementtype = ARRAYTYPE_OBJECT;
                }
        }
        else {
@@ -1754,6 +1756,7 @@ class_link_array(classinfo *c)
                desc->componentvftbl = NULL;
                desc->elementvftbl = NULL;
                desc->dimension = 1;
+               desc->elementtype = desc->arraytype;
        }
 
        return desc;
@@ -2906,7 +2909,7 @@ create_pseudo_classes()
 {
     /* pseudo class for Arraystubs (extends java.lang.Object) */
     
-    pseudo_class_Arraystub = class_new( utf_new_char(";Arraystub;") );
+    pseudo_class_Arraystub = class_new( utf_new_char("$ARRAYSTUB$") );
     list_remove(&unloadedclasses,pseudo_class_Arraystub);
 
     pseudo_class_Arraystub->super = class_java_lang_Object;
@@ -2924,7 +2927,7 @@ create_pseudo_classes()
 
     /* pseudo class representing the null type */
     
-    pseudo_class_Null = class_new( utf_new_char(";Null;") );
+    pseudo_class_Null = class_new( utf_new_char("$NULL$") );
     list_remove(&unloadedclasses,pseudo_class_Null);
 
     pseudo_class_Null->super = class_java_lang_Object;
diff --git a/main.c b/main.c
index 55537b6d3075412a8a76da3e91a3ddfce3fceb78..46c7c023ff4be185a201d44e82dc236909de7045 100644 (file)
--- a/main.c
+++ b/main.c
@@ -37,7 +37,7 @@
      - Calling the class loader
      - Running the main method
 
-   $Id: main.c 664 2003-11-21 18:24:01Z jowenn $
+   $Id: main.c 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -57,7 +57,7 @@
 #include "toolbox/loging.h"
 #include "toolbox/memory.h"
 #include "parseRTstats.h"
-
+#include "typeinfo.h" /* XXX remove debug */
 
 bool compileall = false;
 bool verbose =  false;
@@ -832,7 +832,9 @@ int main(int argc, char **argv)
                        a->data[i - opt_ind] = javastring_new(utf_new_char(argv[i]));
                }
 
-
+#ifdef DEBUG_TYPES
+               typeinfo_test(); /* XXX remove debug */
+#endif
                /*class_showmethods(currentThread->group->header.vftbl->class); */
        
                local_exceptionptr = asm_calljavamethod (mainmethod, a, NULL, NULL, NULL );
index 2ca6ddc0660afc760777b888ee2efd25fc2a58a1..5f733b0b9ddf3d6a20969213e2f3c450fb9e7783 100644 (file)
@@ -37,7 +37,7 @@
      - Calling the class loader
      - Running the main method
 
-   $Id: cacao.c 664 2003-11-21 18:24:01Z jowenn $
+   $Id: cacao.c 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -57,7 +57,7 @@
 #include "toolbox/loging.h"
 #include "toolbox/memory.h"
 #include "parseRTstats.h"
-
+#include "typeinfo.h" /* XXX remove debug */
 
 bool compileall = false;
 bool verbose =  false;
@@ -832,7 +832,9 @@ int main(int argc, char **argv)
                        a->data[i - opt_ind] = javastring_new(utf_new_char(argv[i]));
                }
 
-
+#ifdef DEBUG_TYPES
+               typeinfo_test(); /* XXX remove debug */
+#endif
                /*class_showmethods(currentThread->group->header.vftbl->class); */
        
                local_exceptionptr = asm_calljavamethod (mainmethod, a, NULL, NULL, NULL );
index bfffb6ebe42a5367e947a9ff70c99a7fc8fcec4f..07849fe28924ca3ad5d91872227bf8e661e0e1f9 100644 (file)
@@ -31,7 +31,7 @@
             Philipp Tomsich
                        Edwin Steiner
 
-   $Id: global.h 682 2003-12-01 15:33:30Z jowenn $
+   $Id: global.h 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -707,6 +707,7 @@ struct arraydescriptor {
        short  dimension;        /* dimension of the array (always >= 1)          */
     s4     dataoffset;       /* offset of the array data from object pointer  */
        s4     componentsize;    /* size of a component in bytes                  */
+       short  elementtype;      /* ARRAYTYPE_* constant (XXX optimize away?)     */
 };
 
 /* references to some system classes ******************************************/
diff --git a/src/vm/jit/verify/typeinfo.c b/src/vm/jit/verify/typeinfo.c
new file mode 100644 (file)
index 0000000..0c89617
--- /dev/null
@@ -0,0 +1,1408 @@
+/********************************* typeinfo.c *********************************
+
+       Copyright (c) 2003 ? XXX
+
+       See file COPYRIGHT for information on usage and disclaimer of warranties
+
+       functions for the compiler's type system
+       
+       Authors: Edwin Steiner
+                  
+       Last Change:
+
+*******************************************************************************/
+
+#include "typeinfo.h"
+#include "tables.h"
+#include "loader.h"
+
+#define TYPEINFO_REUSE_MERGED
+
+#define CLASS_IMPLEMENTS_INTERFACE(cls,index)                   \
+    ( ((index) < (cls)->vftbl->interfacetablelength)            \
+      && (VFTBLINTERFACETABLE((cls)->vftbl,(index)) != NULL) )
+
+/**********************************************************************/
+/* READ-ONLY FUNCTIONS                                                */
+/* The following functions don't change typeinfo data.                */
+/**********************************************************************/
+
+bool
+typeinfo_is_array(typeinfo *info)
+{
+    return TYPEINFO_IS_ARRAY(*info);
+}
+
+bool
+typeinfo_is_primitive_array(typeinfo *info,int arraytype)
+{
+    return TYPEINFO_IS_PRIMITIVE_ARRAY(*info,arraytype);
+}
+
+bool
+typeinfo_is_array_of_refs(typeinfo *info)
+{
+    return TYPEINFO_IS_ARRAY_OF_REFS(*info);
+}
+
+static
+bool
+interface_extends_interface(classinfo *cls,classinfo *interf)
+{
+    int i;
+    
+    /* first check direct superinterfaces */
+    for (i=0; i<cls->interfacescount; ++i) {
+        if (cls->interfaces[i] == interf)
+            return true;
+    }
+    
+    /* check indirect superinterfaces */
+    for (i=0; i<cls->interfacescount; ++i) {
+        if (interface_extends_interface(cls->interfaces[i],interf))
+            return true;
+    }
+    
+    return false;
+}
+
+/* XXX If really a performance issue, this could become a macro. */
+static
+bool
+classinfo_implements_interface(classinfo *cls,classinfo *interf)
+{
+    if (cls->flags & ACC_INTERFACE) {
+        /* cls is an interface */
+        if (cls == interf)
+            return true;
+
+        /* check superinterfaces */
+        return interface_extends_interface(cls,interf);
+    }
+
+    return CLASS_IMPLEMENTS_INTERFACE(cls,interf->index);
+}
+
+bool mergedlist_implements_interface(typeinfo_mergedlist *merged,
+                                     classinfo *interf)
+{
+    int i;
+    classinfo **mlist;
+    
+    /* Check if there is an non-empty mergedlist. */
+    if (!merged)
+        return false;
+
+    /* If all classinfos in the (non-empty) merged array implement the
+     * interface return true, otherwise false.
+     */
+    mlist = merged->list;
+    i = merged->count;
+    while (i--) {
+        if (!classinfo_implements_interface(*mlist++,interf))
+            return false;
+    }
+    return true;
+}
+
+bool
+merged_implements_interface(classinfo *typeclass,typeinfo_mergedlist *merged,
+                            classinfo *interf)
+{
+    /* primitive types don't support interfaces. */
+    if (!typeclass)
+        return false;
+
+    /* the null type can be cast to any interface type. */
+    if (typeclass == pseudo_class_Null)
+        return true;
+
+    /* check if typeclass implements the interface. */
+    if (classinfo_implements_interface(typeclass,interf))
+        return true;
+
+    /* check the mergedlist */
+    return (merged && mergedlist_implements_interface(merged,interf));
+}
+
+bool
+typeinfo_is_assignable(typeinfo *value,typeinfo *dest)
+{
+    classinfo *cls;
+
+    cls = value->typeclass;
+
+    /* DEBUG CHECK: dest must not have a merged list. */
+#ifdef DEBUG_TYPES
+    if (dest->merged)
+        panic("Internal error: typeinfo_is_assignable on merged destination.");
+#endif
+    
+    /* assignments of primitive values are not checked here. */
+    if (!cls && !dest->typeclass)
+        return true;
+
+    /* the null type can be assigned to any type */
+    if (TYPEINFO_IS_NULLTYPE(*value))
+        return true;
+
+    /* primitive and reference types are not assignment compatible. */
+    if (!cls || !dest->typeclass)
+        return false;
+
+    if (dest->typeclass->flags & ACC_INTERFACE) {
+        /* We are assigning to an interface type. */
+        return merged_implements_interface(cls,value->merged,
+                                           dest->typeclass);
+    }
+
+    if (TYPEINFO_IS_ARRAY(*dest)) {
+        /* We are assigning to an array type. */
+        if (!TYPEINFO_IS_ARRAY(*value))
+            return false;
+
+        /* {Both value and dest are array types.} */
+
+        /* value must have at least the dimension of dest. */
+        if (value->dimension < dest->dimension)
+            return false;
+
+        if (value->dimension > dest->dimension) {
+            /* value has higher dimension so we need to check
+             * if its component array can be assigned to the
+             * element type of dest */
+            
+            if (dest->elementclass->flags & ACC_INTERFACE) {
+                /* We are assigning to an interface type. */
+                return classinfo_implements_interface(pseudo_class_Arraystub,
+                                                      dest->elementclass);
+            }
+
+            /* We are assigning to a class type. */
+            return class_issubclass(pseudo_class_Arraystub,dest->elementclass);
+        }
+
+        /* {value and dest have the same dimension} */
+
+        if (value->elementtype != dest->elementtype)
+            return false;
+
+        if (value->elementclass) {
+            /* We are assigning an array of objects so we have to
+             * check if the elements are assignable.
+             */
+
+            if (dest->elementclass->flags & ACC_INTERFACE) {
+                /* We are assigning to an interface type. */
+
+                return merged_implements_interface(value->elementclass,
+                                                   value->merged,
+                                                   dest->elementclass);
+            }
+            
+            /* We are assigning to a class type. */
+            return class_issubclass(value->elementclass,dest->elementclass);
+        }
+
+        return true;
+    }
+
+    /* {dest is not an array} */
+        
+    /* We are assigning to a class type */
+    if (cls->flags & ACC_INTERFACE)
+        cls = class_java_lang_Object;
+    
+    return class_issubclass(cls,dest->typeclass);
+}
+
+/**********************************************************************/
+/* INITIALIZATION FUNCTIONS                                           */
+/* The following functions fill in uninitialized typeinfo structures. */
+/**********************************************************************/
+
+/* XXX delete */
+#if 0
+void
+typeinfo_init_from_arraydescriptor(typeinfo *info,
+                                   constant_arraydescriptor *desc)
+{
+    int dim = 1;
+    
+    /* Arrays are instances of the pseudo class Array. */
+    info->typeclass = pseudo_class_Array; /* XXX */
+    info->merged = NULL;
+
+    /* Handle multidimensional arrays */
+    while (desc->arraytype == ARRAYTYPE_ARRAY) {
+        dim++;
+        desc = desc->elementdescriptor;
+    }
+
+    info->dimension = dim;
+
+    if ((info->elementtype = desc->arraytype) == ARRAYTYPE_OBJECT) {
+        info->elementclass = desc->objectclass;
+    }
+    else {
+        info->elementclass = NULL;
+    }
+}
+#endif
+
+void
+typeinfo_init_from_descriptor(typeinfo *info,char *utf_ptr,char *end_ptr)
+{
+    classinfo *cls;
+    char *end;
+    cls = class_from_descriptor(utf_ptr,end_ptr,&end,CLASSLOAD_NEW);
+
+    if (!cls)
+        panic("Invalid descriptor.");
+
+    switch (*utf_ptr) {
+      case 'L':
+      case '[':
+          /* a class, interface or array descriptor */
+          TYPEINFO_INIT_CLASSINFO(*info,cls);
+          break;
+      default:
+          /* a primitive type */
+          TYPEINFO_INIT_PRIMITIVE(*info);
+    }
+
+    /* exceeding characters */         
+    if (end!=end_ptr) panic ("descriptor has exceeding chars");
+}
+
+int
+typeinfo_count_method_args(utf *d,bool twoword)
+{
+    int args = 0;
+    char *utf_ptr = d->text;
+    char *end_pos = utf_end(d);
+    char c,ch;
+
+    /* method descriptor must start with parenthesis */
+    if (*utf_ptr++ != '(') panic ("Missing '(' in method descriptor");
+
+    /* check arguments */
+    while ((c = *utf_ptr++) != ')') {
+        switch (c) {
+          case 'B':
+          case 'C':
+          case 'I':
+          case 'S':
+          case 'Z':  
+          case 'F':  
+              /* primitive one-word type */
+              args++;
+              break;
+              
+          case 'J':  
+          case 'D':
+              /* primitive two-word type */
+              args++;
+              if (twoword) args++;
+              break;
+              
+          case 'L':
+              /* skip classname */
+              while ( *utf_ptr++ != ';' )
+                  if (utf_ptr>=end_pos) 
+                      panic ("Missing ';' in objecttype-descriptor");
+              
+              args++;
+              break;
+              
+          case '[' :
+              /* array type */ 
+              while ((ch = *utf_ptr++)=='[') 
+                  /* skip */ ;
+              
+              /* component type of array */
+              switch (ch) {
+                case 'B':
+                case 'C':
+                case 'I':
+                case 'S':
+                case 'Z':  
+                case 'J':  
+                case 'F':  
+                case 'D':
+                    /* primitive type */  
+                    break;
+                    
+                case 'L':
+                    /* skip classname */
+                    while ( *utf_ptr++ != ';' )
+                        if (utf_ptr>=end_pos) 
+                            panic ("Missing ';' in objecttype-descriptor");
+                    break;
+                    
+                default:   
+                    panic ("Ill formed methodtype-descriptor");
+              }
+              
+              args++;
+              break;
+              
+          default:   
+              panic ("Ill formed methodtype-descriptor");
+        }                      
+    }
+
+    return args;
+}
+
+void
+typeinfo_init_from_method_args(utf *desc,u1 *typebuf,typeinfo *infobuf,
+                               int buflen,bool twoword,
+                               int *returntype,typeinfo *returntypeinfo)
+{
+    char *utf_ptr = desc->text;     /* current position in utf text   */
+    char *end_pos = utf_end(desc);  /* points behind utf string       */
+    char c;
+    int args = 0;
+    classinfo *cls;
+
+    /* method descriptor must start with parenthesis */
+    if (*utf_ptr++ != '(') panic ("Missing '(' in method descriptor");
+
+    /* check arguments */
+    while ((c = *utf_ptr) != ')') {
+        cls = class_from_descriptor(utf_ptr,end_pos,&utf_ptr,CLASSLOAD_NEW);
+        if (!cls)
+            panic("Invalid method descriptor.");
+        
+        switch (c) {
+          case 'B':
+          case 'C':
+          case 'I':
+          case 'S':
+          case 'Z':  
+          case 'F':  
+              /* primitive one-word type */
+              if (++args > buflen)
+                  panic("Buffer too small for method arguments.");
+              if (typebuf)
+                  *typebuf++ = (c == 'F') ? TYPE_FLOAT : TYPE_INT; /* XXX TYPE_FLT? */
+              TYPEINFO_INIT_PRIMITIVE(*infobuf);
+              infobuf++;
+              break;
+              
+          case 'J':  
+          case 'D':
+              /* primitive two-word type */
+              if (++args > buflen)
+                  panic("Buffer too small for method arguments.");
+              if (typebuf)
+                  *typebuf++ = (c == 'J') ? TYPE_LONG : TYPE_DOUBLE; /* XXX TYPE_DBL? */
+              TYPEINFO_INIT_PRIMITIVE(*infobuf);
+              infobuf++;
+              if (twoword) {
+                  if (++args > buflen)
+                      panic("Buffer too small for method arguments.");
+                  TYPEINFO_INIT_PRIMITIVE(*infobuf);
+                  infobuf++;
+              }
+              break;
+              
+          case 'L':
+          case '[' :
+              /* reference type */
+              
+              if (++args > buflen)
+                  panic("Buffer too small for method arguments.");
+              if (typebuf)
+                  *typebuf++ = TYPE_ADDRESS;
+              
+              TYPEINFO_INIT_CLASSINFO(*infobuf,cls);
+              /* XXX remove */ /* utf_display(cls->name); */
+              infobuf++;
+              break;
+          
+          default:   
+              panic ("Ill formed methodtype-descriptor (type)");
+        }
+    }
+    utf_ptr++; /* skip ')' */
+
+    /* check returntype */
+    if (returntype) {
+        switch (*utf_ptr) {
+          case 'B':
+          case 'C':
+          case 'I':
+          case 'S':
+          case 'Z':
+              *returntype = TYPE_INT;
+              goto primitive_tail;
+              
+          case 'J':
+              *returntype = TYPE_LONG;
+              goto primitive_tail;
+              
+          case 'F':  
+              *returntype = TYPE_FLOAT;
+              goto primitive_tail;
+              
+          case 'D':
+              *returntype = TYPE_DOUBLE;
+              goto primitive_tail;
+
+          case 'V':
+              *returntype = TYPE_VOID;
+      primitive_tail:
+              if ((utf_ptr+1) != end_pos)
+                  panic ("Method-descriptor has exceeding chars");
+              if (returntypeinfo) {
+                  TYPEINFO_INIT_PRIMITIVE(*returntypeinfo);
+              }
+              break;
+
+          case 'L':
+          case '[':
+              *returntype = TYPE_ADDRESS;
+              cls = class_from_descriptor(utf_ptr,end_pos,&utf_ptr,CLASSLOAD_NEW);
+              if (!cls)
+                  panic("Invalid return type");
+              if (utf_ptr != end_pos)
+                  panic ("Method-descriptor has exceeding chars");
+              if (returntypeinfo) {
+                  TYPEINFO_INIT_CLASSINFO(*returntypeinfo,cls);
+              }
+              break;
+
+          default:   
+              panic ("Ill formed methodtype-descriptor (returntype)");
+        }
+    }
+}
+
+/* XXX could be made a macro if really performance critical */
+void
+typeinfo_init_component(typeinfo *srcarray,typeinfo *dst)
+{
+    vftbl *comp = NULL;
+    
+    /* XXX find component class */
+    if (!TYPEINFO_IS_ARRAY(*srcarray))
+        panic("Trying to access component of non-array");
+
+    comp = srcarray->typeclass->vftbl->arraydesc->componentvftbl;
+    if (comp) {
+        TYPEINFO_INIT_CLASSINFO(*dst,comp->class);
+    }
+    else {
+        TYPEINFO_INIT_PRIMITIVE(*dst);
+    }
+
+    /* XXX assign directly ? */
+#if 0
+    if ((dst->dimension = srcarray->dimension - 1) == 0) {
+        dst->typeclass = srcarray->elementclass;
+        dst->elementtype = 0;
+        dst->elementclass = NULL;
+    }
+    else {
+        dst->typeclass = srcarray->typeclass;
+        dst->elementtype = srcarray->elementtype;
+        dst->elementclass = srcarray->elementclass;
+    }
+#endif
+    
+    dst->merged = srcarray->merged;
+}
+
+/* Condition: src != dest. */
+void
+typeinfo_clone(typeinfo *src,typeinfo *dest)
+{
+    int count;
+    classinfo **srclist,**destlist;
+
+#ifdef DEBUG_TYPES
+    if (src == dest)
+        panic("Internal error: typeinfo_clone with src==dest");
+#endif
+    
+    *dest = *src;
+
+    if (src->merged) {
+        count = src->merged->count;
+        TYPEINFO_ALLOCMERGED(dest->merged,count);
+        dest->merged->count = count;
+
+        /* XXX use memcpy? */
+        srclist = src->merged->list;
+        destlist = dest->merged->list;
+        while (count--)
+            *destlist++ = *srclist++;
+    }
+}
+
+/**********************************************************************/
+/* MISCELLANEOUS FUNCTIONS                                            */
+/**********************************************************************/
+
+void
+typeinfo_free(typeinfo *info)
+{
+    TYPEINFO_FREE(*info);
+}
+
+/**********************************************************************/
+/* MERGING FUNCTIONS                                                  */
+/* The following functions are used to merge the types represented by */
+/* two typeinfo structures into one typeinfo structure.               */
+/**********************************************************************/
+
+static
+void
+typeinfo_merge_error(char *str,typeinfo *x,typeinfo *y) {
+#ifdef DEBUG_TYPES
+    fprintf(stderr,"Error in typeinfo_merge: %s\n",str);
+    fprintf(stderr,"Typeinfo x:\n");
+    typeinfo_print(stderr,x,1);
+    fprintf(stderr,"Typeinfo y:\n");
+    typeinfo_print(stderr,y,1);
+#endif
+    panic(str);
+}
+
+/* Condition: clsx != clsy. */
+/* Returns: true if dest was changed (always true). */
+static
+bool
+typeinfo_merge_two(typeinfo *dest,classinfo *clsx,classinfo *clsy)
+{
+    TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+    TYPEINFO_ALLOCMERGED(dest->merged,2);
+    dest->merged->count = 2;
+
+#ifdef DEBUG_TYPES
+    if (clsx == clsy)
+        panic("Internal error: typeinfo_merge_two called with clsx==clsy.");
+#endif
+
+    if (clsx < clsy) {
+        dest->merged->list[0] = clsx;
+        dest->merged->list[1] = clsy;
+    }
+    else {
+        dest->merged->list[0] = clsy;
+        dest->merged->list[1] = clsx;
+    }
+
+    return true;
+}
+
+/* Returns: true if dest was changed. */
+static
+bool
+typeinfo_merge_add(typeinfo *dest,typeinfo_mergedlist *m,classinfo *cls)
+{
+    int count;
+    typeinfo_mergedlist *newmerged;
+    classinfo **mlist,**newlist;
+
+    count = m->count;
+    mlist = m->list;
+
+    /* Check if cls is already in the mergedlist m. */
+    while (count--) {
+        if (*mlist++ == cls) {
+            /* cls is in the list, so m is the resulting mergedlist */
+            if (dest->merged == m)
+                return false;
+
+            /* We have to copy the mergedlist */
+            TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+            count = m->count;
+            TYPEINFO_ALLOCMERGED(dest->merged,count);
+            dest->merged->count = count;
+            newlist = dest->merged->list;
+            mlist = m->list;
+            while (count--) {
+                *newlist++ = *mlist++;
+            }
+            return true;
+        }
+    }
+
+    /* Add cls to the mergedlist. */
+    count = m->count;
+    TYPEINFO_ALLOCMERGED(newmerged,count+1);
+    newmerged->count = count+1;
+    newlist = newmerged->list;    
+    mlist = m->list;
+    while (count) {
+        if (*mlist > cls)
+            break;
+        *newlist++ = *mlist++;
+        count--;
+    }
+    *newlist++ = cls;
+    while (count--) {
+        *newlist++ = *mlist++;
+    }
+
+    /* Put the new mergedlist into dest. */
+    TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+    dest->merged = newmerged;
+    
+    return true;
+}
+
+/* Returns: true if dest was changed. */
+static
+bool
+typeinfo_merge_mergedlists(typeinfo *dest,typeinfo_mergedlist *x,
+                           typeinfo_mergedlist *y)
+{
+    int count = 0;
+    int countx,county;
+    typeinfo_mergedlist *temp,*result;
+    classinfo **clsx,**clsy,**newlist;
+
+    /* count the elements that will be in the resulting list */
+    /* (Both lists are sorted, equal elements are counted only once.) */
+    clsx = x->list;
+    clsy = y->list;
+    countx = x->count;
+    county = y->count;
+    while (countx && county) {
+        if (*clsx == *clsy) {
+            clsx++;
+            clsy++;
+            countx--;
+            county--;
+        }
+        else if (*clsx < *clsy) {
+            clsx++;
+            countx--;
+        }
+        else {
+            clsy++;
+            county--;
+        }
+        count++;
+    }
+    count += countx + county;
+
+    /* {The new mergedlist will have count entries.} */
+
+    if (y->count == count) {
+        temp = x; x = y; y = temp;
+    }
+    /* {If one of x,y is already the result it is x.} */
+    if (x->count == count) {
+        /* x->merged is equal to the result */
+        if (x == dest->merged)
+            return false;
+
+        if (!dest->merged || dest->merged->count != count) {
+            TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+            TYPEINFO_ALLOCMERGED(dest->merged,count);
+            dest->merged->count = count;
+        }
+
+        newlist = dest->merged->list;
+        clsx = x->list;
+        while (count--) {
+            *newlist++ = *clsx++;
+        }
+        return true;
+    }
+
+    /* {We have to merge two lists.} */
+
+    /* allocate the result list */
+    TYPEINFO_ALLOCMERGED(result,count);
+    result->count = count;
+    newlist = result->list;
+
+    /* merge the sorted lists */
+    clsx = x->list;
+    clsy = y->list;
+    countx = x->count;
+    county = y->count;
+    while (countx && county) {
+        if (*clsx == *clsy) {
+            *newlist++ = *clsx++;
+            clsy++;
+            countx--;
+            county--;
+        }
+        else if (*clsx < *clsy) {
+            *newlist++ = *clsx++;
+            countx--;
+        }
+        else {
+            *newlist++ = *clsy++;
+            county--;
+        }
+    }
+    while (countx--)
+            *newlist++ = *clsx++;
+    while (county--)
+            *newlist++ = *clsy++;
+
+    /* replace the list in dest with the result list */
+    TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+    dest->merged = result;
+
+    return true;
+}
+
+static
+bool
+typeinfo_merge_nonarrays(typeinfo *dest,
+                         classinfo **result,
+                         classinfo *clsx,classinfo *clsy,
+                         typeinfo_mergedlist *mergedx,
+                         typeinfo_mergedlist *mergedy)
+{
+    classinfo *tcls,*common;
+    typeinfo_mergedlist *tmerged;
+    bool changed;
+
+    /* XXX remove */
+    /*
+#ifdef DEBUG_TYPES
+    typeinfo dbgx,dbgy;
+    printf("typeinfo_merge_nonarrays:\n");
+    TYPEINFO_INIT_CLASSINFO(dbgx,clsx);
+    dbgx.merged = mergedx;
+    TYPEINFO_INIT_CLASSINFO(dbgy,clsy);
+    dbgy.merged = mergedy;
+    typeinfo_print(stdout,&dbgx,4);
+    printf("  with:\n");
+    typeinfo_print(stdout,&dbgy,4);
+#endif
+    */ 
+
+    /* Common case: clsx == clsy */
+    /* (This case is very simple unless *both* x and y really represent
+     *  merges of subclasses of clsx==clsy.)
+     */
+    /* XXX count this case for statistics */
+    if ((clsx == clsy) && (!mergedx || !mergedy)) {
+  return_simple_x:
+        /* XXX remove */ /* log_text("return simple x"); */
+        changed = (dest->merged != NULL);
+        TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+        dest->merged = NULL;
+        *result = clsx;
+        /* XXX remove */ /* log_text("returning"); */
+        return changed;
+    }
+    
+    /* If clsy is an interface, swap x and y. */
+    if (clsy->flags & ACC_INTERFACE) {
+        tcls = clsx; clsx = clsy; clsy = tcls;
+        tmerged = mergedx; mergedx = mergedy; mergedy = tmerged;
+    }
+    /* {We know: If only one of x,y is an interface it is x.} */
+
+    /* Handle merging of interfaces: */
+    if (clsx->flags & ACC_INTERFACE) {
+        /* {clsx is an interface and mergedx == NULL.} */
+        
+        if (clsy->flags & ACC_INTERFACE) {
+            /* We are merging two interfaces. */
+            /* {mergedy == NULL} */
+            /* XXX: should we optimize direct superinterfaces? */
+
+            /* {We know that clsx!=clsy (see common case at beginning.)} */
+            *result = class_java_lang_Object;
+            return typeinfo_merge_two(dest,clsx,clsy);
+        }
+
+        /* {We know: x is an interface, clsy is a class.} */
+
+        /* Check if we are merging an interface with java.lang.Object */
+        if (clsy == class_java_lang_Object && !mergedy) {
+            clsx = clsy;
+            goto return_simple_x;
+        }
+            
+
+        /* If the type y implements clsx then the result of the merge
+         * is clsx regardless of mergedy.
+         */
+        
+        if (CLASS_IMPLEMENTS_INTERFACE(clsy,clsx->index)
+            || mergedlist_implements_interface(mergedy,clsx))
+        {
+            /* y implements x, so the result of the merge is x. */
+            goto return_simple_x;
+        }
+        
+        /* {We know: x is an interface, the type y a class or a merge
+         * of subclasses and does not implement x.} */
+
+        /* There may still be superinterfaces of x which are implemented
+         * by y, too, so we have to add clsx to the mergedlist.
+         */
+
+        /* XXX if x has no superinterfaces we could return a simple java.lang.Object */
+        
+        common = class_java_lang_Object;
+        goto merge_with_simple_x;
+    }
+
+    /* {We know: x and y are classes (not interfaces).} */
+    
+    /* If *x is deeper in the inheritance hierarchy swap x and y. */
+    if (clsx->index > clsy->index) {
+        tcls = clsx; clsx = clsy; clsy = tcls;
+        tmerged = mergedx; mergedx = mergedy; mergedy = tmerged;
+    }
+
+    /* {We know: y is at least as deep in the hierarchy as x.} */
+
+    /* Find nearest common anchestor for the classes. */
+    common = clsx;
+    tcls = clsy;
+    while (tcls->index > common->index)
+        tcls = tcls->super;
+    while (common != tcls) {
+        common = common->super;
+        tcls = tcls->super;
+    }
+
+    /* {common == nearest common anchestor of clsx and clsy.} */
+
+    /* XXX remove */ /* printf("common: "); utf_display(common->name); printf("\n"); */
+
+    /* If clsx==common and x is a whole class (not a merge of subclasses)
+     * then the result of the merge is clsx.
+     */
+    if (clsx == common && !mergedx) {
+        goto return_simple_x;
+    }
+   
+    if (mergedx) {
+        *result = common;
+        if (mergedy)
+            return typeinfo_merge_mergedlists(dest,mergedx,mergedy);
+        else
+            return typeinfo_merge_add(dest,mergedx,clsy);
+    }
+
+ merge_with_simple_x:
+    *result = common;
+    if (mergedy)
+        return typeinfo_merge_add(dest,mergedy,clsx);
+    else
+        return typeinfo_merge_two(dest,clsx,clsy);
+}
+
+/* Condition: *dest must be a valid initialized typeinfo. */
+/* Condition: dest != y. */
+/* Returns: true if dest was changed. */
+bool
+typeinfo_merge(typeinfo *dest,typeinfo* y)
+{
+    typeinfo *x;
+    typeinfo *tmp;                      /* used for swapping */
+    classinfo *common;
+    classinfo *elementclass;
+    int dimension;
+    int elementtype;
+    bool changed;
+
+    /* XXX remove */
+    /*
+#ifdef DEBUG_TYPES
+    typeinfo_print(stdout,dest,4);
+    typeinfo_print(stdout,y,4);
+#endif
+    */ 
+
+    /* This function cannot be used to merge primitive types. */
+    if (!dest->typeclass || !y->typeclass)
+        typeinfo_merge_error("Trying to merge primitive types.",dest,y);
+
+#ifdef DEBUG_TYPES
+    if (dest == y)
+        panic("Internal error: typeinfo_merge with dest==y");
+    
+    /* check that no unlinked classes are merged. */
+    if (!dest->typeclass->linked || !y->typeclass->linked)
+        typeinfo_merge_error("Trying to merge unlinked class(es).",dest,y);
+#endif
+
+    /* XXX remove */ /* log_text("Testing common case"); */
+
+    /* Common case: class dest == class y */
+    /* (This case is very simple unless *both* dest and y really represent
+     *  merges of subclasses of class dest==class y.)
+     */
+    /* XXX count this case for statistics */
+    if ((dest->typeclass == y->typeclass) && (!dest->merged || !y->merged)) {
+        changed = (dest->merged != NULL);
+        TYPEINFO_FREEMERGED_IF_ANY(dest->merged); /* XXX unify if */
+        dest->merged = NULL;
+        /* XXX remove */ /* log_text("common case handled"); */
+        return changed;
+    }
+    
+    /* XXX remove */ /* log_text("Handling null types"); */
+
+    /* Handle null types: */
+    if (TYPEINFO_IS_NULLTYPE(*y)) {
+        return false;
+    }
+    if (TYPEINFO_IS_NULLTYPE(*dest)) {
+        TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+        TYPEINFO_CLONE(*y,*dest);
+        return true;
+    }
+
+    /* This function uses x internally, so x and y can be swapped
+     * without changing dest. */
+    x = dest;
+    changed = false;
+    
+    /* Handle merging of arrays: */
+    if (TYPEINFO_IS_ARRAY(*x) && TYPEINFO_IS_ARRAY(*y)) {
+        
+        /* XXX remove */ /* log_text("Handling arrays"); */
+
+        /* Make x the one with lesser dimension */
+        if (x->dimension > y->dimension) {
+            tmp = x; x = y; y = tmp;
+        }
+
+        /* If one array (y) has higher dimension than the other,
+         * interpret it as an array (same dim. as x) of Arraystubs. */
+        if (x->dimension < y->dimension) {
+            dimension = x->dimension;
+            elementtype = ARRAYTYPE_OBJECT;
+            elementclass = pseudo_class_Arraystub;
+        }
+        else {
+            dimension = y->dimension;
+            elementtype = y->elementtype;
+            elementclass = y->elementclass;
+        }
+        
+        /* {The arrays are of the same dimension.} */
+        
+        if (x->elementtype != elementtype) {
+            /* Different element types are merged, so the resulting array
+             * type has one accessible dimension less. */
+            if (--dimension == 0) {
+                common = pseudo_class_Arraystub;
+                elementtype = 0;
+                elementclass = NULL;
+            }
+            else {
+                common = class_multiarray_of(dimension,pseudo_class_Arraystub);
+                elementtype = ARRAYTYPE_OBJECT;
+                elementclass = pseudo_class_Arraystub;
+            }
+        }
+        else {
+            /* {The arrays have the same dimension and elementtype.} */
+
+            if (elementtype == ARRAYTYPE_OBJECT) {
+                /* The elements are references, so their respective
+                 * types must be merged.
+                 */
+                changed |= typeinfo_merge_nonarrays(dest,
+                                                    &elementclass,
+                                                    x->elementclass,
+                                                    elementclass,
+                                                    x->merged,y->merged);
+
+                /* XXX otimize this? */
+                /* XXX remove */ /* log_text("finding resulting array class: "); */
+                common = class_multiarray_of(dimension,elementclass);
+                /* XXX remove */ /* utf_display(common->name); printf("\n"); */
+            }
+        }
+    }
+    else {
+        /* {We know that at least one of x or y is no array, so the
+         *  result cannot be an array.} */
+        
+        changed |= typeinfo_merge_nonarrays(dest,
+                                            &common,
+                                            x->typeclass,y->typeclass,
+                                            x->merged,y->merged);
+
+        dimension = 0;
+        elementtype = 0;
+        elementclass = NULL;
+    }
+
+    /* Put the new values into dest if neccessary. */
+
+    if (dest->typeclass != common) {
+        dest->typeclass = common;
+        changed = true;
+    }
+    if (dest->dimension != dimension) {
+        dest->dimension = dimension;
+        changed = true;
+    }
+    if (dest->elementtype != elementtype) {
+        dest->elementtype = elementtype;
+        changed = true;
+    }
+    if (dest->elementclass != elementclass) {
+        dest->elementclass = elementclass;
+        changed = true;
+    }
+
+    /* XXX remove */ /* log_text("returning from merge"); */
+    
+    return changed;
+}
+
+
+/**********************************************************************/
+/* DEBUGGING HELPERS                                                  */
+/**********************************************************************/
+
+#ifdef DEBUG_TYPES
+
+#include "tables.h"
+#include "loader.h"
+
+static int
+typeinfo_test_compare(classinfo **a,classinfo **b)
+{
+    if (*a == *b) return 0;
+    if (*a < *b) return -1;
+    return +1;
+}
+
+static void
+typeinfo_test_parse(typeinfo *info,char *str)
+{
+    int num;
+    int i;
+    typeinfo *infobuf;
+    u1 *typebuf;
+    int returntype;
+    utf *desc = utf_new_char(str);
+    
+    num = typeinfo_count_method_args(desc,false);
+    if (num) {
+        typebuf = DMNEW(u1,num);
+        infobuf = DMNEW(typeinfo,num);
+        
+        typeinfo_init_from_method_args(desc,typebuf,infobuf,num,false,
+                                       &returntype,info);
+
+        TYPEINFO_ALLOCMERGED(info->merged,num);
+        info->merged->count = num;
+
+        for (i=0; i<num; ++i) {
+            if (typebuf[i] != TYPE_ADDRESS)
+                panic("non-reference type in mergedlist");
+            info->merged->list[i] = infobuf[i].typeclass;
+        }
+        qsort(info->merged->list,num,sizeof(classinfo*),
+              (int(*)(const void *,const void *))&typeinfo_test_compare);
+    }
+    else {
+        typeinfo_init_from_method_args(desc,NULL,NULL,0,false,
+                                       &returntype,info);
+    }
+}
+
+#define TYPEINFO_TEST_BUFLEN  4000
+
+static bool
+typeinfo_equal(typeinfo *x,typeinfo *y)
+{
+    int i;
+    
+    if (x->typeclass != y->typeclass) return false;
+    if (x->dimension != y->dimension) return false;
+    if (x->dimension) {
+        if (x->elementclass != y->elementclass) return false;
+        if (x->elementtype != y->elementtype) return false;
+    }
+    if (x->merged || y->merged) {
+        if (!(x->merged && y->merged)) return false;
+        if (x->merged->count != y->merged->count) return false;
+        for (i=0; i<x->merged->count; ++i)
+            if (x->merged->list[i] != y->merged->list[i])
+                return false;
+    }
+    return true;
+}
+
+static void
+typeinfo_testmerge(typeinfo *a,typeinfo *b,typeinfo *result,int *failed)
+{
+    typeinfo dest;
+
+    TYPEINFO_CLONE(*a,dest);
+    
+    printf("\n          ");
+    typeinfo_print_short(stdout,&dest);
+    printf("\n          ");
+    typeinfo_print_short(stdout,b);
+    printf("\n");
+
+    typeinfo_merge(&dest,b);
+
+    if (typeinfo_equal(&dest,result)) {
+        printf("OK        ");
+        typeinfo_print_short(stdout,&dest);
+        printf("\n");
+    }
+    else {
+        printf("RESULT    ");
+        typeinfo_print_short(stdout,&dest);
+        printf("\n");
+        printf("SHOULD BE ");
+        typeinfo_print_short(stdout,result);
+        printf("\n");
+        (*failed)++;
+    }
+}
+
+static void
+typeinfo_inc_dimension(typeinfo *info)
+{
+    if (info->dimension++ == 0) {
+        info->elementtype = ARRAYTYPE_OBJECT;
+        info->elementclass = info->typeclass;
+    }
+    info->typeclass = class_array_of(info->typeclass);
+}
+
+#define TYPEINFO_TEST_MAXDIM  10
+
+static void
+typeinfo_testrun(char *filename)
+{
+    char buf[TYPEINFO_TEST_BUFLEN];
+    char bufa[TYPEINFO_TEST_BUFLEN];
+    char bufb[TYPEINFO_TEST_BUFLEN];
+    char bufc[TYPEINFO_TEST_BUFLEN];
+    typeinfo a,b,c,a2,b2;
+    int maxdim;
+    int failed = 0;
+    FILE *file = fopen(filename,"rt");
+    
+    if (!file)
+        panic("could not open typeinfo test file");
+
+    while (fgets(buf,TYPEINFO_TEST_BUFLEN,file)) {
+        if (buf[0] == '#' || !strlen(buf))
+            continue;
+        
+        int res = sscanf(buf,"%s\t%s\t%s\n",bufa,bufb,bufc);
+        if (res != 3 || !strlen(bufa) || !strlen(bufb) || !strlen(bufc))
+            panic("Invalid line in typeinfo test file (none of empty, comment or test)");
+
+        typeinfo_test_parse(&a,bufa);
+        typeinfo_test_parse(&b,bufb);
+        typeinfo_test_parse(&c,bufc);
+        do {
+            typeinfo_testmerge(&a,&b,&c,&failed); /* check result */
+            typeinfo_testmerge(&b,&a,&c,&failed); /* check commutativity */
+
+            if (TYPEINFO_IS_NULLTYPE(a)) break;
+            if (TYPEINFO_IS_NULLTYPE(b)) break;
+            if (TYPEINFO_IS_NULLTYPE(c)) break;
+            
+            maxdim = a.dimension;
+            if (b.dimension > maxdim) maxdim = b.dimension;
+            if (c.dimension > maxdim) maxdim = c.dimension;
+
+            if (maxdim < TYPEINFO_TEST_MAXDIM) {
+                typeinfo_inc_dimension(&a);
+                typeinfo_inc_dimension(&b);
+                typeinfo_inc_dimension(&c);
+            }
+        } while (maxdim < TYPEINFO_TEST_MAXDIM);
+    }
+
+    fclose(file);
+
+    if (failed) {
+        fprintf(stderr,"Failed typeinfo_merge tests: %d\n",failed);
+        panic("Failed test");
+    }
+}
+
+void
+typeinfo_test()
+{
+/*     typeinfo i1,i2,i3,i4,i5,i6,i7,i8; */
+        
+/*     typeinfo_init_from_fielddescriptor(&i1,"[Ljava/lang/Integer;"); */
+/*     typeinfo_init_from_fielddescriptor(&i2,"[[Ljava/lang/String;"); */
+/*     typeinfo_init_from_fielddescriptor(&i3,"[Ljava/lang/Cloneable;"); */
+/*     typeinfo_init_from_fielddescriptor(&i3,"[[Ljava/lang/String;"); */
+/*     TYPEINFO_INIT_NULLTYPE(i1); */
+/*     typeinfo_print_short(stdout,&i1); printf("\n"); */
+/*     typeinfo_print_short(stdout,&i2); printf("\n"); */
+/*     typeinfo_merge(&i1,&i2); */
+/*     typeinfo_print_short(stdout,&i1); printf("\n"); */
+/*     typeinfo_print_short(stdout,&i3); printf("\n"); */
+/*     typeinfo_merge(&i1,&i3); */
+/*     typeinfo_print_short(stdout,&i1); printf("\n"); */
+
+    log_text("Running typeinfo test file...");
+    typeinfo_testrun("typeinfo.tst");
+    log_text("Finished typeinfo test file.");
+}
+
+void
+typeinfo_init_from_fielddescriptor(typeinfo *info,char *desc)
+{
+    typeinfo_init_from_descriptor(info,desc,desc+strlen(desc));
+}
+
+#define TYPEINFO_MAXINDENT  80
+
+void
+typeinfo_print(FILE *file,typeinfo *info,int indent)
+{
+    int i;
+    char ind[TYPEINFO_MAXINDENT + 1];
+
+    if (indent > TYPEINFO_MAXINDENT) indent = TYPEINFO_MAXINDENT;
+    
+    for (i=0; i<indent; ++i)
+        ind[i] = ' ';
+    ind[i] = (char) 0;
+    
+    if (TYPEINFO_IS_PRIMITIVE(*info)) {
+        fprintf(file,"%sprimitive\n",ind);
+        return;
+    }
+    
+    if (TYPEINFO_IS_NULLTYPE(*info)) {
+        fprintf(file,"%snull\n",ind);
+        return;
+    }
+    
+    fprintf(file,"%sClass:      ",ind);
+    utf_fprint(file,info->typeclass->name);
+    fprintf(file,"\n");
+
+    if (TYPEINFO_IS_ARRAY(*info)) {
+        fprintf(file,"%sDimension:    %d",ind,(int)info->dimension);
+        fprintf(file,"\n%sElements:     ",ind);
+        switch (info->elementtype) {
+          case ARRAYTYPE_INT     : fprintf(file,"int\n"); break;
+          case ARRAYTYPE_LONG    : fprintf(file,"long\n"); break;
+          case ARRAYTYPE_FLOAT   : fprintf(file,"float\n"); break;
+          case ARRAYTYPE_DOUBLE  : fprintf(file,"double\n"); break;
+          case ARRAYTYPE_BYTE    : fprintf(file,"byte\n"); break;
+          case ARRAYTYPE_CHAR    : fprintf(file,"char\n"); break;
+          case ARRAYTYPE_SHORT   : fprintf(file,"short\n"); break;
+          case ARRAYTYPE_BOOLEAN : fprintf(file,"boolean\n"); break;
+              
+          case ARRAYTYPE_OBJECT:
+              fprintf(file,"reference: ");
+              utf_fprint(file,info->elementclass->name);
+              fprintf(file,"\n");
+              break;
+              
+          default:
+              fprintf(file,"INVALID ARRAYTYPE!\n");
+        }
+    }
+
+    if (info->merged) {
+        fprintf(file,"%sMerged: ",ind);
+        for (i=0; i<info->merged->count; ++i) {
+            if (i) fprintf(file,", ");
+            utf_fprint(file,info->merged->list[i]->name);
+        }
+        fprintf(file,"\n");
+    }
+}
+
+void
+typeinfo_print_short(FILE *file,typeinfo *info)
+{
+    int i;
+
+    if (TYPEINFO_IS_PRIMITIVE(*info)) {
+        fprintf(file,"primitive");
+        return;
+    }
+    
+    if (TYPEINFO_IS_NULLTYPE(*info)) {
+        fprintf(file,"null");
+        return;
+    }
+    
+    utf_fprint(file,info->typeclass->name);
+
+    /* XXX remove */
+#if 0
+    if (TYPEINFO_IS_ARRAY(*info)) {
+        fprintf(file,"[%d]",info->dimension);
+        switch (info->elementtype) {
+            case ARRAYTYPE_INT     : fprintf(file,"int"); break;
+            case ARRAYTYPE_LONG    : fprintf(file,"long"); break;
+            case ARRAYTYPE_FLOAT   : fprintf(file,"float"); break;
+            case ARRAYTYPE_DOUBLE  : fprintf(file,"double"); break;
+            case ARRAYTYPE_BYTE    : fprintf(file,"byte"); break;
+            case ARRAYTYPE_CHAR    : fprintf(file,"char"); break;
+            case ARRAYTYPE_SHORT   : fprintf(file,"short"); break;
+            case ARRAYTYPE_BOOLEAN : fprintf(file,"boolean"); break;
+
+            case ARRAYTYPE_OBJECT:
+                fprintf(file,"object(");
+                utf_fprint(file,info->elementclass->name);
+                fprintf(file,")");
+                break;
+                
+            default:
+                fprintf(file,"INVALID ARRAYTYPE!");
+        }
+    }
+#endif
+    
+    if (info->merged) {
+        fprintf(file,"{");
+        for (i=0; i<info->merged->count; ++i) {
+            if (i) fprintf(file,",");
+            utf_fprint(file,info->merged->list[i]->name);
+        }
+        fprintf(file,"}");
+    }
+}
+
+void
+typeinfo_print_type(FILE *file,int type,typeinfo *info)
+{
+    switch (type) {
+      case TYPE_VOID:   fprintf(file,"V"); break;
+      case TYPE_INT:    fprintf(file,"I"); break;
+      case TYPE_FLOAT:  fprintf(file,"F"); break;
+      case TYPE_DOUBLE: fprintf(file,"D"); break;
+      case TYPE_LONG:   fprintf(file,"J"); break;
+      case TYPE_ADDRESS:
+          if (TYPEINFO_IS_PRIMITIVE(*info))
+              fprintf(file,"R"); /* returnAddress */
+          else {
+              fprintf(file,"L");
+              typeinfo_print_short(file,info);
+              fprintf(file,";");
+          }
+          break;
+          
+      default:
+          fprintf(file,"!");
+    }
+}
+
+#endif // DEBUG_TYPES
diff --git a/src/vm/jit/verify/typeinfo.h b/src/vm/jit/verify/typeinfo.h
new file mode 100644 (file)
index 0000000..06c6828
--- /dev/null
@@ -0,0 +1,379 @@
+/********************************* typeinfo.h **********************************
+
+       Copyright (c) 2003 ? XXX
+
+       See file COPYRIGHT for information on usage and disclaimer of warranties
+
+       defininitions for the compiler's type system
+       
+       Authors: Edwin Steiner
+                  
+       Last Change:
+
+*******************************************************************************/
+
+#ifndef __typeinfo_h_
+#define __typeinfo_h_
+
+/* #define DEBUG_TYPES */ /* XXX */
+
+#include "global.h"
+
+/* resolve typedef cycles *****************************************************/
+
+typedef struct typeinfo typeinfo;
+typedef struct typeinfo_mergedlist typeinfo_mergedlist;
+
+/* global variables ***********************************************************/
+
+/* XXX move this documentation to global.h */
+/* The following classinfo pointers are used internally by the type system.
+ * Please do not use them directly, use the TYPEINFO_ macros instead.
+ */
+
+/*
+ * pseudo_class_Arraystub
+ *     (extends Object implements Cloneable, java.io.Serializable)
+ *
+ *     If two arrays of incompatible component types are merged,
+ *     the resulting reference has no accessible components.
+ *     The result does, however, implement the interfaces Cloneable
+ *     and java.io.Serializable. This pseudo class is used internally
+ *     to represent such results. (They are *not* considered arrays!)
+ *
+ * pseudo_class_Array                          XXX delete?
+ *     (extends pseudo_class_Arraystub)
+ *
+ *     This pseudo class is used internally as the class of all arrays
+ *     to distinguish them from arbitrary Objects.
+ *
+ * pseudo_class_Null
+ *
+ *     This pseudo class is used internally to represent the
+ *     null type.
+ */
+
+/* data structures for the type system ****************************************/
+
+/* The typeinfo structure stores detailed information on reference types.
+ * (stack elements, variables, etc. with type == TYPE_ADR.)
+ * XXX: exclude ReturnAddresses?
+ *
+ * For primitive types either there is no typeinfo allocated or the
+ * typeclass pointer in the typeinfo struct is NULL.
+ *
+ * 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
+ *
+ *        In this case the other fields of the structure
+ *        are INVALID.
+ *
+ * B) typeclass == pseudo_class_Null
+ *
+ *        XXX
+ *
+ * C) typeclass is an array class
+ *
+ *        XXX
+ *
+ * D) typeclass == pseudo_class_Arraystub
+ *
+ *        XXX
+ *
+ * E) typeclass is an interface
+ *
+ *        XXX
+ *
+ * F) typeclass is a (non-pseudo-)class != java.lang.Object
+ *
+ *        XXX
+ *        All classinfos in u.merged.list (if any) are
+ *        subclasses of typeclass.
+ *
+ * G) typeclass is java.lang.Object
+ *
+ *        XXX
+ *        In this case u.merged.count and u.merged.list
+ *        are valid and may be non-zero.
+ *        The classinfos in u.merged.list (if any) can be
+ *        classes, interfaces or pseudo classes.
+ */
+
+/* The following algorithm is used to determine if the type described
+ * by this typeinfo struct supports the interface X:
+ *
+ *     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) XXX 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 {
+        classinfo           *typeclass;
+        classinfo           *elementclass; /* valid if dimension>0 */
+        typeinfo_mergedlist *merged;
+        u1                   dimension;
+        u1                   elementtype;  /* valid if dimension>0 */
+};
+
+struct typeinfo_mergedlist {
+        s4         count;
+        classinfo *list[1]; /* variable length! */
+};
+
+/****************************************************************************/
+/* MACROS                                                                   */
+/****************************************************************************/
+
+/* NOTE: These macros take typeinfo *structs* not pointers as arguments.
+ *       You have to dereference any pointers.
+ */
+
+/* internally used macros ***************************************************/
+
+/* internal, don't use this explicitly! */
+/* XXX change to GC? */
+#define TYPEINFO_ALLOCMERGED(mergedlist,count)                  \
+            {(mergedlist) = (typeinfo_mergedlist*)dump_alloc(   \
+                sizeof(typeinfo_mergedlist)                     \
+                + ((count)-1)*sizeof(classinfo*));}
+
+/* internal, don't use this explicitly! */
+/* XXX change to GC? */
+#if 0
+#define TYPEINFO_FREEMERGED(mergedlist)                         \
+            {mem_free((mergedlist),sizeof(typeinfo_mergedlist)  \
+                + ((mergedlist)->count - 1)*sizeof(classinfo*));}
+#endif
+#define TYPEINFO_FREEMERGED(mergedlist)
+
+/* internal, don't use this explicitly! */
+#define TYPEINFO_FREEMERGED_IF_ANY(mergedlist)                  \
+            {if (mergedlist) TYPEINFO_FREEMERGED(mergedlist);}
+
+/* macros for type queries **************************************************/
+
+#define TYPEINFO_IS_PRIMITIVE(info)                             \
+            ((info).typeclass == NULL)
+
+#define TYPEINFO_IS_REFERENCE(info)                             \
+            ((info).typeclass != NULL)
+
+#define TYPEINFO_IS_NULLTYPE(info)                              \
+            ((info).typeclass == pseudo_class_Null)
+
+/* 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 != NULL) )
+
+/* assumes that info describes an array type */
+#define TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info)                 \
+            ( ((info).elementclass != NULL)                     \
+              || ((info).dimension >= 2) )
+
+#define TYPEINFO_IS_ARRAY_OF_REFS(info)                         \
+            ( TYPEINFO_IS_ARRAY(info)                           \
+              && TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info) )
+
+/* macros for initializing typeinfo structures ******************************/
+
+#define TYPEINFO_INIT_PRIMITIVE(info)                           \
+            {(info).typeclass = NULL;                           \
+             (info).elementclass = NULL;                        \
+             (info).merged = NULL;                              \
+             (info).dimension = 0;                              \
+             (info).elementtype = 0;}
+
+#define TYPEINFO_INIT_NON_ARRAY_CLASSINFO(info,cinfo)   \
+            {(info).typeclass = (cinfo);                \
+             (info).elementclass = NULL;                \
+             (info).merged = NULL;                      \
+             (info).dimension = 0;                      \
+             (info).elementtype = 0;}
+
+#define TYPEINFO_INIT_NULLTYPE(info)                            \
+            TYPEINFO_INIT_CLASSINFO(info,pseudo_class_Null)
+
+/* XXX delete */
+#if 0
+#define TYPEINFO_INIT_ARRAY(info,dim,elmtype,elmcinfo)        \
+            {(info).typeclass = pseudo_class_Array;             \
+             (info).elementclass = (elmcinfo);                  \
+             (info).merged = NULL;                              \
+             (info).dimension = (dim);                          \
+             (info).elementtype = (elmtype);}
+#endif
+
+/* XXX delete? */
+#define TYPEINFO_INIT_ARRAY(info,cls)                                   \
+            {(info).typeclass = cls;                                    \
+             if (cls->vftbl->arraydesc->elementvftbl)                   \
+                 (info).elementclass = cls->vftbl->arraydesc->elementvftbl->class; \
+             else                                                       \
+                 (info).elementclass = NULL;                            \
+             (info).merged = NULL;                                      \
+             (info).dimension = cls->vftbl->arraydesc->dimension;       \
+             (info).elementtype = cls->vftbl->arraydesc->elementtype;}
+
+#define TYPEINFO_INIT_CLASSINFO(info,cls)                               \
+        {if (((info).typeclass = cls)->vftbl->arraydesc) {              \
+                if (cls->vftbl->arraydesc->elementvftbl)                \
+                    (info).elementclass = cls->vftbl->arraydesc->elementvftbl->class; \
+                else                                                    \
+                    (info).elementclass = NULL;                         \
+                (info).dimension = cls->vftbl->arraydesc->dimension;    \
+                (info).elementtype = cls->vftbl->arraydesc->elementtype;\
+            }                                                           \
+            else {                                                      \
+                (info).elementclass = NULL;                             \
+                (info).dimension = 0;                                   \
+                (info).elementtype = 0;                                 \
+            }                                                           \
+            (info).merged = NULL;}
+
+/* XXX */
+#define TYPEINFO_INC_DIMENSION(info)                            \
+             (info).dimension++
+
+#define TYPEINFO_INIT_FROM_FIELDINFO(info,fi)                   \
+            typeinfo_init_from_descriptor(&(info),              \
+                (fi)->descriptor->text,utf_end((fi)->descriptor));
+
+/* macros for freeing typeinfo structures ***********************************/
+
+#define TYPEINFO_FREE(info)                                     \
+            {TYPEINFO_FREEMERGED_IF_ANY((info).merged);         \
+             (info).merged = NULL;}
+
+/* macros for writing types (destination must have been initialized) ********/
+/* XXX delete them? */
+
+#define TYPEINFO_PUT_NULLTYPE(info)                             \
+            {(info).typeclass = pseudo_class_Null;}
+
+#define TYPEINFO_PUT_NON_ARRAY_CLASSINFO(info,cinfo)            \
+            {(info).typeclass = (cinfo);}
+
+/* XXX delete */
+#if 0
+#define TYPEINFO_PUT_ARRAY(info,dim,elmtype)                    \
+            {(info).typeclass = pseudo_class_Array;             \
+             (info).dimension = (dim);                          \
+             (info).elementtype = (elmtype);}
+#endif
+
+/* XXX delete? */
+#define TYPEINFO_PUT_ARRAY(info,cls)                                    \
+            {(info).typeclass = cls;                                    \
+             if (cls->vftbl->arraydesc->elementvftbl)                \
+                 (info).elementclass = cls->vftbl->arraydesc->elementvftbl->class; \
+             (info).dimension = cls->vftbl->arraydesc->dimension;       \
+             (info).elementtype = cls->vftbl->arraydesc->elementtype;}
+
+#define TYPEINFO_PUT_CLASSINFO(info,cls)                                \
+        {if (((info).typeclass = cls)->vftbl->arraydesc) {              \
+                if (cls->vftbl->arraydesc->elementvftbl)                \
+                    (info).elementclass = cls->vftbl->arraydesc->elementvftbl->class; \
+                (info).dimension = cls->vftbl->arraydesc->dimension;    \
+                (info).elementtype = cls->vftbl->arraydesc->elementtype; \
+            }}
+
+/* XXX delete */
+#if 0
+#define TYPEINFO_PUT_OBJECT_ARRAY(info,dim,elmcinfo)            \
+            {TYPEINFO_PUT_ARRAY(info,dim,ARRAYTYPE_OBJECT);     \
+             (info).elementclass = (elmcinfo);}
+#endif
+
+/* srcarray must be an array (not checked) */
+#define TYPEINFO_PUT_COMPONENT(srcarray,dst)                    \
+            {typeinfo_put_component(&(srcarray),&(dst));}
+
+/* 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)                                  \
+            {(dst) = (src);}
+
+/* TYPEINFO_CLONE makes a deep copy, the merged list (if any) is duplicated
+ * into a newly allocated array.
+ */
+#define TYPEINFO_CLONE(src,dst)                                 \
+            {(dst) = (src);                                     \
+             if ((dst).merged) typeinfo_clone(&(src),&(dst));}
+
+/****************************************************************************/
+/* FUNCTIONS                                                                */
+/****************************************************************************/
+
+/* inquiry functions (read-only) ********************************************/
+
+bool typeinfo_is_array(typeinfo *info);
+bool typeinfo_is_primitive_array(typeinfo *info,int arraytype);
+bool typeinfo_is_array_of_refs(typeinfo *info);
+
+bool typeinfo_implements_interface(typeinfo *info,classinfo *interf);
+bool typeinfo_is_assignable(typeinfo *value,typeinfo *dest);
+
+/* initialization functions *************************************************/
+
+void typeinfo_init_from_descriptor(typeinfo *info,char *utf_ptr,char *end_ptr);
+void typeinfo_init_component(typeinfo *srcarray,typeinfo *dst);
+
+int  typeinfo_count_method_args(utf *d,bool twoword); /* this not included */
+void typeinfo_init_from_method_args(utf *desc,u1 *typebuf,
+                                    typeinfo *infobuf,
+                                    int buflen,bool twoword,
+                                    int *returntype,typeinfo *returntypeinfo);
+
+void typeinfo_clone(typeinfo *src,typeinfo *dest);
+
+/* functions for the type system ********************************************/
+
+void typeinfo_free(typeinfo *info);
+
+bool typeinfo_merge(typeinfo *dest,typeinfo* y);
+
+/* debugging helpers ********************************************************/
+
+#ifdef DEBUG_TYPES
+
+void typeinfo_test();
+void typeinfo_init_from_fielddescriptor(typeinfo *info,char *desc);
+void typeinfo_print(FILE *file,typeinfo *info,int indent);
+void typeinfo_print_short(FILE *file,typeinfo *info);
+void typeinfo_print_type(FILE *file,int type,typeinfo *info);
+
+#endif // DEBUG_TYPES
+
+#endif
diff --git a/src/vm/jit/verify/typeinfo.tst b/src/vm/jit/verify/typeinfo.tst
new file mode 100644 (file)
index 0000000..4dc5232
--- /dev/null
@@ -0,0 +1,70 @@
+# Input for testing the typeinfo_ functions
+#
+# simple merges (result is common ancestor)
+#
+()Ljava/lang/Object;           ()Ljava/lang/Object;            ()Ljava/lang/Object;
+()Ljava/lang/Object;           ()Ljava/lang/String;            ()Ljava/lang/Object;
+()Ljava/lang/Number;           ()Ljava/lang/Integer;           ()Ljava/lang/Number;
+()Ljava/lang/Object;           ()Ljava/lang/Integer;           ()Ljava/lang/Object;
+()Ljava/lang/Object;           ()Ljava/lang/Cloneable;         ()Ljava/lang/Object;
+()Ljava/lang/Cloneable;                ()Ljava/lang/Cloneable;         ()Ljava/lang/Cloneable;
+()Ljava/lang/Cloneable;                ()[Ljava/lang/Object;           ()Ljava/lang/Cloneable;
+()Ljava/lang/Cloneable;                ()[I                            ()Ljava/lang/Cloneable;
+()Ljava/io/Serializable;       ()[Ljava/lang/Object;           ()Ljava/io/Serializable;
+()Ljava/io/Serializable;       ()[D                            ()Ljava/io/Serializable;
+()Ljava/lang/Object;           ()[Ljava/lang/Object;           ()Ljava/lang/Object;
+()Ljava/lang/Object;           ()[I                            ()Ljava/lang/Object;
+()L$ARRAYSTUB$;                        ()L$ARRAYSTUB$;                 ()L$ARRAYSTUB$;
+()L$ARRAYSTUB$;                ()Ljava/lang/Object;            ()Ljava/lang/Object;
+()L$ARRAYSTUB$;                ()Ljava/lang/Cloneable;         ()Ljava/lang/Cloneable;
+()L$ARRAYSTUB$;                ()Ljava/io/Serializable;        ()Ljava/io/Serializable;
+()L$NULL$;                     ()L$NULL$;                      ()L$NULL$;
+()L$NULL$;                     ()[Ljava/lang/Object;           ()[Ljava/lang/Object;
+#
+#
+# merges where the result is a mergedlist
+#
+()Ljava/lang/String;   ()Ljava/lang/Number;    (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;
+#
+#
+# merges with one mergedlist where the resulting mergedlist is unchanged
+#
+(Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;       ()Ljava/lang/String;    (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;
+(Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;       ()Ljava/lang/Number;    (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Integer;   (Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Long;      (Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()L$NULL$;              (Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;
+#
+#
+# merges with one mergedlist where the result has no mergedlist
+#
+(Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;       ()Ljava/lang/Object;    ()Ljava/lang/Object;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Number;    ()Ljava/lang/Number;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Object;    ()Ljava/lang/Object;
+#
+#
+# one mergedlist where the resulting mergeslist is longer
+#
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Double;    (Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/Double;)Ljava/lang/Number;
+#
+#
+# merges with two mergedlists with the same typeclass
+#
+(Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;       (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;        (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                (Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/Float;)Ljava/lang/Number;        (Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/Float;)Ljava/lang/Number;
+#
+#
+# merges with two mergedlists with different typeclass
+#
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                (Ljava/lang/Integer;Ljava/lang/String;)Ljava/lang/Object;       (Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/String;)Ljava/lang/Object;
+#
+#
+# merging primitive arrays
+#
+()[I   ()[I    ()[I
+()[I   ()[F    ()L$ARRAYSTUB$;
+#
+#
+# merging reference arrays with primitive arrays
+#
+()[I   ()[Ljava/lang/String;   ()L$ARRAYSTUB$;
index 8ff55a17fa7cf07d47c57625ddbb5e1ba55a5655..25cefc519231bc6fe3dab378dcb2b9e8e0980331 100644 (file)
@@ -29,7 +29,7 @@
             Roman Obermaiser
             Mark Probst
 
-   $Id: loader.c 682 2003-12-01 15:33:30Z jowenn $
+   $Id: loader.c 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -1714,10 +1714,12 @@ class_link_array(classinfo *c)
                if (compvftbl->arraydesc) {
                        desc->elementvftbl = compvftbl->arraydesc->elementvftbl;
                        desc->dimension = compvftbl->arraydesc->dimension + 1;
+                       desc->elementtype = compvftbl->arraydesc->elementtype;
                }
                else {
                        desc->elementvftbl = compvftbl;
                        desc->dimension = 1;
+                       desc->elementtype = ARRAYTYPE_OBJECT;
                }
        }
        else {
@@ -1754,6 +1756,7 @@ class_link_array(classinfo *c)
                desc->componentvftbl = NULL;
                desc->elementvftbl = NULL;
                desc->dimension = 1;
+               desc->elementtype = desc->arraytype;
        }
 
        return desc;
@@ -2906,7 +2909,7 @@ create_pseudo_classes()
 {
     /* pseudo class for Arraystubs (extends java.lang.Object) */
     
-    pseudo_class_Arraystub = class_new( utf_new_char(";Arraystub;") );
+    pseudo_class_Arraystub = class_new( utf_new_char("$ARRAYSTUB$") );
     list_remove(&unloadedclasses,pseudo_class_Arraystub);
 
     pseudo_class_Arraystub->super = class_java_lang_Object;
@@ -2924,7 +2927,7 @@ create_pseudo_classes()
 
     /* pseudo class representing the null type */
     
-    pseudo_class_Null = class_new( utf_new_char(";Null;") );
+    pseudo_class_Null = class_new( utf_new_char("$NULL$") );
     list_remove(&unloadedclasses,pseudo_class_Null);
 
     pseudo_class_Null->super = class_java_lang_Object;
index 4b39eb9fffefd986fbaa8f7fc65b444a226f1837..efddda46be7a5ad16e2a01aefc94035d915df04b 100644 (file)
@@ -35,7 +35,7 @@
        - the heap
        - additional support functions
 
-   $Id: tables.c 682 2003-12-01 15:33:30Z jowenn $
+   $Id: tables.c 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -878,6 +878,43 @@ classinfo *class_array_of(classinfo *component)
     return class_new( utf_new(namebuf,namelen) );
 }
 
+/*************** Function: class_multiarray_of ********************************
+
+    Returns an array class with the given dimension and element class.
+    The array class is dynamically created if neccessary.
+
+*******************************************************************************/
+
+classinfo *class_multiarray_of(int dim,classinfo *element)
+{
+    int namelen;
+    char *namebuf;
+
+       if (dim<1)
+               panic("Invalid array dimension requested");
+
+    /* Assemble the array class name */
+    namelen = element->name->blength;
+    
+    if (element->name->text[0] == '[') {
+        /* the element is itself an array */
+        namebuf = DMNEW(char,namelen+dim);
+        memcpy(namebuf+dim,element->name->text,namelen);
+        namelen += dim;
+    }
+    else {
+        /* the element is a non-array class */
+        namebuf = DMNEW(char,namelen+2+dim);
+        namebuf[dim] = 'L';
+        memcpy(namebuf+dim+1,element->name->text,namelen);
+        namelen += (2+dim);
+        namebuf[namelen-1] = ';';
+    }
+       memset(namebuf,'[',dim);
+
+    return class_new( utf_new(namebuf,namelen) );
+}
+
 /************************** function: utf_strlen ******************************
 
     determine number of unicode characters in the utf string
index 99f365495d2c15c51b21fc02ccf3e7d24bddd18a..de0ae2cae8a898b5c7296074240f4df20ae2e1c6 100644 (file)
@@ -26,7 +26,7 @@
 
    Authors: Reinhard Grafl
 
-   $Id: tables.h 664 2003-11-21 18:24:01Z jowenn $
+   $Id: tables.h 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -80,6 +80,9 @@ classinfo *class_new(utf *u);
 /* return an array class with the given component class */
 classinfo *class_array_of(classinfo *component);
 
+/* return an array class with the given dimension and element class */
+classinfo *class_multiarray_of(int dim,classinfo *element);
+
 /* get javatype according to a typedescriptor */
 u2 desc_to_type(utf *descriptor);
 
index 4b39eb9fffefd986fbaa8f7fc65b444a226f1837..efddda46be7a5ad16e2a01aefc94035d915df04b 100644 (file)
--- a/tables.c
+++ b/tables.c
@@ -35,7 +35,7 @@
        - the heap
        - additional support functions
 
-   $Id: tables.c 682 2003-12-01 15:33:30Z jowenn $
+   $Id: tables.c 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -878,6 +878,43 @@ classinfo *class_array_of(classinfo *component)
     return class_new( utf_new(namebuf,namelen) );
 }
 
+/*************** Function: class_multiarray_of ********************************
+
+    Returns an array class with the given dimension and element class.
+    The array class is dynamically created if neccessary.
+
+*******************************************************************************/
+
+classinfo *class_multiarray_of(int dim,classinfo *element)
+{
+    int namelen;
+    char *namebuf;
+
+       if (dim<1)
+               panic("Invalid array dimension requested");
+
+    /* Assemble the array class name */
+    namelen = element->name->blength;
+    
+    if (element->name->text[0] == '[') {
+        /* the element is itself an array */
+        namebuf = DMNEW(char,namelen+dim);
+        memcpy(namebuf+dim,element->name->text,namelen);
+        namelen += dim;
+    }
+    else {
+        /* the element is a non-array class */
+        namebuf = DMNEW(char,namelen+2+dim);
+        namebuf[dim] = 'L';
+        memcpy(namebuf+dim+1,element->name->text,namelen);
+        namelen += (2+dim);
+        namebuf[namelen-1] = ';';
+    }
+       memset(namebuf,'[',dim);
+
+    return class_new( utf_new(namebuf,namelen) );
+}
+
 /************************** function: utf_strlen ******************************
 
     determine number of unicode characters in the utf string
index 99f365495d2c15c51b21fc02ccf3e7d24bddd18a..de0ae2cae8a898b5c7296074240f4df20ae2e1c6 100644 (file)
--- a/tables.h
+++ b/tables.h
@@ -26,7 +26,7 @@
 
    Authors: Reinhard Grafl
 
-   $Id: tables.h 664 2003-11-21 18:24:01Z jowenn $
+   $Id: tables.h 687 2003-12-04 22:29:54Z edwin $
 
 */
 
@@ -80,6 +80,9 @@ classinfo *class_new(utf *u);
 /* return an array class with the given component class */
 classinfo *class_array_of(classinfo *component);
 
+/* return an array class with the given dimension and element class */
+classinfo *class_multiarray_of(int dim,classinfo *element);
+
 /* get javatype according to a typedescriptor */
 u2 desc_to_type(utf *descriptor);
 
diff --git a/typeinfo.c b/typeinfo.c
new file mode 100644 (file)
index 0000000..0c89617
--- /dev/null
@@ -0,0 +1,1408 @@
+/********************************* typeinfo.c *********************************
+
+       Copyright (c) 2003 ? XXX
+
+       See file COPYRIGHT for information on usage and disclaimer of warranties
+
+       functions for the compiler's type system
+       
+       Authors: Edwin Steiner
+                  
+       Last Change:
+
+*******************************************************************************/
+
+#include "typeinfo.h"
+#include "tables.h"
+#include "loader.h"
+
+#define TYPEINFO_REUSE_MERGED
+
+#define CLASS_IMPLEMENTS_INTERFACE(cls,index)                   \
+    ( ((index) < (cls)->vftbl->interfacetablelength)            \
+      && (VFTBLINTERFACETABLE((cls)->vftbl,(index)) != NULL) )
+
+/**********************************************************************/
+/* READ-ONLY FUNCTIONS                                                */
+/* The following functions don't change typeinfo data.                */
+/**********************************************************************/
+
+bool
+typeinfo_is_array(typeinfo *info)
+{
+    return TYPEINFO_IS_ARRAY(*info);
+}
+
+bool
+typeinfo_is_primitive_array(typeinfo *info,int arraytype)
+{
+    return TYPEINFO_IS_PRIMITIVE_ARRAY(*info,arraytype);
+}
+
+bool
+typeinfo_is_array_of_refs(typeinfo *info)
+{
+    return TYPEINFO_IS_ARRAY_OF_REFS(*info);
+}
+
+static
+bool
+interface_extends_interface(classinfo *cls,classinfo *interf)
+{
+    int i;
+    
+    /* first check direct superinterfaces */
+    for (i=0; i<cls->interfacescount; ++i) {
+        if (cls->interfaces[i] == interf)
+            return true;
+    }
+    
+    /* check indirect superinterfaces */
+    for (i=0; i<cls->interfacescount; ++i) {
+        if (interface_extends_interface(cls->interfaces[i],interf))
+            return true;
+    }
+    
+    return false;
+}
+
+/* XXX If really a performance issue, this could become a macro. */
+static
+bool
+classinfo_implements_interface(classinfo *cls,classinfo *interf)
+{
+    if (cls->flags & ACC_INTERFACE) {
+        /* cls is an interface */
+        if (cls == interf)
+            return true;
+
+        /* check superinterfaces */
+        return interface_extends_interface(cls,interf);
+    }
+
+    return CLASS_IMPLEMENTS_INTERFACE(cls,interf->index);
+}
+
+bool mergedlist_implements_interface(typeinfo_mergedlist *merged,
+                                     classinfo *interf)
+{
+    int i;
+    classinfo **mlist;
+    
+    /* Check if there is an non-empty mergedlist. */
+    if (!merged)
+        return false;
+
+    /* If all classinfos in the (non-empty) merged array implement the
+     * interface return true, otherwise false.
+     */
+    mlist = merged->list;
+    i = merged->count;
+    while (i--) {
+        if (!classinfo_implements_interface(*mlist++,interf))
+            return false;
+    }
+    return true;
+}
+
+bool
+merged_implements_interface(classinfo *typeclass,typeinfo_mergedlist *merged,
+                            classinfo *interf)
+{
+    /* primitive types don't support interfaces. */
+    if (!typeclass)
+        return false;
+
+    /* the null type can be cast to any interface type. */
+    if (typeclass == pseudo_class_Null)
+        return true;
+
+    /* check if typeclass implements the interface. */
+    if (classinfo_implements_interface(typeclass,interf))
+        return true;
+
+    /* check the mergedlist */
+    return (merged && mergedlist_implements_interface(merged,interf));
+}
+
+bool
+typeinfo_is_assignable(typeinfo *value,typeinfo *dest)
+{
+    classinfo *cls;
+
+    cls = value->typeclass;
+
+    /* DEBUG CHECK: dest must not have a merged list. */
+#ifdef DEBUG_TYPES
+    if (dest->merged)
+        panic("Internal error: typeinfo_is_assignable on merged destination.");
+#endif
+    
+    /* assignments of primitive values are not checked here. */
+    if (!cls && !dest->typeclass)
+        return true;
+
+    /* the null type can be assigned to any type */
+    if (TYPEINFO_IS_NULLTYPE(*value))
+        return true;
+
+    /* primitive and reference types are not assignment compatible. */
+    if (!cls || !dest->typeclass)
+        return false;
+
+    if (dest->typeclass->flags & ACC_INTERFACE) {
+        /* We are assigning to an interface type. */
+        return merged_implements_interface(cls,value->merged,
+                                           dest->typeclass);
+    }
+
+    if (TYPEINFO_IS_ARRAY(*dest)) {
+        /* We are assigning to an array type. */
+        if (!TYPEINFO_IS_ARRAY(*value))
+            return false;
+
+        /* {Both value and dest are array types.} */
+
+        /* value must have at least the dimension of dest. */
+        if (value->dimension < dest->dimension)
+            return false;
+
+        if (value->dimension > dest->dimension) {
+            /* value has higher dimension so we need to check
+             * if its component array can be assigned to the
+             * element type of dest */
+            
+            if (dest->elementclass->flags & ACC_INTERFACE) {
+                /* We are assigning to an interface type. */
+                return classinfo_implements_interface(pseudo_class_Arraystub,
+                                                      dest->elementclass);
+            }
+
+            /* We are assigning to a class type. */
+            return class_issubclass(pseudo_class_Arraystub,dest->elementclass);
+        }
+
+        /* {value and dest have the same dimension} */
+
+        if (value->elementtype != dest->elementtype)
+            return false;
+
+        if (value->elementclass) {
+            /* We are assigning an array of objects so we have to
+             * check if the elements are assignable.
+             */
+
+            if (dest->elementclass->flags & ACC_INTERFACE) {
+                /* We are assigning to an interface type. */
+
+                return merged_implements_interface(value->elementclass,
+                                                   value->merged,
+                                                   dest->elementclass);
+            }
+            
+            /* We are assigning to a class type. */
+            return class_issubclass(value->elementclass,dest->elementclass);
+        }
+
+        return true;
+    }
+
+    /* {dest is not an array} */
+        
+    /* We are assigning to a class type */
+    if (cls->flags & ACC_INTERFACE)
+        cls = class_java_lang_Object;
+    
+    return class_issubclass(cls,dest->typeclass);
+}
+
+/**********************************************************************/
+/* INITIALIZATION FUNCTIONS                                           */
+/* The following functions fill in uninitialized typeinfo structures. */
+/**********************************************************************/
+
+/* XXX delete */
+#if 0
+void
+typeinfo_init_from_arraydescriptor(typeinfo *info,
+                                   constant_arraydescriptor *desc)
+{
+    int dim = 1;
+    
+    /* Arrays are instances of the pseudo class Array. */
+    info->typeclass = pseudo_class_Array; /* XXX */
+    info->merged = NULL;
+
+    /* Handle multidimensional arrays */
+    while (desc->arraytype == ARRAYTYPE_ARRAY) {
+        dim++;
+        desc = desc->elementdescriptor;
+    }
+
+    info->dimension = dim;
+
+    if ((info->elementtype = desc->arraytype) == ARRAYTYPE_OBJECT) {
+        info->elementclass = desc->objectclass;
+    }
+    else {
+        info->elementclass = NULL;
+    }
+}
+#endif
+
+void
+typeinfo_init_from_descriptor(typeinfo *info,char *utf_ptr,char *end_ptr)
+{
+    classinfo *cls;
+    char *end;
+    cls = class_from_descriptor(utf_ptr,end_ptr,&end,CLASSLOAD_NEW);
+
+    if (!cls)
+        panic("Invalid descriptor.");
+
+    switch (*utf_ptr) {
+      case 'L':
+      case '[':
+          /* a class, interface or array descriptor */
+          TYPEINFO_INIT_CLASSINFO(*info,cls);
+          break;
+      default:
+          /* a primitive type */
+          TYPEINFO_INIT_PRIMITIVE(*info);
+    }
+
+    /* exceeding characters */         
+    if (end!=end_ptr) panic ("descriptor has exceeding chars");
+}
+
+int
+typeinfo_count_method_args(utf *d,bool twoword)
+{
+    int args = 0;
+    char *utf_ptr = d->text;
+    char *end_pos = utf_end(d);
+    char c,ch;
+
+    /* method descriptor must start with parenthesis */
+    if (*utf_ptr++ != '(') panic ("Missing '(' in method descriptor");
+
+    /* check arguments */
+    while ((c = *utf_ptr++) != ')') {
+        switch (c) {
+          case 'B':
+          case 'C':
+          case 'I':
+          case 'S':
+          case 'Z':  
+          case 'F':  
+              /* primitive one-word type */
+              args++;
+              break;
+              
+          case 'J':  
+          case 'D':
+              /* primitive two-word type */
+              args++;
+              if (twoword) args++;
+              break;
+              
+          case 'L':
+              /* skip classname */
+              while ( *utf_ptr++ != ';' )
+                  if (utf_ptr>=end_pos) 
+                      panic ("Missing ';' in objecttype-descriptor");
+              
+              args++;
+              break;
+              
+          case '[' :
+              /* array type */ 
+              while ((ch = *utf_ptr++)=='[') 
+                  /* skip */ ;
+              
+              /* component type of array */
+              switch (ch) {
+                case 'B':
+                case 'C':
+                case 'I':
+                case 'S':
+                case 'Z':  
+                case 'J':  
+                case 'F':  
+                case 'D':
+                    /* primitive type */  
+                    break;
+                    
+                case 'L':
+                    /* skip classname */
+                    while ( *utf_ptr++ != ';' )
+                        if (utf_ptr>=end_pos) 
+                            panic ("Missing ';' in objecttype-descriptor");
+                    break;
+                    
+                default:   
+                    panic ("Ill formed methodtype-descriptor");
+              }
+              
+              args++;
+              break;
+              
+          default:   
+              panic ("Ill formed methodtype-descriptor");
+        }                      
+    }
+
+    return args;
+}
+
+void
+typeinfo_init_from_method_args(utf *desc,u1 *typebuf,typeinfo *infobuf,
+                               int buflen,bool twoword,
+                               int *returntype,typeinfo *returntypeinfo)
+{
+    char *utf_ptr = desc->text;     /* current position in utf text   */
+    char *end_pos = utf_end(desc);  /* points behind utf string       */
+    char c;
+    int args = 0;
+    classinfo *cls;
+
+    /* method descriptor must start with parenthesis */
+    if (*utf_ptr++ != '(') panic ("Missing '(' in method descriptor");
+
+    /* check arguments */
+    while ((c = *utf_ptr) != ')') {
+        cls = class_from_descriptor(utf_ptr,end_pos,&utf_ptr,CLASSLOAD_NEW);
+        if (!cls)
+            panic("Invalid method descriptor.");
+        
+        switch (c) {
+          case 'B':
+          case 'C':
+          case 'I':
+          case 'S':
+          case 'Z':  
+          case 'F':  
+              /* primitive one-word type */
+              if (++args > buflen)
+                  panic("Buffer too small for method arguments.");
+              if (typebuf)
+                  *typebuf++ = (c == 'F') ? TYPE_FLOAT : TYPE_INT; /* XXX TYPE_FLT? */
+              TYPEINFO_INIT_PRIMITIVE(*infobuf);
+              infobuf++;
+              break;
+              
+          case 'J':  
+          case 'D':
+              /* primitive two-word type */
+              if (++args > buflen)
+                  panic("Buffer too small for method arguments.");
+              if (typebuf)
+                  *typebuf++ = (c == 'J') ? TYPE_LONG : TYPE_DOUBLE; /* XXX TYPE_DBL? */
+              TYPEINFO_INIT_PRIMITIVE(*infobuf);
+              infobuf++;
+              if (twoword) {
+                  if (++args > buflen)
+                      panic("Buffer too small for method arguments.");
+                  TYPEINFO_INIT_PRIMITIVE(*infobuf);
+                  infobuf++;
+              }
+              break;
+              
+          case 'L':
+          case '[' :
+              /* reference type */
+              
+              if (++args > buflen)
+                  panic("Buffer too small for method arguments.");
+              if (typebuf)
+                  *typebuf++ = TYPE_ADDRESS;
+              
+              TYPEINFO_INIT_CLASSINFO(*infobuf,cls);
+              /* XXX remove */ /* utf_display(cls->name); */
+              infobuf++;
+              break;
+          
+          default:   
+              panic ("Ill formed methodtype-descriptor (type)");
+        }
+    }
+    utf_ptr++; /* skip ')' */
+
+    /* check returntype */
+    if (returntype) {
+        switch (*utf_ptr) {
+          case 'B':
+          case 'C':
+          case 'I':
+          case 'S':
+          case 'Z':
+              *returntype = TYPE_INT;
+              goto primitive_tail;
+              
+          case 'J':
+              *returntype = TYPE_LONG;
+              goto primitive_tail;
+              
+          case 'F':  
+              *returntype = TYPE_FLOAT;
+              goto primitive_tail;
+              
+          case 'D':
+              *returntype = TYPE_DOUBLE;
+              goto primitive_tail;
+
+          case 'V':
+              *returntype = TYPE_VOID;
+      primitive_tail:
+              if ((utf_ptr+1) != end_pos)
+                  panic ("Method-descriptor has exceeding chars");
+              if (returntypeinfo) {
+                  TYPEINFO_INIT_PRIMITIVE(*returntypeinfo);
+              }
+              break;
+
+          case 'L':
+          case '[':
+              *returntype = TYPE_ADDRESS;
+              cls = class_from_descriptor(utf_ptr,end_pos,&utf_ptr,CLASSLOAD_NEW);
+              if (!cls)
+                  panic("Invalid return type");
+              if (utf_ptr != end_pos)
+                  panic ("Method-descriptor has exceeding chars");
+              if (returntypeinfo) {
+                  TYPEINFO_INIT_CLASSINFO(*returntypeinfo,cls);
+              }
+              break;
+
+          default:   
+              panic ("Ill formed methodtype-descriptor (returntype)");
+        }
+    }
+}
+
+/* XXX could be made a macro if really performance critical */
+void
+typeinfo_init_component(typeinfo *srcarray,typeinfo *dst)
+{
+    vftbl *comp = NULL;
+    
+    /* XXX find component class */
+    if (!TYPEINFO_IS_ARRAY(*srcarray))
+        panic("Trying to access component of non-array");
+
+    comp = srcarray->typeclass->vftbl->arraydesc->componentvftbl;
+    if (comp) {
+        TYPEINFO_INIT_CLASSINFO(*dst,comp->class);
+    }
+    else {
+        TYPEINFO_INIT_PRIMITIVE(*dst);
+    }
+
+    /* XXX assign directly ? */
+#if 0
+    if ((dst->dimension = srcarray->dimension - 1) == 0) {
+        dst->typeclass = srcarray->elementclass;
+        dst->elementtype = 0;
+        dst->elementclass = NULL;
+    }
+    else {
+        dst->typeclass = srcarray->typeclass;
+        dst->elementtype = srcarray->elementtype;
+        dst->elementclass = srcarray->elementclass;
+    }
+#endif
+    
+    dst->merged = srcarray->merged;
+}
+
+/* Condition: src != dest. */
+void
+typeinfo_clone(typeinfo *src,typeinfo *dest)
+{
+    int count;
+    classinfo **srclist,**destlist;
+
+#ifdef DEBUG_TYPES
+    if (src == dest)
+        panic("Internal error: typeinfo_clone with src==dest");
+#endif
+    
+    *dest = *src;
+
+    if (src->merged) {
+        count = src->merged->count;
+        TYPEINFO_ALLOCMERGED(dest->merged,count);
+        dest->merged->count = count;
+
+        /* XXX use memcpy? */
+        srclist = src->merged->list;
+        destlist = dest->merged->list;
+        while (count--)
+            *destlist++ = *srclist++;
+    }
+}
+
+/**********************************************************************/
+/* MISCELLANEOUS FUNCTIONS                                            */
+/**********************************************************************/
+
+void
+typeinfo_free(typeinfo *info)
+{
+    TYPEINFO_FREE(*info);
+}
+
+/**********************************************************************/
+/* MERGING FUNCTIONS                                                  */
+/* The following functions are used to merge the types represented by */
+/* two typeinfo structures into one typeinfo structure.               */
+/**********************************************************************/
+
+static
+void
+typeinfo_merge_error(char *str,typeinfo *x,typeinfo *y) {
+#ifdef DEBUG_TYPES
+    fprintf(stderr,"Error in typeinfo_merge: %s\n",str);
+    fprintf(stderr,"Typeinfo x:\n");
+    typeinfo_print(stderr,x,1);
+    fprintf(stderr,"Typeinfo y:\n");
+    typeinfo_print(stderr,y,1);
+#endif
+    panic(str);
+}
+
+/* Condition: clsx != clsy. */
+/* Returns: true if dest was changed (always true). */
+static
+bool
+typeinfo_merge_two(typeinfo *dest,classinfo *clsx,classinfo *clsy)
+{
+    TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+    TYPEINFO_ALLOCMERGED(dest->merged,2);
+    dest->merged->count = 2;
+
+#ifdef DEBUG_TYPES
+    if (clsx == clsy)
+        panic("Internal error: typeinfo_merge_two called with clsx==clsy.");
+#endif
+
+    if (clsx < clsy) {
+        dest->merged->list[0] = clsx;
+        dest->merged->list[1] = clsy;
+    }
+    else {
+        dest->merged->list[0] = clsy;
+        dest->merged->list[1] = clsx;
+    }
+
+    return true;
+}
+
+/* Returns: true if dest was changed. */
+static
+bool
+typeinfo_merge_add(typeinfo *dest,typeinfo_mergedlist *m,classinfo *cls)
+{
+    int count;
+    typeinfo_mergedlist *newmerged;
+    classinfo **mlist,**newlist;
+
+    count = m->count;
+    mlist = m->list;
+
+    /* Check if cls is already in the mergedlist m. */
+    while (count--) {
+        if (*mlist++ == cls) {
+            /* cls is in the list, so m is the resulting mergedlist */
+            if (dest->merged == m)
+                return false;
+
+            /* We have to copy the mergedlist */
+            TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+            count = m->count;
+            TYPEINFO_ALLOCMERGED(dest->merged,count);
+            dest->merged->count = count;
+            newlist = dest->merged->list;
+            mlist = m->list;
+            while (count--) {
+                *newlist++ = *mlist++;
+            }
+            return true;
+        }
+    }
+
+    /* Add cls to the mergedlist. */
+    count = m->count;
+    TYPEINFO_ALLOCMERGED(newmerged,count+1);
+    newmerged->count = count+1;
+    newlist = newmerged->list;    
+    mlist = m->list;
+    while (count) {
+        if (*mlist > cls)
+            break;
+        *newlist++ = *mlist++;
+        count--;
+    }
+    *newlist++ = cls;
+    while (count--) {
+        *newlist++ = *mlist++;
+    }
+
+    /* Put the new mergedlist into dest. */
+    TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+    dest->merged = newmerged;
+    
+    return true;
+}
+
+/* Returns: true if dest was changed. */
+static
+bool
+typeinfo_merge_mergedlists(typeinfo *dest,typeinfo_mergedlist *x,
+                           typeinfo_mergedlist *y)
+{
+    int count = 0;
+    int countx,county;
+    typeinfo_mergedlist *temp,*result;
+    classinfo **clsx,**clsy,**newlist;
+
+    /* count the elements that will be in the resulting list */
+    /* (Both lists are sorted, equal elements are counted only once.) */
+    clsx = x->list;
+    clsy = y->list;
+    countx = x->count;
+    county = y->count;
+    while (countx && county) {
+        if (*clsx == *clsy) {
+            clsx++;
+            clsy++;
+            countx--;
+            county--;
+        }
+        else if (*clsx < *clsy) {
+            clsx++;
+            countx--;
+        }
+        else {
+            clsy++;
+            county--;
+        }
+        count++;
+    }
+    count += countx + county;
+
+    /* {The new mergedlist will have count entries.} */
+
+    if (y->count == count) {
+        temp = x; x = y; y = temp;
+    }
+    /* {If one of x,y is already the result it is x.} */
+    if (x->count == count) {
+        /* x->merged is equal to the result */
+        if (x == dest->merged)
+            return false;
+
+        if (!dest->merged || dest->merged->count != count) {
+            TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+            TYPEINFO_ALLOCMERGED(dest->merged,count);
+            dest->merged->count = count;
+        }
+
+        newlist = dest->merged->list;
+        clsx = x->list;
+        while (count--) {
+            *newlist++ = *clsx++;
+        }
+        return true;
+    }
+
+    /* {We have to merge two lists.} */
+
+    /* allocate the result list */
+    TYPEINFO_ALLOCMERGED(result,count);
+    result->count = count;
+    newlist = result->list;
+
+    /* merge the sorted lists */
+    clsx = x->list;
+    clsy = y->list;
+    countx = x->count;
+    county = y->count;
+    while (countx && county) {
+        if (*clsx == *clsy) {
+            *newlist++ = *clsx++;
+            clsy++;
+            countx--;
+            county--;
+        }
+        else if (*clsx < *clsy) {
+            *newlist++ = *clsx++;
+            countx--;
+        }
+        else {
+            *newlist++ = *clsy++;
+            county--;
+        }
+    }
+    while (countx--)
+            *newlist++ = *clsx++;
+    while (county--)
+            *newlist++ = *clsy++;
+
+    /* replace the list in dest with the result list */
+    TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+    dest->merged = result;
+
+    return true;
+}
+
+static
+bool
+typeinfo_merge_nonarrays(typeinfo *dest,
+                         classinfo **result,
+                         classinfo *clsx,classinfo *clsy,
+                         typeinfo_mergedlist *mergedx,
+                         typeinfo_mergedlist *mergedy)
+{
+    classinfo *tcls,*common;
+    typeinfo_mergedlist *tmerged;
+    bool changed;
+
+    /* XXX remove */
+    /*
+#ifdef DEBUG_TYPES
+    typeinfo dbgx,dbgy;
+    printf("typeinfo_merge_nonarrays:\n");
+    TYPEINFO_INIT_CLASSINFO(dbgx,clsx);
+    dbgx.merged = mergedx;
+    TYPEINFO_INIT_CLASSINFO(dbgy,clsy);
+    dbgy.merged = mergedy;
+    typeinfo_print(stdout,&dbgx,4);
+    printf("  with:\n");
+    typeinfo_print(stdout,&dbgy,4);
+#endif
+    */ 
+
+    /* Common case: clsx == clsy */
+    /* (This case is very simple unless *both* x and y really represent
+     *  merges of subclasses of clsx==clsy.)
+     */
+    /* XXX count this case for statistics */
+    if ((clsx == clsy) && (!mergedx || !mergedy)) {
+  return_simple_x:
+        /* XXX remove */ /* log_text("return simple x"); */
+        changed = (dest->merged != NULL);
+        TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+        dest->merged = NULL;
+        *result = clsx;
+        /* XXX remove */ /* log_text("returning"); */
+        return changed;
+    }
+    
+    /* If clsy is an interface, swap x and y. */
+    if (clsy->flags & ACC_INTERFACE) {
+        tcls = clsx; clsx = clsy; clsy = tcls;
+        tmerged = mergedx; mergedx = mergedy; mergedy = tmerged;
+    }
+    /* {We know: If only one of x,y is an interface it is x.} */
+
+    /* Handle merging of interfaces: */
+    if (clsx->flags & ACC_INTERFACE) {
+        /* {clsx is an interface and mergedx == NULL.} */
+        
+        if (clsy->flags & ACC_INTERFACE) {
+            /* We are merging two interfaces. */
+            /* {mergedy == NULL} */
+            /* XXX: should we optimize direct superinterfaces? */
+
+            /* {We know that clsx!=clsy (see common case at beginning.)} */
+            *result = class_java_lang_Object;
+            return typeinfo_merge_two(dest,clsx,clsy);
+        }
+
+        /* {We know: x is an interface, clsy is a class.} */
+
+        /* Check if we are merging an interface with java.lang.Object */
+        if (clsy == class_java_lang_Object && !mergedy) {
+            clsx = clsy;
+            goto return_simple_x;
+        }
+            
+
+        /* If the type y implements clsx then the result of the merge
+         * is clsx regardless of mergedy.
+         */
+        
+        if (CLASS_IMPLEMENTS_INTERFACE(clsy,clsx->index)
+            || mergedlist_implements_interface(mergedy,clsx))
+        {
+            /* y implements x, so the result of the merge is x. */
+            goto return_simple_x;
+        }
+        
+        /* {We know: x is an interface, the type y a class or a merge
+         * of subclasses and does not implement x.} */
+
+        /* There may still be superinterfaces of x which are implemented
+         * by y, too, so we have to add clsx to the mergedlist.
+         */
+
+        /* XXX if x has no superinterfaces we could return a simple java.lang.Object */
+        
+        common = class_java_lang_Object;
+        goto merge_with_simple_x;
+    }
+
+    /* {We know: x and y are classes (not interfaces).} */
+    
+    /* If *x is deeper in the inheritance hierarchy swap x and y. */
+    if (clsx->index > clsy->index) {
+        tcls = clsx; clsx = clsy; clsy = tcls;
+        tmerged = mergedx; mergedx = mergedy; mergedy = tmerged;
+    }
+
+    /* {We know: y is at least as deep in the hierarchy as x.} */
+
+    /* Find nearest common anchestor for the classes. */
+    common = clsx;
+    tcls = clsy;
+    while (tcls->index > common->index)
+        tcls = tcls->super;
+    while (common != tcls) {
+        common = common->super;
+        tcls = tcls->super;
+    }
+
+    /* {common == nearest common anchestor of clsx and clsy.} */
+
+    /* XXX remove */ /* printf("common: "); utf_display(common->name); printf("\n"); */
+
+    /* If clsx==common and x is a whole class (not a merge of subclasses)
+     * then the result of the merge is clsx.
+     */
+    if (clsx == common && !mergedx) {
+        goto return_simple_x;
+    }
+   
+    if (mergedx) {
+        *result = common;
+        if (mergedy)
+            return typeinfo_merge_mergedlists(dest,mergedx,mergedy);
+        else
+            return typeinfo_merge_add(dest,mergedx,clsy);
+    }
+
+ merge_with_simple_x:
+    *result = common;
+    if (mergedy)
+        return typeinfo_merge_add(dest,mergedy,clsx);
+    else
+        return typeinfo_merge_two(dest,clsx,clsy);
+}
+
+/* Condition: *dest must be a valid initialized typeinfo. */
+/* Condition: dest != y. */
+/* Returns: true if dest was changed. */
+bool
+typeinfo_merge(typeinfo *dest,typeinfo* y)
+{
+    typeinfo *x;
+    typeinfo *tmp;                      /* used for swapping */
+    classinfo *common;
+    classinfo *elementclass;
+    int dimension;
+    int elementtype;
+    bool changed;
+
+    /* XXX remove */
+    /*
+#ifdef DEBUG_TYPES
+    typeinfo_print(stdout,dest,4);
+    typeinfo_print(stdout,y,4);
+#endif
+    */ 
+
+    /* This function cannot be used to merge primitive types. */
+    if (!dest->typeclass || !y->typeclass)
+        typeinfo_merge_error("Trying to merge primitive types.",dest,y);
+
+#ifdef DEBUG_TYPES
+    if (dest == y)
+        panic("Internal error: typeinfo_merge with dest==y");
+    
+    /* check that no unlinked classes are merged. */
+    if (!dest->typeclass->linked || !y->typeclass->linked)
+        typeinfo_merge_error("Trying to merge unlinked class(es).",dest,y);
+#endif
+
+    /* XXX remove */ /* log_text("Testing common case"); */
+
+    /* Common case: class dest == class y */
+    /* (This case is very simple unless *both* dest and y really represent
+     *  merges of subclasses of class dest==class y.)
+     */
+    /* XXX count this case for statistics */
+    if ((dest->typeclass == y->typeclass) && (!dest->merged || !y->merged)) {
+        changed = (dest->merged != NULL);
+        TYPEINFO_FREEMERGED_IF_ANY(dest->merged); /* XXX unify if */
+        dest->merged = NULL;
+        /* XXX remove */ /* log_text("common case handled"); */
+        return changed;
+    }
+    
+    /* XXX remove */ /* log_text("Handling null types"); */
+
+    /* Handle null types: */
+    if (TYPEINFO_IS_NULLTYPE(*y)) {
+        return false;
+    }
+    if (TYPEINFO_IS_NULLTYPE(*dest)) {
+        TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+        TYPEINFO_CLONE(*y,*dest);
+        return true;
+    }
+
+    /* This function uses x internally, so x and y can be swapped
+     * without changing dest. */
+    x = dest;
+    changed = false;
+    
+    /* Handle merging of arrays: */
+    if (TYPEINFO_IS_ARRAY(*x) && TYPEINFO_IS_ARRAY(*y)) {
+        
+        /* XXX remove */ /* log_text("Handling arrays"); */
+
+        /* Make x the one with lesser dimension */
+        if (x->dimension > y->dimension) {
+            tmp = x; x = y; y = tmp;
+        }
+
+        /* If one array (y) has higher dimension than the other,
+         * interpret it as an array (same dim. as x) of Arraystubs. */
+        if (x->dimension < y->dimension) {
+            dimension = x->dimension;
+            elementtype = ARRAYTYPE_OBJECT;
+            elementclass = pseudo_class_Arraystub;
+        }
+        else {
+            dimension = y->dimension;
+            elementtype = y->elementtype;
+            elementclass = y->elementclass;
+        }
+        
+        /* {The arrays are of the same dimension.} */
+        
+        if (x->elementtype != elementtype) {
+            /* Different element types are merged, so the resulting array
+             * type has one accessible dimension less. */
+            if (--dimension == 0) {
+                common = pseudo_class_Arraystub;
+                elementtype = 0;
+                elementclass = NULL;
+            }
+            else {
+                common = class_multiarray_of(dimension,pseudo_class_Arraystub);
+                elementtype = ARRAYTYPE_OBJECT;
+                elementclass = pseudo_class_Arraystub;
+            }
+        }
+        else {
+            /* {The arrays have the same dimension and elementtype.} */
+
+            if (elementtype == ARRAYTYPE_OBJECT) {
+                /* The elements are references, so their respective
+                 * types must be merged.
+                 */
+                changed |= typeinfo_merge_nonarrays(dest,
+                                                    &elementclass,
+                                                    x->elementclass,
+                                                    elementclass,
+                                                    x->merged,y->merged);
+
+                /* XXX otimize this? */
+                /* XXX remove */ /* log_text("finding resulting array class: "); */
+                common = class_multiarray_of(dimension,elementclass);
+                /* XXX remove */ /* utf_display(common->name); printf("\n"); */
+            }
+        }
+    }
+    else {
+        /* {We know that at least one of x or y is no array, so the
+         *  result cannot be an array.} */
+        
+        changed |= typeinfo_merge_nonarrays(dest,
+                                            &common,
+                                            x->typeclass,y->typeclass,
+                                            x->merged,y->merged);
+
+        dimension = 0;
+        elementtype = 0;
+        elementclass = NULL;
+    }
+
+    /* Put the new values into dest if neccessary. */
+
+    if (dest->typeclass != common) {
+        dest->typeclass = common;
+        changed = true;
+    }
+    if (dest->dimension != dimension) {
+        dest->dimension = dimension;
+        changed = true;
+    }
+    if (dest->elementtype != elementtype) {
+        dest->elementtype = elementtype;
+        changed = true;
+    }
+    if (dest->elementclass != elementclass) {
+        dest->elementclass = elementclass;
+        changed = true;
+    }
+
+    /* XXX remove */ /* log_text("returning from merge"); */
+    
+    return changed;
+}
+
+
+/**********************************************************************/
+/* DEBUGGING HELPERS                                                  */
+/**********************************************************************/
+
+#ifdef DEBUG_TYPES
+
+#include "tables.h"
+#include "loader.h"
+
+static int
+typeinfo_test_compare(classinfo **a,classinfo **b)
+{
+    if (*a == *b) return 0;
+    if (*a < *b) return -1;
+    return +1;
+}
+
+static void
+typeinfo_test_parse(typeinfo *info,char *str)
+{
+    int num;
+    int i;
+    typeinfo *infobuf;
+    u1 *typebuf;
+    int returntype;
+    utf *desc = utf_new_char(str);
+    
+    num = typeinfo_count_method_args(desc,false);
+    if (num) {
+        typebuf = DMNEW(u1,num);
+        infobuf = DMNEW(typeinfo,num);
+        
+        typeinfo_init_from_method_args(desc,typebuf,infobuf,num,false,
+                                       &returntype,info);
+
+        TYPEINFO_ALLOCMERGED(info->merged,num);
+        info->merged->count = num;
+
+        for (i=0; i<num; ++i) {
+            if (typebuf[i] != TYPE_ADDRESS)
+                panic("non-reference type in mergedlist");
+            info->merged->list[i] = infobuf[i].typeclass;
+        }
+        qsort(info->merged->list,num,sizeof(classinfo*),
+              (int(*)(const void *,const void *))&typeinfo_test_compare);
+    }
+    else {
+        typeinfo_init_from_method_args(desc,NULL,NULL,0,false,
+                                       &returntype,info);
+    }
+}
+
+#define TYPEINFO_TEST_BUFLEN  4000
+
+static bool
+typeinfo_equal(typeinfo *x,typeinfo *y)
+{
+    int i;
+    
+    if (x->typeclass != y->typeclass) return false;
+    if (x->dimension != y->dimension) return false;
+    if (x->dimension) {
+        if (x->elementclass != y->elementclass) return false;
+        if (x->elementtype != y->elementtype) return false;
+    }
+    if (x->merged || y->merged) {
+        if (!(x->merged && y->merged)) return false;
+        if (x->merged->count != y->merged->count) return false;
+        for (i=0; i<x->merged->count; ++i)
+            if (x->merged->list[i] != y->merged->list[i])
+                return false;
+    }
+    return true;
+}
+
+static void
+typeinfo_testmerge(typeinfo *a,typeinfo *b,typeinfo *result,int *failed)
+{
+    typeinfo dest;
+
+    TYPEINFO_CLONE(*a,dest);
+    
+    printf("\n          ");
+    typeinfo_print_short(stdout,&dest);
+    printf("\n          ");
+    typeinfo_print_short(stdout,b);
+    printf("\n");
+
+    typeinfo_merge(&dest,b);
+
+    if (typeinfo_equal(&dest,result)) {
+        printf("OK        ");
+        typeinfo_print_short(stdout,&dest);
+        printf("\n");
+    }
+    else {
+        printf("RESULT    ");
+        typeinfo_print_short(stdout,&dest);
+        printf("\n");
+        printf("SHOULD BE ");
+        typeinfo_print_short(stdout,result);
+        printf("\n");
+        (*failed)++;
+    }
+}
+
+static void
+typeinfo_inc_dimension(typeinfo *info)
+{
+    if (info->dimension++ == 0) {
+        info->elementtype = ARRAYTYPE_OBJECT;
+        info->elementclass = info->typeclass;
+    }
+    info->typeclass = class_array_of(info->typeclass);
+}
+
+#define TYPEINFO_TEST_MAXDIM  10
+
+static void
+typeinfo_testrun(char *filename)
+{
+    char buf[TYPEINFO_TEST_BUFLEN];
+    char bufa[TYPEINFO_TEST_BUFLEN];
+    char bufb[TYPEINFO_TEST_BUFLEN];
+    char bufc[TYPEINFO_TEST_BUFLEN];
+    typeinfo a,b,c,a2,b2;
+    int maxdim;
+    int failed = 0;
+    FILE *file = fopen(filename,"rt");
+    
+    if (!file)
+        panic("could not open typeinfo test file");
+
+    while (fgets(buf,TYPEINFO_TEST_BUFLEN,file)) {
+        if (buf[0] == '#' || !strlen(buf))
+            continue;
+        
+        int res = sscanf(buf,"%s\t%s\t%s\n",bufa,bufb,bufc);
+        if (res != 3 || !strlen(bufa) || !strlen(bufb) || !strlen(bufc))
+            panic("Invalid line in typeinfo test file (none of empty, comment or test)");
+
+        typeinfo_test_parse(&a,bufa);
+        typeinfo_test_parse(&b,bufb);
+        typeinfo_test_parse(&c,bufc);
+        do {
+            typeinfo_testmerge(&a,&b,&c,&failed); /* check result */
+            typeinfo_testmerge(&b,&a,&c,&failed); /* check commutativity */
+
+            if (TYPEINFO_IS_NULLTYPE(a)) break;
+            if (TYPEINFO_IS_NULLTYPE(b)) break;
+            if (TYPEINFO_IS_NULLTYPE(c)) break;
+            
+            maxdim = a.dimension;
+            if (b.dimension > maxdim) maxdim = b.dimension;
+            if (c.dimension > maxdim) maxdim = c.dimension;
+
+            if (maxdim < TYPEINFO_TEST_MAXDIM) {
+                typeinfo_inc_dimension(&a);
+                typeinfo_inc_dimension(&b);
+                typeinfo_inc_dimension(&c);
+            }
+        } while (maxdim < TYPEINFO_TEST_MAXDIM);
+    }
+
+    fclose(file);
+
+    if (failed) {
+        fprintf(stderr,"Failed typeinfo_merge tests: %d\n",failed);
+        panic("Failed test");
+    }
+}
+
+void
+typeinfo_test()
+{
+/*     typeinfo i1,i2,i3,i4,i5,i6,i7,i8; */
+        
+/*     typeinfo_init_from_fielddescriptor(&i1,"[Ljava/lang/Integer;"); */
+/*     typeinfo_init_from_fielddescriptor(&i2,"[[Ljava/lang/String;"); */
+/*     typeinfo_init_from_fielddescriptor(&i3,"[Ljava/lang/Cloneable;"); */
+/*     typeinfo_init_from_fielddescriptor(&i3,"[[Ljava/lang/String;"); */
+/*     TYPEINFO_INIT_NULLTYPE(i1); */
+/*     typeinfo_print_short(stdout,&i1); printf("\n"); */
+/*     typeinfo_print_short(stdout,&i2); printf("\n"); */
+/*     typeinfo_merge(&i1,&i2); */
+/*     typeinfo_print_short(stdout,&i1); printf("\n"); */
+/*     typeinfo_print_short(stdout,&i3); printf("\n"); */
+/*     typeinfo_merge(&i1,&i3); */
+/*     typeinfo_print_short(stdout,&i1); printf("\n"); */
+
+    log_text("Running typeinfo test file...");
+    typeinfo_testrun("typeinfo.tst");
+    log_text("Finished typeinfo test file.");
+}
+
+void
+typeinfo_init_from_fielddescriptor(typeinfo *info,char *desc)
+{
+    typeinfo_init_from_descriptor(info,desc,desc+strlen(desc));
+}
+
+#define TYPEINFO_MAXINDENT  80
+
+void
+typeinfo_print(FILE *file,typeinfo *info,int indent)
+{
+    int i;
+    char ind[TYPEINFO_MAXINDENT + 1];
+
+    if (indent > TYPEINFO_MAXINDENT) indent = TYPEINFO_MAXINDENT;
+    
+    for (i=0; i<indent; ++i)
+        ind[i] = ' ';
+    ind[i] = (char) 0;
+    
+    if (TYPEINFO_IS_PRIMITIVE(*info)) {
+        fprintf(file,"%sprimitive\n",ind);
+        return;
+    }
+    
+    if (TYPEINFO_IS_NULLTYPE(*info)) {
+        fprintf(file,"%snull\n",ind);
+        return;
+    }
+    
+    fprintf(file,"%sClass:      ",ind);
+    utf_fprint(file,info->typeclass->name);
+    fprintf(file,"\n");
+
+    if (TYPEINFO_IS_ARRAY(*info)) {
+        fprintf(file,"%sDimension:    %d",ind,(int)info->dimension);
+        fprintf(file,"\n%sElements:     ",ind);
+        switch (info->elementtype) {
+          case ARRAYTYPE_INT     : fprintf(file,"int\n"); break;
+          case ARRAYTYPE_LONG    : fprintf(file,"long\n"); break;
+          case ARRAYTYPE_FLOAT   : fprintf(file,"float\n"); break;
+          case ARRAYTYPE_DOUBLE  : fprintf(file,"double\n"); break;
+          case ARRAYTYPE_BYTE    : fprintf(file,"byte\n"); break;
+          case ARRAYTYPE_CHAR    : fprintf(file,"char\n"); break;
+          case ARRAYTYPE_SHORT   : fprintf(file,"short\n"); break;
+          case ARRAYTYPE_BOOLEAN : fprintf(file,"boolean\n"); break;
+              
+          case ARRAYTYPE_OBJECT:
+              fprintf(file,"reference: ");
+              utf_fprint(file,info->elementclass->name);
+              fprintf(file,"\n");
+              break;
+              
+          default:
+              fprintf(file,"INVALID ARRAYTYPE!\n");
+        }
+    }
+
+    if (info->merged) {
+        fprintf(file,"%sMerged: ",ind);
+        for (i=0; i<info->merged->count; ++i) {
+            if (i) fprintf(file,", ");
+            utf_fprint(file,info->merged->list[i]->name);
+        }
+        fprintf(file,"\n");
+    }
+}
+
+void
+typeinfo_print_short(FILE *file,typeinfo *info)
+{
+    int i;
+
+    if (TYPEINFO_IS_PRIMITIVE(*info)) {
+        fprintf(file,"primitive");
+        return;
+    }
+    
+    if (TYPEINFO_IS_NULLTYPE(*info)) {
+        fprintf(file,"null");
+        return;
+    }
+    
+    utf_fprint(file,info->typeclass->name);
+
+    /* XXX remove */
+#if 0
+    if (TYPEINFO_IS_ARRAY(*info)) {
+        fprintf(file,"[%d]",info->dimension);
+        switch (info->elementtype) {
+            case ARRAYTYPE_INT     : fprintf(file,"int"); break;
+            case ARRAYTYPE_LONG    : fprintf(file,"long"); break;
+            case ARRAYTYPE_FLOAT   : fprintf(file,"float"); break;
+            case ARRAYTYPE_DOUBLE  : fprintf(file,"double"); break;
+            case ARRAYTYPE_BYTE    : fprintf(file,"byte"); break;
+            case ARRAYTYPE_CHAR    : fprintf(file,"char"); break;
+            case ARRAYTYPE_SHORT   : fprintf(file,"short"); break;
+            case ARRAYTYPE_BOOLEAN : fprintf(file,"boolean"); break;
+
+            case ARRAYTYPE_OBJECT:
+                fprintf(file,"object(");
+                utf_fprint(file,info->elementclass->name);
+                fprintf(file,")");
+                break;
+                
+            default:
+                fprintf(file,"INVALID ARRAYTYPE!");
+        }
+    }
+#endif
+    
+    if (info->merged) {
+        fprintf(file,"{");
+        for (i=0; i<info->merged->count; ++i) {
+            if (i) fprintf(file,",");
+            utf_fprint(file,info->merged->list[i]->name);
+        }
+        fprintf(file,"}");
+    }
+}
+
+void
+typeinfo_print_type(FILE *file,int type,typeinfo *info)
+{
+    switch (type) {
+      case TYPE_VOID:   fprintf(file,"V"); break;
+      case TYPE_INT:    fprintf(file,"I"); break;
+      case TYPE_FLOAT:  fprintf(file,"F"); break;
+      case TYPE_DOUBLE: fprintf(file,"D"); break;
+      case TYPE_LONG:   fprintf(file,"J"); break;
+      case TYPE_ADDRESS:
+          if (TYPEINFO_IS_PRIMITIVE(*info))
+              fprintf(file,"R"); /* returnAddress */
+          else {
+              fprintf(file,"L");
+              typeinfo_print_short(file,info);
+              fprintf(file,";");
+          }
+          break;
+          
+      default:
+          fprintf(file,"!");
+    }
+}
+
+#endif // DEBUG_TYPES
diff --git a/typeinfo.h b/typeinfo.h
new file mode 100644 (file)
index 0000000..06c6828
--- /dev/null
@@ -0,0 +1,379 @@
+/********************************* typeinfo.h **********************************
+
+       Copyright (c) 2003 ? XXX
+
+       See file COPYRIGHT for information on usage and disclaimer of warranties
+
+       defininitions for the compiler's type system
+       
+       Authors: Edwin Steiner
+                  
+       Last Change:
+
+*******************************************************************************/
+
+#ifndef __typeinfo_h_
+#define __typeinfo_h_
+
+/* #define DEBUG_TYPES */ /* XXX */
+
+#include "global.h"
+
+/* resolve typedef cycles *****************************************************/
+
+typedef struct typeinfo typeinfo;
+typedef struct typeinfo_mergedlist typeinfo_mergedlist;
+
+/* global variables ***********************************************************/
+
+/* XXX move this documentation to global.h */
+/* The following classinfo pointers are used internally by the type system.
+ * Please do not use them directly, use the TYPEINFO_ macros instead.
+ */
+
+/*
+ * pseudo_class_Arraystub
+ *     (extends Object implements Cloneable, java.io.Serializable)
+ *
+ *     If two arrays of incompatible component types are merged,
+ *     the resulting reference has no accessible components.
+ *     The result does, however, implement the interfaces Cloneable
+ *     and java.io.Serializable. This pseudo class is used internally
+ *     to represent such results. (They are *not* considered arrays!)
+ *
+ * pseudo_class_Array                          XXX delete?
+ *     (extends pseudo_class_Arraystub)
+ *
+ *     This pseudo class is used internally as the class of all arrays
+ *     to distinguish them from arbitrary Objects.
+ *
+ * pseudo_class_Null
+ *
+ *     This pseudo class is used internally to represent the
+ *     null type.
+ */
+
+/* data structures for the type system ****************************************/
+
+/* The typeinfo structure stores detailed information on reference types.
+ * (stack elements, variables, etc. with type == TYPE_ADR.)
+ * XXX: exclude ReturnAddresses?
+ *
+ * For primitive types either there is no typeinfo allocated or the
+ * typeclass pointer in the typeinfo struct is NULL.
+ *
+ * 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
+ *
+ *        In this case the other fields of the structure
+ *        are INVALID.
+ *
+ * B) typeclass == pseudo_class_Null
+ *
+ *        XXX
+ *
+ * C) typeclass is an array class
+ *
+ *        XXX
+ *
+ * D) typeclass == pseudo_class_Arraystub
+ *
+ *        XXX
+ *
+ * E) typeclass is an interface
+ *
+ *        XXX
+ *
+ * F) typeclass is a (non-pseudo-)class != java.lang.Object
+ *
+ *        XXX
+ *        All classinfos in u.merged.list (if any) are
+ *        subclasses of typeclass.
+ *
+ * G) typeclass is java.lang.Object
+ *
+ *        XXX
+ *        In this case u.merged.count and u.merged.list
+ *        are valid and may be non-zero.
+ *        The classinfos in u.merged.list (if any) can be
+ *        classes, interfaces or pseudo classes.
+ */
+
+/* The following algorithm is used to determine if the type described
+ * by this typeinfo struct supports the interface X:
+ *
+ *     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) XXX 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 {
+        classinfo           *typeclass;
+        classinfo           *elementclass; /* valid if dimension>0 */
+        typeinfo_mergedlist *merged;
+        u1                   dimension;
+        u1                   elementtype;  /* valid if dimension>0 */
+};
+
+struct typeinfo_mergedlist {
+        s4         count;
+        classinfo *list[1]; /* variable length! */
+};
+
+/****************************************************************************/
+/* MACROS                                                                   */
+/****************************************************************************/
+
+/* NOTE: These macros take typeinfo *structs* not pointers as arguments.
+ *       You have to dereference any pointers.
+ */
+
+/* internally used macros ***************************************************/
+
+/* internal, don't use this explicitly! */
+/* XXX change to GC? */
+#define TYPEINFO_ALLOCMERGED(mergedlist,count)                  \
+            {(mergedlist) = (typeinfo_mergedlist*)dump_alloc(   \
+                sizeof(typeinfo_mergedlist)                     \
+                + ((count)-1)*sizeof(classinfo*));}
+
+/* internal, don't use this explicitly! */
+/* XXX change to GC? */
+#if 0
+#define TYPEINFO_FREEMERGED(mergedlist)                         \
+            {mem_free((mergedlist),sizeof(typeinfo_mergedlist)  \
+                + ((mergedlist)->count - 1)*sizeof(classinfo*));}
+#endif
+#define TYPEINFO_FREEMERGED(mergedlist)
+
+/* internal, don't use this explicitly! */
+#define TYPEINFO_FREEMERGED_IF_ANY(mergedlist)                  \
+            {if (mergedlist) TYPEINFO_FREEMERGED(mergedlist);}
+
+/* macros for type queries **************************************************/
+
+#define TYPEINFO_IS_PRIMITIVE(info)                             \
+            ((info).typeclass == NULL)
+
+#define TYPEINFO_IS_REFERENCE(info)                             \
+            ((info).typeclass != NULL)
+
+#define TYPEINFO_IS_NULLTYPE(info)                              \
+            ((info).typeclass == pseudo_class_Null)
+
+/* 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 != NULL) )
+
+/* assumes that info describes an array type */
+#define TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info)                 \
+            ( ((info).elementclass != NULL)                     \
+              || ((info).dimension >= 2) )
+
+#define TYPEINFO_IS_ARRAY_OF_REFS(info)                         \
+            ( TYPEINFO_IS_ARRAY(info)                           \
+              && TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info) )
+
+/* macros for initializing typeinfo structures ******************************/
+
+#define TYPEINFO_INIT_PRIMITIVE(info)                           \
+            {(info).typeclass = NULL;                           \
+             (info).elementclass = NULL;                        \
+             (info).merged = NULL;                              \
+             (info).dimension = 0;                              \
+             (info).elementtype = 0;}
+
+#define TYPEINFO_INIT_NON_ARRAY_CLASSINFO(info,cinfo)   \
+            {(info).typeclass = (cinfo);                \
+             (info).elementclass = NULL;                \
+             (info).merged = NULL;                      \
+             (info).dimension = 0;                      \
+             (info).elementtype = 0;}
+
+#define TYPEINFO_INIT_NULLTYPE(info)                            \
+            TYPEINFO_INIT_CLASSINFO(info,pseudo_class_Null)
+
+/* XXX delete */
+#if 0
+#define TYPEINFO_INIT_ARRAY(info,dim,elmtype,elmcinfo)        \
+            {(info).typeclass = pseudo_class_Array;             \
+             (info).elementclass = (elmcinfo);                  \
+             (info).merged = NULL;                              \
+             (info).dimension = (dim);                          \
+             (info).elementtype = (elmtype);}
+#endif
+
+/* XXX delete? */
+#define TYPEINFO_INIT_ARRAY(info,cls)                                   \
+            {(info).typeclass = cls;                                    \
+             if (cls->vftbl->arraydesc->elementvftbl)                   \
+                 (info).elementclass = cls->vftbl->arraydesc->elementvftbl->class; \
+             else                                                       \
+                 (info).elementclass = NULL;                            \
+             (info).merged = NULL;                                      \
+             (info).dimension = cls->vftbl->arraydesc->dimension;       \
+             (info).elementtype = cls->vftbl->arraydesc->elementtype;}
+
+#define TYPEINFO_INIT_CLASSINFO(info,cls)                               \
+        {if (((info).typeclass = cls)->vftbl->arraydesc) {              \
+                if (cls->vftbl->arraydesc->elementvftbl)                \
+                    (info).elementclass = cls->vftbl->arraydesc->elementvftbl->class; \
+                else                                                    \
+                    (info).elementclass = NULL;                         \
+                (info).dimension = cls->vftbl->arraydesc->dimension;    \
+                (info).elementtype = cls->vftbl->arraydesc->elementtype;\
+            }                                                           \
+            else {                                                      \
+                (info).elementclass = NULL;                             \
+                (info).dimension = 0;                                   \
+                (info).elementtype = 0;                                 \
+            }                                                           \
+            (info).merged = NULL;}
+
+/* XXX */
+#define TYPEINFO_INC_DIMENSION(info)                            \
+             (info).dimension++
+
+#define TYPEINFO_INIT_FROM_FIELDINFO(info,fi)                   \
+            typeinfo_init_from_descriptor(&(info),              \
+                (fi)->descriptor->text,utf_end((fi)->descriptor));
+
+/* macros for freeing typeinfo structures ***********************************/
+
+#define TYPEINFO_FREE(info)                                     \
+            {TYPEINFO_FREEMERGED_IF_ANY((info).merged);         \
+             (info).merged = NULL;}
+
+/* macros for writing types (destination must have been initialized) ********/
+/* XXX delete them? */
+
+#define TYPEINFO_PUT_NULLTYPE(info)                             \
+            {(info).typeclass = pseudo_class_Null;}
+
+#define TYPEINFO_PUT_NON_ARRAY_CLASSINFO(info,cinfo)            \
+            {(info).typeclass = (cinfo);}
+
+/* XXX delete */
+#if 0
+#define TYPEINFO_PUT_ARRAY(info,dim,elmtype)                    \
+            {(info).typeclass = pseudo_class_Array;             \
+             (info).dimension = (dim);                          \
+             (info).elementtype = (elmtype);}
+#endif
+
+/* XXX delete? */
+#define TYPEINFO_PUT_ARRAY(info,cls)                                    \
+            {(info).typeclass = cls;                                    \
+             if (cls->vftbl->arraydesc->elementvftbl)                \
+                 (info).elementclass = cls->vftbl->arraydesc->elementvftbl->class; \
+             (info).dimension = cls->vftbl->arraydesc->dimension;       \
+             (info).elementtype = cls->vftbl->arraydesc->elementtype;}
+
+#define TYPEINFO_PUT_CLASSINFO(info,cls)                                \
+        {if (((info).typeclass = cls)->vftbl->arraydesc) {              \
+                if (cls->vftbl->arraydesc->elementvftbl)                \
+                    (info).elementclass = cls->vftbl->arraydesc->elementvftbl->class; \
+                (info).dimension = cls->vftbl->arraydesc->dimension;    \
+                (info).elementtype = cls->vftbl->arraydesc->elementtype; \
+            }}
+
+/* XXX delete */
+#if 0
+#define TYPEINFO_PUT_OBJECT_ARRAY(info,dim,elmcinfo)            \
+            {TYPEINFO_PUT_ARRAY(info,dim,ARRAYTYPE_OBJECT);     \
+             (info).elementclass = (elmcinfo);}
+#endif
+
+/* srcarray must be an array (not checked) */
+#define TYPEINFO_PUT_COMPONENT(srcarray,dst)                    \
+            {typeinfo_put_component(&(srcarray),&(dst));}
+
+/* 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)                                  \
+            {(dst) = (src);}
+
+/* TYPEINFO_CLONE makes a deep copy, the merged list (if any) is duplicated
+ * into a newly allocated array.
+ */
+#define TYPEINFO_CLONE(src,dst)                                 \
+            {(dst) = (src);                                     \
+             if ((dst).merged) typeinfo_clone(&(src),&(dst));}
+
+/****************************************************************************/
+/* FUNCTIONS                                                                */
+/****************************************************************************/
+
+/* inquiry functions (read-only) ********************************************/
+
+bool typeinfo_is_array(typeinfo *info);
+bool typeinfo_is_primitive_array(typeinfo *info,int arraytype);
+bool typeinfo_is_array_of_refs(typeinfo *info);
+
+bool typeinfo_implements_interface(typeinfo *info,classinfo *interf);
+bool typeinfo_is_assignable(typeinfo *value,typeinfo *dest);
+
+/* initialization functions *************************************************/
+
+void typeinfo_init_from_descriptor(typeinfo *info,char *utf_ptr,char *end_ptr);
+void typeinfo_init_component(typeinfo *srcarray,typeinfo *dst);
+
+int  typeinfo_count_method_args(utf *d,bool twoword); /* this not included */
+void typeinfo_init_from_method_args(utf *desc,u1 *typebuf,
+                                    typeinfo *infobuf,
+                                    int buflen,bool twoword,
+                                    int *returntype,typeinfo *returntypeinfo);
+
+void typeinfo_clone(typeinfo *src,typeinfo *dest);
+
+/* functions for the type system ********************************************/
+
+void typeinfo_free(typeinfo *info);
+
+bool typeinfo_merge(typeinfo *dest,typeinfo* y);
+
+/* debugging helpers ********************************************************/
+
+#ifdef DEBUG_TYPES
+
+void typeinfo_test();
+void typeinfo_init_from_fielddescriptor(typeinfo *info,char *desc);
+void typeinfo_print(FILE *file,typeinfo *info,int indent);
+void typeinfo_print_short(FILE *file,typeinfo *info);
+void typeinfo_print_type(FILE *file,int type,typeinfo *info);
+
+#endif // DEBUG_TYPES
+
+#endif
diff --git a/typeinfo.tst b/typeinfo.tst
new file mode 100644 (file)
index 0000000..4dc5232
--- /dev/null
@@ -0,0 +1,70 @@
+# Input for testing the typeinfo_ functions
+#
+# simple merges (result is common ancestor)
+#
+()Ljava/lang/Object;           ()Ljava/lang/Object;            ()Ljava/lang/Object;
+()Ljava/lang/Object;           ()Ljava/lang/String;            ()Ljava/lang/Object;
+()Ljava/lang/Number;           ()Ljava/lang/Integer;           ()Ljava/lang/Number;
+()Ljava/lang/Object;           ()Ljava/lang/Integer;           ()Ljava/lang/Object;
+()Ljava/lang/Object;           ()Ljava/lang/Cloneable;         ()Ljava/lang/Object;
+()Ljava/lang/Cloneable;                ()Ljava/lang/Cloneable;         ()Ljava/lang/Cloneable;
+()Ljava/lang/Cloneable;                ()[Ljava/lang/Object;           ()Ljava/lang/Cloneable;
+()Ljava/lang/Cloneable;                ()[I                            ()Ljava/lang/Cloneable;
+()Ljava/io/Serializable;       ()[Ljava/lang/Object;           ()Ljava/io/Serializable;
+()Ljava/io/Serializable;       ()[D                            ()Ljava/io/Serializable;
+()Ljava/lang/Object;           ()[Ljava/lang/Object;           ()Ljava/lang/Object;
+()Ljava/lang/Object;           ()[I                            ()Ljava/lang/Object;
+()L$ARRAYSTUB$;                        ()L$ARRAYSTUB$;                 ()L$ARRAYSTUB$;
+()L$ARRAYSTUB$;                ()Ljava/lang/Object;            ()Ljava/lang/Object;
+()L$ARRAYSTUB$;                ()Ljava/lang/Cloneable;         ()Ljava/lang/Cloneable;
+()L$ARRAYSTUB$;                ()Ljava/io/Serializable;        ()Ljava/io/Serializable;
+()L$NULL$;                     ()L$NULL$;                      ()L$NULL$;
+()L$NULL$;                     ()[Ljava/lang/Object;           ()[Ljava/lang/Object;
+#
+#
+# merges where the result is a mergedlist
+#
+()Ljava/lang/String;   ()Ljava/lang/Number;    (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;
+#
+#
+# merges with one mergedlist where the resulting mergedlist is unchanged
+#
+(Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;       ()Ljava/lang/String;    (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;
+(Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;       ()Ljava/lang/Number;    (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Integer;   (Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Long;      (Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()L$NULL$;              (Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;
+#
+#
+# merges with one mergedlist where the result has no mergedlist
+#
+(Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;       ()Ljava/lang/Object;    ()Ljava/lang/Object;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Number;    ()Ljava/lang/Number;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Object;    ()Ljava/lang/Object;
+#
+#
+# one mergedlist where the resulting mergeslist is longer
+#
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                ()Ljava/lang/Double;    (Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/Double;)Ljava/lang/Number;
+#
+#
+# merges with two mergedlists with the same typeclass
+#
+(Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;       (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;        (Ljava/lang/String;Ljava/lang/Number;)Ljava/lang/Object;
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                (Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/Float;)Ljava/lang/Number;        (Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/Float;)Ljava/lang/Number;
+#
+#
+# merges with two mergedlists with different typeclass
+#
+(Ljava/lang/Integer;Ljava/lang/Long;)Ljava/lang/Number;                (Ljava/lang/Integer;Ljava/lang/String;)Ljava/lang/Object;       (Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/String;)Ljava/lang/Object;
+#
+#
+# merging primitive arrays
+#
+()[I   ()[I    ()[I
+()[I   ()[F    ()L$ARRAYSTUB$;
+#
+#
+# merging reference arrays with primitive arrays
+#
+()[I   ()[Ljava/lang/String;   ()L$ARRAYSTUB$;