* src/threads/posix/lock.c: Moved to .cpp.
[cacao.git] / src / vm / classcache.c
index c5563ccf0f9676cff958a6f23def9a277e8642bb..6324f2a78787e0c012d30eab9b0109a0109481ca 100644 (file)
@@ -1,9 +1,7 @@
 /* src/vm/classcache.c - loaded class cache and loading constraints
 
-   Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates,
-   R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
-   C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
-   Institut f. Computersprachen - TU Wien
+   Copyright (C) 1996-2005, 2006, 2007, 2008
+   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
 
    This file is part of CACAO.
 
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.
-
-   Contact: cacao@complang.tuwien.ac.at
-
-   Authors: Edwin Steiner
-
-   Changes: Christian Thalinger
-
-   $Id: classcache.c 3888 2005-12-05 22:08:45Z twisti $
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
 
 */
 
 
+#include "config.h"
+
 #include <assert.h>
 
-#include "config.h"
 #include "vm/types.h"
 
 #include "mm/memory.h"
+
+#include "threads/lock.hpp"
+#include "threads/mutex.hpp"
+
+#include "toolbox/hashtable.h"
+#include "toolbox/logging.h"
+
 #include "vm/classcache.h"
-#include "vm/exceptions.h"
-#include "vm/hashtable.h"
-#include "vm/stringlocal.h"
+#include "vm/exceptions.hpp"
+#include "vm/options.h"
 #include "vm/utf8.h"
 
 
+/*************************************************************************
+
+  Class Cache
+
+  The classcache has two functions:
+  
+       1) caching the resolution of class references
+       2) storing and checking loading constraints
+
+  We will use the following terms in this description:
+
+       N          a class name: a utf string
+       (N,L)      a class reference with initiating loader L and class name N
+       C          a class (object): the result of resolving a reference (N,L)
+               We will write resultion as
+                               C = *(N,L)
+       (N,L1,L2)  a loading constraint indicating that (N,L1) and (N,L2) must
+                  resolve to the same class C. So (N,L1,L2) means
+                               *(N,L1) = *(N,L2)
+
+  The functions of the classcache require:
+
+    1) a mapping (N,L) |--> C for looking up prior resolution results.
+       2) storing the current set of loading constraints { (N,L1,L2) }
+
+  These functions can be rearranged like that:
+
+    a mapping N |--> (a mapping L |--> C or NULL, 
+                         a set of constraints {(L1,L2)})
+
+  Thus we can treat the mapping and constraints for each name N
+  separately. The implementation does this by keeping a hash table
+  mapping a name N to a `classcache_name_entry` which contains all
+  info with respect to N.
+
+  For a class name N we can define an equivalence relation ~N~ on
+  class loaders:
+
+       L1 ~N~ L2  <==>  *(N,L1) = *(N,L2)
+
+  A loading constraint (N,L1,L2) implies L1 ~N~ L2.
+
+  Also, if two references (N,L1) and (N,L2) resolve to the same class C
+  we have L1 ~N~ L2 because class loaders are required to return
+  consistent resolutions for a name N [XXX].
+
+  A `classcache_name_entry` keeps a set of tuples { (Cx,IL,CL) },
+  where
+               Cx...is a class C or NULL
+               IL...is the set of initiating loaders
+               CL...is the set of constrained loaders
+               
+  Such a tuple is called `classcache_class_entry` in the source code.
+
+  The following holds for each tuple (Cx,IL,CL):
+
+    .  (Cx is NULL) implies IL = {}.
+          
+       .  If Cx is a class, IL is the set of loaders that have been
+          recorded as initiating loaders for Cx. IL may be the
+          empty set {} in case Cx has already been defined but no
+          initiating loader has been recorded, yet.
+  
+    .  (IL u CL) is a subset of an equivalence class of ~N~.
+
+                (This means that all loaders in IL and CL must resolve
+                the name N to the same class.)
+
+  The following holds for the set of tuples { (Cx,IL,CL) }:
+
+    .  For a given class C there is at most one tuple with Cx = C
+          in the set. (There may be an arbitrary number of tuples
+          with Cx = NULL, however.)
+
+       .  For a given loader L there is at most one tuple with
+          L in (IL u CL).
+
+  The implementation stores sets of loaders as linked lists of
+  `classcache_loader_entry`s.
+
+  Comments about manipulating the classcache can be found in the
+  individual functions below.
+*************************************************************************/
+
+
 /* initial number of slots in the classcache hash table */
 #define CLASSCACHE_INIT_SIZE  2048
 
 /* DEBUG HELPERS                                                              */
 /*============================================================================*/
 
-/*#define CLASSCACHE_VERBOSE*/
+/* #define CLASSCACHE_VERBOSE */
+
+/*============================================================================*/
+/* STATISTICS                                                                 */
+/*============================================================================*/
+
+/*#define CLASSCACHE_STATS*/
+
+#ifdef CLASSCACHE_STATS
+static int stat_classnames_stored = 0;
+static int stat_classes_stored = 0;
+static int stat_trivial_constraints = 0;
+static int stat_nontriv_constraints = 0;
+static int stat_nontriv_constraints_both = 0;
+static int stat_nontriv_constraints_merged = 0;
+static int stat_nontriv_constraints_one = 0;
+static int stat_nontriv_constraints_none = 0;
+static int stat_new_loader_entry = 0;
+static int stat_merge_class_entries = 0;
+static int stat_merge_loader_entries = 0;
+static int stat_lookup = 0;
+static int stat_lookup_class_entry_checked = 0;
+static int stat_lookup_loader_checked = 0;
+static int stat_lookup_name = 0;
+static int stat_lookup_name_entry = 0;
+static int stat_lookup_name_notfound = 0;
+static int stat_lookup_new_name = 0;
+static int stat_lookup_new_name_entry = 0;
+static int stat_lookup_new_name_collisions = 0;
+static int stat_rehash_names = 0;
+static int stat_rehash_names_collisions = 0;
+
+#define CLASSCACHE_COUNT(cnt)  (cnt)++
+#define CLASSCACHE_COUNTIF(cond,cnt)  do{if(cond) (cnt)++;} while(0)
+
+void classcache_print_statistics(FILE *file) {
+       fprintf(file,"classnames stored   : %8d\n",stat_classnames_stored);
+       fprintf(file,"classes stored      : %8d\n",stat_classes_stored);
+       fprintf(file,"trivial constraints : %8d\n",stat_trivial_constraints);
+       fprintf(file,"non-triv constraints: %8d\n",stat_nontriv_constraints);
+       fprintf(file,"   both loaders rec.: %8d\n",stat_nontriv_constraints_both);
+       fprintf(file,"       merged       : %8d\n",stat_nontriv_constraints_merged);
+       fprintf(file,"   one loader rec.  : %8d\n",stat_nontriv_constraints_one);
+       fprintf(file,"   no loaders rec.  : %8d\n",stat_nontriv_constraints_none);
+       fprintf(file,"new loader entries  : %8d\n",stat_new_loader_entry);
+       fprintf(file,"merge class entries : %8d\n",stat_merge_class_entries);
+       fprintf(file,"merge loader entries: %8d\n",stat_merge_loader_entries);
+       fprintf(file,"lookups             : %8d\n",stat_lookup);
+       fprintf(file,"   class entries ckd: %8d\n",stat_lookup_class_entry_checked);
+       fprintf(file,"   loader checked   : %8d\n",stat_lookup_loader_checked);
+       fprintf(file,"lookup name         : %8d\n",stat_lookup_name);
+       fprintf(file,"   entries checked  : %8d\n",stat_lookup_name_entry);
+       fprintf(file,"   not found        : %8d\n",stat_lookup_name_notfound);
+       fprintf(file,"lookup (new) name   : %8d\n",stat_lookup_new_name);
+       fprintf(file,"   entries checked  : %8d\n",stat_lookup_new_name_entry);
+       fprintf(file,"   new collisions   : %8d\n",stat_lookup_new_name_collisions);
+       fprintf(file,"names rehashed      : %8d times\n",stat_rehash_names);
+       fprintf(file,"    collisions      : %8d\n",stat_rehash_names_collisions);
+}
+#else
+#define CLASSCACHE_COUNT(cnt)
+#define CLASSCACHE_COUNTIF(cond,cnt)
+#endif
 
 /*============================================================================*/
 /* THREAD-SAFE LOCKING                                                        */
        /*          NOT synchronized!              */
        /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
 
-#if defined(USE_THREADS)
-# define CLASSCACHE_LOCK()      builtin_monitorenter(lock_hashtable_classcache)
-# define CLASSCACHE_UNLOCK()    builtin_monitorexit(lock_hashtable_classcache)
+#if defined(ENABLE_THREADS)
+# define CLASSCACHE_LOCK()      Mutex_lock(classcache_hashtable_mutex)
+# define CLASSCACHE_UNLOCK()    Mutex_unlock(classcache_hashtable_mutex)
 #else
 # define CLASSCACHE_LOCK()
 # define CLASSCACHE_UNLOCK()
 
 hashtable hashtable_classcache;
 
-#if defined(USE_THREADS)
-static java_objectheader *lock_hashtable_classcache;
+#if defined(ENABLE_THREADS)
+static Mutex *classcache_hashtable_mutex;
 #endif
 
 
@@ -87,9 +232,19 @@ static java_objectheader *lock_hashtable_classcache;
 /*                                                                            */
 /*============================================================================*/
 
+/* prototypes */
+
+static void classcache_free_class_entry(classcache_class_entry *clsen);
+static void classcache_remove_class_entry(classcache_name_entry *en,
+                                                                                 classcache_class_entry *clsen);
+
+/* hash function to use */
+
+#define CLASSCACHE_HASH utf_full_hashkey
+
 /* classcache_init *************************************************************
  
-   Initialize the loaded class cache
+   Initialize the class cache
 
    Note: NOT synchronized!
   
@@ -97,18 +252,16 @@ static java_objectheader *lock_hashtable_classcache;
 
 bool classcache_init(void)
 {
+       TRACESUBSYSTEMINITIALIZATION("classcache_init");
+
        /* create the hashtable */
 
        hashtable_create(&hashtable_classcache, CLASSCACHE_INIT_SIZE);
 
-#if defined(USE_THREADS)
-       /* create utf hashtable lock object */
+#if defined(ENABLE_THREADS)
+       /* create utf hashtable mutex */
 
-       lock_hashtable_classcache = NEW(java_objectheader);
-
-# if defined(NATIVE_THREADS)
-       initObjectLock(lock_hashtable_classcache);
-# endif
+       classcache_hashtable_mutex = Mutex_new();
 #endif
 
        /* everything's ok */
@@ -131,7 +284,7 @@ bool classcache_init(void)
 *******************************************************************************/
 
 static classcache_loader_entry * classcache_new_loader_entry(
-                                                                       classloader * loader,
+                                                                       classloader_t * loader,
                                                                        classcache_loader_entry * next)
 {
        classcache_loader_entry *lden;
@@ -139,6 +292,7 @@ static classcache_loader_entry * classcache_new_loader_entry(
        lden = NEW(classcache_loader_entry);
        lden->loader = loader;
        lden->next = next;
+       CLASSCACHE_COUNT(stat_new_loader_entry);
 
        return lden;
 }
@@ -169,6 +323,8 @@ static classcache_loader_entry * classcache_merge_loaders(
        classcache_loader_entry *ldenB;
        classcache_loader_entry **chain;
 
+       CLASSCACHE_COUNT(stat_merge_loader_entries);
+
        /* XXX This is a quadratic algorithm. If this ever
         * becomes a problem, the loader lists should be
         * stored as sorted lists and merged in linear time. */
@@ -198,6 +354,65 @@ static classcache_loader_entry * classcache_merge_loaders(
        return result;
 }
 
+/* classcache_merge_class_entries **********************************************
+   Merge two `classcache_class_entry`s into one.
+   (internally used helper function)
+  
+   IN:
+       en...............the classcache_name_entry containing both class entries
+       clsenA...........first class entry, will receive the result
+          clsenB...........second class entry
+
+   PRE-CONDITION:
+       Either both entries must have the same classobj, or one of them has
+          classobj == NULL.
+
+   NOTE:
+       clsenB is freed by this function!
+  
+*******************************************************************************/
+
+static void classcache_merge_class_entries(classcache_name_entry *en,
+                                                                                  classcache_class_entry *clsenA,
+                                                                                  classcache_class_entry *clsenB)
+{
+#ifdef CLASSCACHE_VERBOSE
+       char logbuffer[1024];
+#endif
+       
+       assert(en);
+       assert(clsenA);
+       assert(clsenB);
+       assert(!clsenA->classobj || !clsenB->classobj || clsenA->classobj == clsenB->classobj);
+
+#ifdef CLASSCACHE_VERBOSE
+       sprintf(logbuffer,"classcache_merge_class_entries(%p,%p->%p,%p->%p) ", 
+                       (void*)en,(void*)clsenA,(void*)clsenA->classobj,(void*)clsenB,(void*)clsenB->classobj);
+       if (clsenA->classobj)
+               utf_cat_classname(logbuffer, clsenA->classobj->name);
+       if (clsenB->classobj)
+               utf_cat_classname(logbuffer, clsenB->classobj->name);
+       log_println(logbuffer);
+#endif
+
+       CLASSCACHE_COUNT(stat_merge_class_entries);
+
+       /* clsenB will be merged into clsenA */
+       clsenA->loaders = classcache_merge_loaders(clsenA->loaders, clsenB->loaders);
+       clsenB->loaders = NULL; /* these have been freed or reused */
+
+       clsenA->constraints = classcache_merge_loaders(clsenA->constraints,
+                                                                                                  clsenB->constraints);
+       clsenB->constraints = NULL; /* these have been freed or reused */
+
+       if (!clsenA->classobj)
+               clsenA->classobj = clsenB->classobj;
+
+       /* remove clsenB from the list of class entries */
+       classcache_remove_class_entry(en, clsenB);
+}
+
 
 /* classcache_lookup_name ******************************************************
  
@@ -218,9 +433,10 @@ static classcache_name_entry *classcache_lookup_name(utf *name)
        classcache_name_entry *c;           /* hash table element                 */
        u4 key;                             /* hashkey computed from classname    */
        u4 slot;                            /* slot in hashtable                  */
-/*     u4 i; */
 
-       key  = utf_hashkey(name->text, (u4) name->blength);
+       CLASSCACHE_COUNT(stat_lookup_name);
+
+       key  = CLASSCACHE_HASH(name->text, (u4) name->blength);
        slot = key & (hashtable_classcache.size - 1);
        c    = hashtable_classcache.ptr[slot];
 
@@ -228,6 +444,7 @@ static classcache_name_entry *classcache_lookup_name(utf *name)
 
        while (c) {
                /* entry found in hashtable */
+               CLASSCACHE_COUNT(stat_lookup_name_entry);
 
                if (c->name == name)
                        return c;
@@ -237,6 +454,7 @@ static classcache_name_entry *classcache_lookup_name(utf *name)
 
        /* not found */
 
+       CLASSCACHE_COUNT(stat_lookup_name_notfound);
        return NULL;
 }
 
@@ -262,7 +480,9 @@ static classcache_name_entry *classcache_new_name(utf *name)
        u4 slot;                                        /* slot in hashtable               */
        u4 i;
 
-       key  = utf_hashkey(name->text, (u4) name->blength);
+       CLASSCACHE_COUNT(stat_lookup_new_name);
+
+       key  = CLASSCACHE_HASH(name->text, (u4) name->blength);
        slot = key & (hashtable_classcache.size - 1);
        c    = hashtable_classcache.ptr[slot];
 
@@ -270,6 +490,7 @@ static classcache_name_entry *classcache_new_name(utf *name)
 
        while (c) {
                /* entry found in hashtable */
+               CLASSCACHE_COUNT(stat_lookup_new_name_entry);
 
                if (c->name == name)
                        return c;
@@ -286,19 +507,21 @@ static classcache_name_entry *classcache_new_name(utf *name)
 
        /* insert entry into hashtable */
        c->hashlink = (classcache_name_entry *) hashtable_classcache.ptr[slot];
+       CLASSCACHE_COUNTIF(c->hashlink,stat_lookup_new_name_collisions);
        hashtable_classcache.ptr[slot] = c;
 
        /* update number of hashtable-entries */
        hashtable_classcache.entries++;
+       CLASSCACHE_COUNT(stat_classnames_stored);
 
-       if (hashtable_classcache.entries > (hashtable_classcache.size * 2)) {
-
-               /* reorganization of hashtable, average length of 
-                  the external chains is approx. 2                */
+       if ((hashtable_classcache.entries*2) > hashtable_classcache.size) {
+               /* reorganization of hashtable */ 
 
                classcache_name_entry *c2;
                hashtable newhash;              /* the new hashtable */
 
+               CLASSCACHE_COUNT(stat_rehash_names);
+
                /* create new hashtable, double the size */
 
                hashtable_create(&newhash, hashtable_classcache.size * 2);
@@ -311,9 +534,10 @@ static classcache_name_entry *classcache_new_name(utf *name)
                        while (c2) {
                                classcache_name_entry *nextc = c2->hashlink;
                                u4 newslot =
-                                       (utf_hashkey(c2->name->text, (u4) c2->name->blength)) & (newhash.size - 1);
+                                       (CLASSCACHE_HASH(c2->name->text, (u4) c2->name->blength)) & (newhash.size - 1);
 
                                c2->hashlink = (classcache_name_entry *) newhash.ptr[newslot];
+                               CLASSCACHE_COUNTIF(c2->hashlink,stat_rehash_names_collisions);
                                newhash.ptr[newslot] = c2;
 
                                c2 = nextc;
@@ -346,7 +570,7 @@ static classcache_name_entry *classcache_new_name(utf *name)
    
 *******************************************************************************/
 
-classinfo *classcache_lookup(classloader *initloader, utf *classname)
+classinfo *classcache_lookup(classloader_t *initloader, utf *classname)
 {
        classcache_name_entry *en;
        classcache_class_entry *clsen;
@@ -355,15 +579,18 @@ classinfo *classcache_lookup(classloader *initloader, utf *classname)
 
        CLASSCACHE_LOCK();
 
+       CLASSCACHE_COUNT(stat_lookup);
        en = classcache_lookup_name(classname);
 
        if (en) {
                /* iterate over all class entries */
 
                for (clsen = en->classes; clsen; clsen = clsen->next) {
+                       CLASSCACHE_COUNT(stat_lookup_class_entry_checked);
                        /* check if this entry has been loaded by initloader */
 
                        for (lden = clsen->loaders; lden; lden = lden->next) {
+                               CLASSCACHE_COUNT(stat_lookup_loader_checked);
                                if (lden->loader == initloader) {
                                        /* found the loaded class entry */
 
@@ -395,7 +622,7 @@ classinfo *classcache_lookup(classloader *initloader, utf *classname)
    
 *******************************************************************************/
 
-classinfo *classcache_lookup_defined(classloader *defloader, utf *classname)
+classinfo *classcache_lookup_defined(classloader_t *defloader, utf *classname)
 {
        classcache_name_entry *en;
        classcache_class_entry *clsen;
@@ -441,7 +668,7 @@ classinfo *classcache_lookup_defined(classloader *defloader, utf *classname)
    
 *******************************************************************************/
 
-classinfo *classcache_lookup_defined_or_initiated(classloader *loader, 
+classinfo *classcache_lookup_defined_or_initiated(classloader_t *loader, 
                                                                                                  utf *classname)
 {
        classcache_name_entry *en;
@@ -499,19 +726,22 @@ classinfo *classcache_lookup_defined_or_initiated(classloader *loader,
    RETURN VALUE:
        cls..............everything ok, the class was stored in the cache,
           other classinfo..another class with the same (initloader,name) has been
-                           stored earlier. CLS has been freed and the earlier
+                           stored earlier. CLS has been freed[1] and the earlier
                                                stored class is returned.
        NULL.............an exception has been thrown.
    
    Note: synchronized with global tablelock
+
+   [1]...in case MAYFREE is true
    
 *******************************************************************************/
 
-classinfo *classcache_store(classloader *initloader, classinfo *cls,
+classinfo *classcache_store(classloader_t *initloader, classinfo *cls,
                                                        bool mayfree)
 {
        classcache_name_entry *en;
        classcache_class_entry *clsen;
+       classcache_class_entry *clsenB;
        classcache_loader_entry *lden;
 #ifdef CLASSCACHE_VERBOSE
        char logbuffer[1024];
@@ -523,10 +753,10 @@ classinfo *classcache_store(classloader *initloader, classinfo *cls,
        CLASSCACHE_LOCK();
 
 #ifdef CLASSCACHE_VERBOSE
-       sprintf(logbuffer,"classcache_store (%p,%d,", (void*)initloader,mayfree);
-       utf_strcat(logbuffer, cls->name);
+       sprintf(logbuffer,"classcache_store (%p,%d,%p=", (void*)initloader,mayfree,(void*)cls);
+       utf_cat_classname(logbuffer, cls->name);
        strcat(logbuffer,")");
-       log_text(logbuffer);
+       log_println(logbuffer);
 #endif
 
        en = classcache_new_name(cls->name);
@@ -538,37 +768,58 @@ classinfo *classcache_store(classloader *initloader, classinfo *cls,
 
                /* check if this entry has already been loaded by initloader */
                for (lden = clsen->loaders; lden; lden = lden->next) {
-                       if (lden->loader == initloader && clsen->classobj != cls) {
-                               /* A class with the same (initloader,name) pair has been stored already. */
-                               /* We free the given class and return the earlier one.                   */
+                       if (lden->loader == initloader) {
+                          if (clsen->classobj != cls) {
+                                       /* A class with the same (initloader,name) pair has been stored already. */
+                                       /* We free the given class and return the earlier one.                   */
 #ifdef CLASSCACHE_VERBOSE
-                               dolog("replacing %p with earlier loaded class %p",cls,clsen->classobj);
+                                       log_println("replacing %p with earlier loaded class %p",cls,clsen->classobj);
 #endif
-                               assert(clsen->classobj);
-                               if (mayfree)
-                                       class_free(cls);
-                               cls = clsen->classobj;
-                               goto return_success;
+                                       assert(clsen->classobj);
+                                       if (mayfree)
+                                               class_free(cls);
+                                       cls = clsen->classobj;
+                          }
+                          goto return_success;
                        }
                }
 
+               /* {This entry has not been resolved with initloader} */
+
                /* check if initloader is constrained to this entry */
                for (lden = clsen->constraints; lden; lden = lden->next) {
                        if (lden->loader == initloader) {
-                               /* we have to use this entry */
-                               /* check if is has already been resolved to another class */
-                               if (clsen->classobj && clsen->classobj != cls) {
-                                       /* a loading constraint is violated */
-                                       *exceptionptr = exceptions_new_linkageerror(
-                                                                               "loading constraint violated: ",cls);
-                                       goto return_exception;
+                               /* we have to use this entry. check if it has been resolved */
+                               if (clsen->classobj) {
+                                       /* check if is has already been resolved to another class */
+                                       if (clsen->classobj != cls) {
+                                               /* a loading constraint is violated */
+                                               exceptions_throw_linkageerror("loading constraint violated: ", cls);
+                                               goto return_exception;
+                                       }
+
+                                       /* record initloader as initiating loader */
+                                       clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
+                                       goto return_success;
                                }
 
+                               /* {this is the first resolution for this entry} */
                                /* record initloader as initiating loader */
                                clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
 
+                               /* maybe we can merge this entry with another one */
+                               for (clsenB = en->classes; clsenB; clsenB = clsenB->next) {
+                                       /* we dont want the entry that we have already */
+                                       if (clsenB->classobj == cls) {
+                                               /* this entry has the same classobj. let's merge them */
+                                               classcache_merge_class_entries(en,clsen,clsenB);
+                                               goto return_success;
+                                       }
+                               }
+
                                /* record the loaded class object */
                                clsen->classobj = cls;
+                               CLASSCACHE_COUNT(stat_classes_stored);
 
                                /* done */
                                goto return_success;
@@ -577,6 +828,23 @@ classinfo *classcache_store(classloader *initloader, classinfo *cls,
 
        }
 
+       /* {There is no class entry containing initloader as initiating 
+        *  or constrained loader.} */
+
+       /* we look for a class entry with the same classobj we want to store */
+       for (clsen = en->classes; clsen; clsen = clsen->next) {
+               if (clsen->classobj == cls) {
+                       /* this entry is about the same classobj. let's use it */
+                       /* check if this entry has already been loaded by initloader */
+                       for (lden = clsen->loaders; lden; lden = lden->next) {
+                               if (lden->loader == initloader)
+                                       goto return_success;
+                       }
+                       clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
+                       goto return_success;
+               }
+       }
+
        /* create a new class entry for this class object with */
        /* initiating loader initloader                        */
 
@@ -587,8 +855,12 @@ classinfo *classcache_store(classloader *initloader, classinfo *cls,
 
        clsen->next = en->classes;
        en->classes = clsen;
+       CLASSCACHE_COUNT(stat_classes_stored);
 
   return_success:
+#ifdef CLASSCACHE_VERBOSE
+       classcache_debug_dump(stdout,cls->name);
+#endif
        CLASSCACHE_UNLOCK();
        return cls;
 
@@ -626,7 +898,7 @@ bool classcache_store_unique(classinfo *cls)
                return false;
 
        if (result != cls) {
-               *exceptionptr = new_internalerror("class already stored in the class cache");
+               exceptions_throw_internalerror("class already stored in the class cache");
                return false;
        }
 
@@ -666,9 +938,9 @@ classinfo *classcache_store_defined(classinfo *cls)
 
 #ifdef CLASSCACHE_VERBOSE
        sprintf(logbuffer,"classcache_store_defined (%p,", (void*)cls->classloader);
-       utf_strcat(logbuffer, cls->name);
+       utf_cat_classname(logbuffer, cls->name);
        strcat(logbuffer,")");
-       log_text(logbuffer);
+       log_println(logbuffer);
 #endif
 
        en = classcache_new_name(cls->name);
@@ -684,7 +956,7 @@ classinfo *classcache_store_defined(classinfo *cls)
                        /* (if it is a different classinfo)                     */
                        if (clsen->classobj != cls) {
 #ifdef CLASSCACHE_VERBOSE
-                               dolog("replacing %p with earlier defined class %p",cls,clsen->classobj);
+                               log_println("replacing %p with earlier defined class %p",cls,clsen->classobj);
 #endif
                                class_free(cls);
                                cls = clsen->classobj;
@@ -703,8 +975,12 @@ classinfo *classcache_store_defined(classinfo *cls)
 
        clsen->next = en->classes;
        en->classes = clsen;
+       CLASSCACHE_COUNT(stat_classes_stored);
 
 return_success:
+#ifdef CLASSCACHE_VERBOSE
+       classcache_debug_dump(stdout,cls->name);
+#endif
        CLASSCACHE_UNLOCK();
        return cls;
 }
@@ -726,7 +1002,7 @@ return_success:
 
 static classcache_class_entry * classcache_find_loader(
                                                                        classcache_name_entry * entry,
-                                                                       classloader * loader)
+                                                                       classloader_t * loader)
 {
        classcache_class_entry *clsen;
        classcache_loader_entry *lden;
@@ -861,7 +1137,7 @@ void classcache_free(void)
                }
        }
 
-       MFREE(hashtable_classcache.ptr, voidptr, hashtable_classcache.size);
+       MFREE(hashtable_classcache.ptr, void*, hashtable_classcache.size);
        hashtable_classcache.size = 0;
        hashtable_classcache.entries = 0;
        hashtable_classcache.ptr = NULL;
@@ -884,8 +1160,9 @@ void classcache_free(void)
    
 *******************************************************************************/
 
-bool classcache_add_constraint(classloader * a,
-                                                          classloader * b,
+#if defined(ENABLE_VERIFIER)
+bool classcache_add_constraint(classloader_t * a,
+                                                          classloader_t * b,
                                                           utf * classname)
 {
        classcache_name_entry *en;
@@ -895,20 +1172,25 @@ bool classcache_add_constraint(classloader * a,
        assert(classname);
 
 #ifdef CLASSCACHE_VERBOSE
-       fprintf(stderr, "classcache_add_constraint(%p,%p,", (void *) a, (void *) b);
-       utf_fprint_classname(stderr, classname);
-       fprintf(stderr, ")\n");
+       log_start();
+       log_print("classcache_add_constraint(%p,%p,", (void *) a, (void *) b);
+       utf_fprint_printable_ascii_classname(stdout, classname);
+       log_print(")\n");
+       log_finish();
 #endif
 
        /* a constraint with a == b is trivially satisfied */
-       if (a == b)
+       if (a == b) {
+               CLASSCACHE_COUNT(stat_trivial_constraints);
                return true;
+       }
 
        CLASSCACHE_LOCK();
 
        en = classcache_new_name(classname);
 
        assert(en);
+       CLASSCACHE_COUNT(stat_nontriv_constraints);
 
        /* find the entry loaded by / constrained to each loader */
        clsenA = classcache_find_loader(en, a);
@@ -916,6 +1198,7 @@ bool classcache_add_constraint(classloader * a,
 
        if (clsenA && clsenB) {
                /* { both loaders have corresponding entries } */
+               CLASSCACHE_COUNT(stat_nontriv_constraints_both);
 
                /* if the entries are the same, the constraint is already recorded */
                if (clsenA == clsenB)
@@ -925,25 +1208,14 @@ bool classcache_add_constraint(classloader * a,
                if (clsenA->classobj && clsenB->classobj
                        && clsenA->classobj != clsenB->classobj) {
                        /* no, the constraint is violated */
-                       *exceptionptr = exceptions_new_linkageerror(
-                                                         "loading constraint violated: ",clsenA->classobj);
+                       exceptions_throw_linkageerror("loading constraint violated: ",
+                                                                                 clsenA->classobj);
                        goto return_exception;
                }
 
                /* yes, merge the entries */
-               /* clsenB will be merged into clsenA */
-               clsenA->loaders = classcache_merge_loaders(clsenA->loaders, clsenB->loaders);
-               clsenB->loaders = NULL;
-
-               clsenA->constraints = classcache_merge_loaders(clsenA->constraints,
-                                                                                                          clsenB->constraints);
-               clsenB->constraints = NULL;
-
-               if (!clsenA->classobj)
-                       clsenA->classobj = clsenB->classobj;
-
-               /* remove clsenB from the list of class entries */
-               classcache_remove_class_entry(en, clsenB);
+               classcache_merge_class_entries(en,clsenA,clsenB);
+               CLASSCACHE_COUNT(stat_nontriv_constraints_merged);
        }
        else {
                /* { at most one of the loaders has a corresponding entry } */
@@ -954,6 +1226,7 @@ bool classcache_add_constraint(classloader * a,
 
                if (!clsenA) {
                        /* { no loader has a corresponding entry } */
+                       CLASSCACHE_COUNT(stat_nontriv_constraints_none);
 
                        /* create a new class entry with the constraint (a,b,en->name) */
                        clsenA = NEW(classcache_class_entry);
@@ -966,6 +1239,8 @@ bool classcache_add_constraint(classloader * a,
                        en->classes = clsenA;
                }
                else {
+                       CLASSCACHE_COUNT(stat_nontriv_constraints_one);
+
                        /* make b the loader that has no corresponding entry */
                        if (clsenB)
                                b = a;
@@ -983,6 +1258,237 @@ bool classcache_add_constraint(classloader * a,
        CLASSCACHE_UNLOCK();
        return false;                           /* exception */
 }
+#endif /* defined(ENABLE_VERIFIER) */
+
+/* classcache_add_constraints_for_params ***************************************
+   Add loading constraints for the parameters and return type of 
+   the given method.
+  
+   IN:
+       a................first initiating loader
+       b................second initiating loader
+       m................methodinfo 
+  
+   RETURN VALUE:
+       true.............everything ok, the constraints have been added,
+       false............an exception has been thrown.
+   
+   Note: synchronized with global tablelock
+   
+*******************************************************************************/
+
+#if defined(ENABLE_VERIFIER)
+bool classcache_add_constraints_for_params(classloader_t * a,
+                                                                                  classloader_t * b,
+                                                                                  methodinfo *m)
+{
+       methoddesc *md;
+       typedesc *td;
+       s4 i;
+
+       /* a constraint with a == b is trivially satisfied */
+
+       if (a == b) {
+               return true;
+       }
+
+       /* get the parsed descriptor */
+
+       assert(m);
+       md = m->parseddesc;
+       assert(md);
+
+       /* constrain the return type */
+
+       if (md->returntype.type == TYPE_ADR) {
+               if (!classcache_add_constraint(a, b, md->returntype.classref->name))
+                       return false; /* exception */
+       }
+
+       /* constrain each reference type used in the parameters */
+
+       td = md->paramtypes;
+       i = md->paramcount;
+       for (; i--; td++) {
+               if (td->type != TYPE_ADR)
+                       continue;
+
+               if (!classcache_add_constraint(a, b, td->classref->name))
+                       return false; /* exception */
+       }
+
+       /* everything ok */
+       return true;
+}
+#endif /* defined(ENABLE_VERIFIER) */
+
+
+/* classcache_number_of_loaded_classes *****************************************
+
+   Counts the number of loaded classes and returns it.
+
+   Note: This function assumes that the CLASSCACHE_LOCK is held by the
+   caller!
+
+*******************************************************************************/
+
+static s4 classcache_number_of_loaded_classes(void)
+{
+       classcache_name_entry  *en;
+       classcache_class_entry *clsen;
+       s4                      number;
+       s4                      i;
+
+       /* initialize class counter */
+
+       number = 0;
+
+       for (i = 0; i < hashtable_classcache.size; i++) {
+               /* iterate over hashlink */
+
+               for (en = hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
+                       /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
+
+                       if (en->name->text[0] == '$')
+                               continue;
+
+                       /* iterate over classes with same name */
+
+                       for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+                               /* get only loaded classes */
+
+                               if (clsen->classobj != NULL)
+                                       number++;
+                       }
+               }
+       }
+
+       return number;
+}
+
+
+/* classcache_get_loaded_class_count *******************************************
+
+   Counts the number of loaded classes and returns it.
+
+*******************************************************************************/
+
+s4 classcache_get_loaded_class_count(void)
+{
+       s4 count;
+
+       CLASSCACHE_LOCK();
+
+       count = classcache_number_of_loaded_classes();
+       
+       CLASSCACHE_UNLOCK();
+
+       return count;
+}
+
+
+/* classcache_get_loaded_classes ***********************************************
+
+   Returns an array of all loaded classes as array.  The array is
+   allocaed on the Java heap.
+
+*******************************************************************************/
+
+#if defined(ENABLE_JVMTI)
+void classcache_get_loaded_classes(s4 *class_count_ptr,
+                                                                  classinfo ***classes_ptr)
+{
+       classinfo              **classes;
+       s4                       class_count;
+       classcache_name_entry   *en;
+       classcache_class_entry  *clsen;
+       s4                       i;
+       s4                       j;
+
+       CLASSCACHE_LOCK();
+
+       /* get the number of loaded classes and allocate the array */
+
+       class_count = classcache_number_of_loaded_classes();
+
+       classes = GCMNEW(classinfo*, class_count);
+
+       /* look in every slot of the hashtable */
+
+       for (i = 0, j = 0; i < hashtable_classcache.size; i++) {
+               /* iterate over hashlink */
+
+               for (en = hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
+                       /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
+
+                       if (en->name->text[0] == '$')
+                               continue;
+
+                       /* iterate over classes with same name */
+
+                       for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+                               /* get only loaded classes */
+
+                               if (clsen->classobj != NULL) {
+                                       classes[j] = clsen->classobj;
+                                       j++;
+                               }
+                       }
+               }
+       }
+
+       /* pass the return values */
+
+       *class_count_ptr = class_count;
+       *classes_ptr     = classes;
+
+       CLASSCACHE_UNLOCK();
+}
+#endif /* defined(ENABLE_JVMTI) */
+
+
+/* classcache_foreach_loaded_class *********************************************
+
+   Calls the given function for each loaded class.
+
+*******************************************************************************/
+
+void classcache_foreach_loaded_class(classcache_foreach_functionptr_t func,
+                                                                        void *data)
+{
+       classcache_name_entry   *en;
+       classcache_class_entry  *clsen;
+       s4                       i;
+
+       CLASSCACHE_LOCK();
+
+       /* look in every slot of the hashtable */
+
+       for (i = 0; i < hashtable_classcache.size; i++) {
+               /* iterate over hashlink */
+
+               for (en = hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
+                       /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
+
+                       if (en->name->text[0] == '$')
+                               continue;
+
+                       /* iterate over classes with same name */
+
+                       for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+                               /* get only loaded classes */
+
+                               if (clsen->classobj != NULL) {
+                                       (*func)(clsen->classobj, data);
+                               }
+                       }
+               }
+       }
+
+       CLASSCACHE_UNLOCK();
+}
+
 
 /*============================================================================*/
 /* DEBUG DUMPS                                                                */
@@ -994,13 +1500,16 @@ bool classcache_add_constraint(classloader * a,
   
    IN:
        file.............output stream
+          only.............if != NULL, only print entries for this name
+                           (Currently we print also the rest of the hash chain to
+                                                get a feel for the average length of hash chains.)
   
    Note: synchronized with global tablelock
    
 *******************************************************************************/
 
 #ifndef NDEBUG
-void classcache_debug_dump(FILE * file)
+void classcache_debug_dump(FILE * file,utf *only)
 {
        classcache_name_entry *c;
        classcache_class_entry *clsen;
@@ -1009,37 +1518,52 @@ void classcache_debug_dump(FILE * file)
 
        CLASSCACHE_LOCK();
 
-       fprintf(file, "\n=== [loaded class cache] =====================================\n\n");
-       fprintf(file, "hash size   : %d\n", (int) hashtable_classcache.size);
-       fprintf(file, "hash entries: %d\n", (int) hashtable_classcache.entries);
-       fprintf(file, "\n");
+       log_println("=== [loaded class cache] =====================================");
+       log_println("hash size   : %d", (int) hashtable_classcache.size);
+       log_println("hash entries: %d", (int) hashtable_classcache.entries);
+       log_println("");
+
+       if (only) {
+               c = classcache_lookup_name(only);
+               slot = 0; /* avoid compiler warning */
+               goto dump_it;
+       }
 
        for (slot = 0; slot < hashtable_classcache.size; ++slot) {
                c = (classcache_name_entry *) hashtable_classcache.ptr[slot];
 
+dump_it:
                for (; c; c = c->hashlink) {
-                       utf_fprint_classname(file, c->name);
+                       utf_fprint_printable_ascii_classname(file, c->name);
                        fprintf(file, "\n");
 
                        /* iterate over all class entries */
                        for (clsen = c->classes; clsen; clsen = clsen->next) {
                                if (clsen->classobj) {
-                                       fprintf(file, "    loaded %p\n", (void *) clsen->classobj);
+                                       log_println("    loaded %p", (void *) clsen->classobj);
                                }
                                else {
-                                       fprintf(file, "    unresolved\n");
+                                       log_println("    unresolved");
                                }
-                               fprintf(file, "        loaders:");
+
+                               log_start();
+                               log_print("        loaders: ");
                                for (lden = clsen->loaders; lden; lden = lden->next) {
-                                       fprintf(file, "<%p> %p", (void *) lden, (void *) lden->loader);
+                                       log_print("<%p> %p ", (void *) lden, (void *) lden->loader);
                                }
-                               fprintf(file, "\n        constraints:");
+                               log_finish();
+
+                               log_start();
+                               log_print("        constraints: ");
                                for (lden = clsen->constraints; lden; lden = lden->next) {
-                                       fprintf(file, "<%p> %p", (void *) lden, (void *) lden->loader);
+                                       log_print("<%p> %p ", (void *) lden, (void *) lden->loader);
                                }
-                               fprintf(file, "\n");
+                               log_finish();
                        }
                }
+
+               if (only)
+                       break;
        }
        fprintf(file, "\n==============================================================\n\n");