* src/threads/posix/lock.c: Moved to .cpp.
[cacao.git] / src / vm / classcache.c
index bc86f721944cd3352c8522c5ab89f9f0ba3b2ba2..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.
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
 
-   Contact: cacao@complang.tuwien.ac.at
+*/
 
-   Authors: Edwin Steiner
 
-   Changes: Christian Thalinger
+#include "config.h"
 
-   $Id: classcache.c 2725 2005-06-16 19:10:35Z edwin $
+#include <assert.h>
 
-*/
+#include "vm/types.h"
 
+#include "mm/memory.h"
 
-#include <assert.h>
+#include "threads/lock.hpp"
+#include "threads/mutex.hpp"
+
+#include "toolbox/hashtable.h"
+#include "toolbox/logging.h"
 
-#include "mm/memory.h"
 #include "vm/classcache.h"
-#include "vm/exceptions.h"
-#include "vm/stringlocal.h"
-#include "vm/tables.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 */
 
-#ifndef NDEBUG
-#define CLASSCACHE_DEBUG
-#endif
+/*============================================================================*/
+/* STATISTICS                                                                 */
+/*============================================================================*/
 
-#ifdef CLASSCACHE_DEBUG
-#define CLASSCACHE_ASSERT(cond)  assert(cond)
+/*#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_ASSERT(cond)
+#define CLASSCACHE_COUNT(cnt)
+#define CLASSCACHE_COUNTIF(cond,cnt)
 #endif
 
 /*============================================================================*/
        /*          NOT synchronized!              */
        /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
 
-#if defined(USE_THREADS) && defined(NATIVE_THREADS)
-#  define CLASSCACHE_LOCK()    tables_lock()
-#  define CLASSCACHE_UNLOCK()  tables_unlock()
+#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()
+# define CLASSCACHE_LOCK()
+# define CLASSCACHE_UNLOCK()
 #endif
 
 /*============================================================================*/
 /* GLOBAL VARIABLES                                                           */
 /*============================================================================*/
 
-hashtable classcache_hash;
+hashtable hashtable_classcache;
+
+#if defined(ENABLE_THREADS)
+static Mutex *classcache_hashtable_mutex;
+#endif
+
 
 /*============================================================================*/
 /*                                                                            */
 /*============================================================================*/
 
+/* 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!
   
 *******************************************************************************/
 
-void
-classcache_init(
-       )
+bool classcache_init(void)
 {
-       init_hashtable(&classcache_hash, CLASSCACHE_INIT_SIZE);
+       TRACESUBSYSTEMINITIALIZATION("classcache_init");
+
+       /* create the hashtable */
+
+       hashtable_create(&hashtable_classcache, CLASSCACHE_INIT_SIZE);
+
+#if defined(ENABLE_THREADS)
+       /* create utf hashtable mutex */
+
+       classcache_hashtable_mutex = Mutex_new();
+#endif
+
+       /* everything's ok */
+
+       return true;
 }
 
 /* classcache_new_loader_entry *************************************************
@@ -118,16 +283,16 @@ classcache_init(
   
 *******************************************************************************/
 
-static classcache_loader_entry *
-classcache_new_loader_entry(
-       /*@shared@*/ /*@null@*/ classloader * loader,
-       /*@only  @*/ /*@null@*/ classcache_loader_entry * next)
+static classcache_loader_entry * classcache_new_loader_entry(
+                                                                       classloader_t * loader,
+                                                                       classcache_loader_entry * next)
 {
        classcache_loader_entry *lden;
 
        lden = NEW(classcache_loader_entry);
        lden->loader = loader;
        lden->next = next;
+       CLASSCACHE_COUNT(stat_new_loader_entry);
 
        return lden;
 }
@@ -149,16 +314,17 @@ classcache_new_loader_entry(
   
 *******************************************************************************/
 
-static /*@null@*/ classcache_loader_entry *
-classcache_merge_loaders(
-       /*@null@*/ classcache_loader_entry * lista,
-       /*@null@*/ classcache_loader_entry * listb)
+static classcache_loader_entry * classcache_merge_loaders(
+                                                                       classcache_loader_entry * lista,
+                                                                       classcache_loader_entry * listb)
 {
        classcache_loader_entry *result;
        classcache_loader_entry *ldenA;
        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. */
@@ -166,9 +332,9 @@ classcache_merge_loaders(
        result = NULL;
        chain = &result;
 
-       for (ldenA = lista; ldenA != NULL; ldenA = ldenA->next) {
+       for (ldenA = lista; ldenA; ldenA = ldenA->next) {
 
-               for (ldenB = listb; ldenB != NULL; ldenB = ldenB->next) {
+               for (ldenB = listb; ldenB; ldenB = ldenB->next) {
                        if (ldenB->loader == ldenA->loader)
                                goto common_element;
                }
@@ -188,6 +354,66 @@ 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 ******************************************************
  
    Lookup a name in the first level of the cache
@@ -202,38 +428,37 @@ classcache_merge_loaders(
           
 *******************************************************************************/
 
-static /*@exposed@*/ /*@null@*/ classcache_name_entry *
-classcache_lookup_name(
-       /*@shared@*/ utf * name)
+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;
+       classcache_name_entry *c;           /* hash table element                 */
+       u4 key;                             /* hashkey computed from classname    */
+       u4 slot;                            /* slot in hashtable                  */
 
-       key = utf_hashkey(name->text, (u4) name->blength);
-       slot = key & (classcache_hash.size - 1);
-       c = classcache_hash.ptr[slot];
+       CLASSCACHE_COUNT(stat_lookup_name);
+
+       key  = CLASSCACHE_HASH(name->text, (u4) name->blength);
+       slot = key & (hashtable_classcache.size - 1);
+       c    = hashtable_classcache.ptr[slot];
 
        /* search external hash chain for the entry */
+
        while (c) {
-               if (c->name->blength == name->blength) {
-                       for (i = 0; i < (u4) name->blength; i++)
-                               if (name->text[i] != c->name->text[i])
-                                       goto nomatch;
+               /* entry found in hashtable */
+               CLASSCACHE_COUNT(stat_lookup_name_entry);
 
-                       /* entry found in hashtable */
+               if (c->name == name)
                        return c;
-               }
 
-         nomatch:
-               c = c->hashlink;                /* next element in external chain */
+               c = c->hashlink;                    /* next element in external chain */
        }
 
        /* not found */
+
+       CLASSCACHE_COUNT(stat_lookup_name_notfound);
        return NULL;
 }
 
+
 /* classcache_new_name *********************************************************
  
    Return a classcache_name_entry for the given name. The entry is created
@@ -248,32 +473,29 @@ classcache_lookup_name(
           
 *******************************************************************************/
 
-static /*@exposed@*/ classcache_name_entry *
-classcache_new_name(
-       /*@shared@*/ utf * name)
+static classcache_name_entry *classcache_new_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);
-       slot = key & (classcache_hash.size - 1);
-       c = classcache_hash.ptr[slot];
+       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];
 
        /* search external hash chain for the entry */
+
        while (c) {
-               if (c->name->blength == name->blength) {
-                       for (i = 0; i < (u4) name->blength; i++)
-                               if (name->text[i] != c->name->text[i])
-                                       goto nomatch;
+               /* entry found in hashtable */
+               CLASSCACHE_COUNT(stat_lookup_new_name_entry);
 
-                       /* entry found in hashtable */
+               if (c->name == name)
                        return c;
-               }
 
-         nomatch:
-               c = c->hashlink;                /* next element in external chain */
+               c = c->hashlink;                    /* next element in external chain */
        }
 
        /* location in hashtable found, create new entry */
@@ -284,33 +506,38 @@ classcache_new_name(
        c->classes = NULL;
 
        /* insert entry into hashtable */
-       c->hashlink = (classcache_name_entry *) classcache_hash.ptr[slot];
-       classcache_hash.ptr[slot] = c;
+       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 */
-       classcache_hash.entries++;
+       hashtable_classcache.entries++;
+       CLASSCACHE_COUNT(stat_classnames_stored);
 
-       if (classcache_hash.entries > (classcache_hash.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 */
-               init_hashtable(&newhash, classcache_hash.size * 2);
-               newhash.entries = classcache_hash.entries;
+
+               hashtable_create(&newhash, hashtable_classcache.size * 2);
+               newhash.entries = hashtable_classcache.entries;
 
                /* transfer elements to new hashtable */
-               for (i = 0; i < classcache_hash.size; i++) {
-                       c2 = (classcache_name_entry *) classcache_hash.ptr[i];
+
+               for (i = 0; i < hashtable_classcache.size; i++) {
+                       c2 = (classcache_name_entry *) hashtable_classcache.ptr[i];
                        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;
@@ -318,14 +545,15 @@ classcache_new_name(
                }
 
                /* dispose old table */
-               MFREE(classcache_hash.ptr, void *,
-                         classcache_hash.size);
-               classcache_hash = newhash;
+
+               MFREE(hashtable_classcache.ptr, void *, hashtable_classcache.size);
+               hashtable_classcache = newhash;
        }
 
        return c;
 }
 
+
 /* classcache_lookup ***********************************************************
  
    Lookup a possibly loaded class
@@ -342,10 +570,7 @@ classcache_new_name(
    
 *******************************************************************************/
 
-/*@null@*/ classinfo *
-classcache_lookup(
-       /*@shared@*/ classloader * initloader,
-       /*@shared@*/ utf * classname)
+classinfo *classcache_lookup(classloader_t *initloader, utf *classname)
 {
        classcache_name_entry *en;
        classcache_class_entry *clsen;
@@ -354,27 +579,35 @@ classcache_lookup(
 
        CLASSCACHE_LOCK();
 
+       CLASSCACHE_COUNT(stat_lookup);
        en = classcache_lookup_name(classname);
 
        if (en) {
                /* iterate over all class entries */
-               for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+
+               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 != NULL; lden = lden->next) {
+
+                       for (lden = clsen->loaders; lden; lden = lden->next) {
+                               CLASSCACHE_COUNT(stat_lookup_loader_checked);
                                if (lden->loader == initloader) {
                                        /* found the loaded class entry */
-                                       CLASSCACHE_ASSERT(clsen->classobj != NULL);
+
+                                       assert(clsen->classobj);
                                        cls = clsen->classobj;
                                        goto found;
                                }
                        }
                }
        }
+
   found:
        CLASSCACHE_UNLOCK();
        return cls;
 }
 
+
 /* classcache_lookup_defined ***************************************************
  
    Lookup a class with the given name and defining loader
@@ -389,10 +622,7 @@ classcache_lookup(
    
 *******************************************************************************/
 
-/*@null@*/ classinfo *
-classcache_lookup_defined(
-       /*@shared@*/ classloader * defloader,
-       /*@shared@*/ utf * classname)
+classinfo *classcache_lookup_defined(classloader_t *defloader, utf *classname)
 {
        classcache_name_entry *en;
        classcache_class_entry *clsen;
@@ -404,7 +634,7 @@ classcache_lookup_defined(
 
        if (en) {
                /* iterate over all class entries */
-               for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+               for (clsen = en->classes; clsen; clsen = clsen->next) {
                        if (!clsen->classobj)
                                continue;
 
@@ -415,11 +645,71 @@ classcache_lookup_defined(
                        }
                }
        }
+
   found:
        CLASSCACHE_UNLOCK();
        return cls;
 }
 
+
+/* classcache_lookup_defined_or_initiated **************************************
+   Lookup a class that has been defined or initiated by the given loader
+  
+   IN:
+       loader...........defining or initiating loader
+       classname........class name to look up
+  
+   RETURN VALUE:
+       The return value is a pointer to the cached class object,
+       or NULL, if the class is not in the cache.
+
+   Note: synchronized with global tablelock
+   
+*******************************************************************************/
+
+classinfo *classcache_lookup_defined_or_initiated(classloader_t *loader, 
+                                                                                                 utf *classname)
+{
+       classcache_name_entry *en;
+       classcache_class_entry *clsen;
+       classcache_loader_entry *lden;
+       classinfo *cls = NULL;
+
+       CLASSCACHE_LOCK();
+
+       en = classcache_lookup_name(classname);
+
+       if (en) {
+               /* iterate over all class entries */
+
+               for (clsen = en->classes; clsen; clsen = clsen->next) {
+
+                       /* check if this entry has been defined by loader */
+                       if (clsen->classobj && clsen->classobj->classloader == loader) {
+                               cls = clsen->classobj;
+                               goto found;
+                       }
+                       
+                       /* check if this entry has been initiated by loader */
+                       for (lden = clsen->loaders; lden; lden = lden->next) {
+                               if (lden->loader == loader) {
+                                       /* found the loaded class entry */
+
+                                       assert(clsen->classobj);
+                                       cls = clsen->classobj;
+                                       goto found;
+                               }
+                       }
+               }
+       }
+
+  found:
+       CLASSCACHE_UNLOCK();
+       return cls;
+}
+
+
 /* classcache_store ************************************************************
    
    Store a loaded class. If a class of the same name has already been stored
@@ -436,79 +726,100 @@ classcache_lookup_defined(
    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
    
 *******************************************************************************/
 
-/*@shared@*/ /*@null@*/ classinfo *
-classcache_store(
-       /*@shared@*/ /*@null@*/ classloader * initloader,
-       /*@shared@*/ classinfo * cls,
-       bool mayfree)
+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];
 #endif
        
-       CLASSCACHE_ASSERT(cls != NULL);
-       CLASSCACHE_ASSERT(cls->loaded != 0);
+       assert(cls);
+       assert(cls->state & CLASS_LOADED);
 
        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);
 
-       CLASSCACHE_ASSERT(en != NULL);
+       assert(en);
 
        /* iterate over all class entries */
-       for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+       for (clsen = en->classes; clsen; clsen = clsen->next) {
 
                /* check if this entry has already been loaded by initloader */
-               for (lden = clsen->loaders; lden != NULL; lden = lden->next) {
+               for (lden = clsen->loaders; lden; lden = lden->next) {
                        if (lden->loader == initloader) {
-                               /* A class with the same (initloader,name) pair has been stored already. */
-                               /* We free the given class and return the earlier one.                   */
+                          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
-                               CLASSCACHE_ASSERT(clsen->classobj != NULL);
-                               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 != NULL; lden = lden->next) {
+               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 != NULL && clsen->classobj != cls) {
-                                       /* a loading constraint is violated */
-                                       *exceptionptr = new_exception_message(string_java_lang_LinkageError,
-                                                                                                                 "loading constraint violated XXX add message");
-                                       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;
@@ -517,6 +828,23 @@ classcache_store(
 
        }
 
+       /* {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                        */
 
@@ -527,8 +855,12 @@ classcache_store(
 
        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;
 
@@ -557,18 +889,16 @@ classcache_store(
    
 *******************************************************************************/
 
-bool 
-classcache_store_unique(
-               /*@shared@*/ classinfo *cls)
+bool classcache_store_unique(classinfo *cls)
 {
-       /*@shared@*/ classinfo *result;
+       classinfo *result;
 
        result = classcache_store(NULL,cls,false);
        if (result == NULL)
                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;
        }
 
@@ -593,8 +923,7 @@ classcache_store_unique(
    
 *******************************************************************************/
 
-/*@shared@*/ /*@null@*/ classinfo * 
-classcache_store_defined(/*@shared@*/ classinfo *cls)
+classinfo *classcache_store_defined(classinfo *cls)
 {
        classcache_name_entry *en;
        classcache_class_entry *clsen;
@@ -602,35 +931,36 @@ classcache_store_defined(/*@shared@*/ classinfo *cls)
        char logbuffer[1024];
 #endif
 
-       CLASSCACHE_ASSERT(cls != NULL);
-       CLASSCACHE_ASSERT(cls->loaded != 0);
+       assert(cls);
+       assert(cls->state & CLASS_LOADED);
 
        CLASSCACHE_LOCK();
 
 #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);
 
-       CLASSCACHE_ASSERT(en != NULL);
+       assert(en);
 
        /* iterate over all class entries */
-       for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+       for (clsen = en->classes; clsen; clsen = clsen->next) {
                
                /* check if this class has been defined by the same classloader */
-               if (clsen->classobj != NULL && clsen->classobj->classloader == cls->classloader) {
+               if (clsen->classobj && clsen->classobj->classloader == cls->classloader) {
                        /* we found an earlier definition, delete the newer one */
+                       /* (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
-                       /* we assert that the earlier object is not the same that we were given */
-                       CLASSCACHE_ASSERT(clsen->classobj != cls);
-                       class_free(cls);
-                       cls = clsen->classobj;
+                               class_free(cls);
+                               cls = clsen->classobj;
+                       }
                        goto return_success;
                }
        }
@@ -645,8 +975,12 @@ classcache_store_defined(/*@shared@*/ 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;
 }
@@ -666,27 +1000,26 @@ return_success:
    
 *******************************************************************************/
 
-static /*@exposed@*/ /*@null@*/ classcache_class_entry *
-classcache_find_loader(
-       classcache_name_entry * entry,
-       /*@shared@*/ /*@null@*/ classloader * loader)
+static classcache_class_entry * classcache_find_loader(
+                                                                       classcache_name_entry * entry,
+                                                                       classloader_t * loader)
 {
        classcache_class_entry *clsen;
        classcache_loader_entry *lden;
 
-       CLASSCACHE_ASSERT(entry != NULL);
+       assert(entry);
 
        /* iterate over all class entries */
-       for (clsen = entry->classes; clsen != NULL; clsen = clsen->next) {
+       for (clsen = entry->classes; clsen; clsen = clsen->next) {
 
                /* check if this entry has already been loaded by initloader */
-               for (lden = clsen->loaders; lden != NULL; lden = lden->next) {
+               for (lden = clsen->loaders; lden; lden = lden->next) {
                        if (lden->loader == loader)
                                return clsen;   /* found */
                }
 
                /* check if loader is constrained to this entry */
-               for (lden = clsen->constraints; lden != NULL; lden = lden->next) {
+               for (lden = clsen->constraints; lden; lden = lden->next) {
                        if (lden->loader == loader)
                                return clsen;   /* found */
                }
@@ -705,20 +1038,18 @@ classcache_find_loader(
           
 *******************************************************************************/
 
-static void
-classcache_free_class_entry(
-       classcache_class_entry * clsen)
+static void classcache_free_class_entry(classcache_class_entry * clsen)
 {
        classcache_loader_entry *lden;
        classcache_loader_entry *next;
 
-       CLASSCACHE_ASSERT(clsen != NULL);
+       assert(clsen);
 
-       for (lden = clsen->loaders; lden != NULL; lden = next) {
+       for (lden = clsen->loaders; lden; lden = next) {
                next = lden->next;
                FREE(lden, classcache_loader_entry);
        }
-       for (lden = clsen->constraints; lden != NULL; lden = next) {
+       for (lden = clsen->constraints; lden; lden = next) {
                next = lden->next;
                FREE(lden, classcache_loader_entry);
        }
@@ -738,15 +1069,13 @@ classcache_free_class_entry(
   
 *******************************************************************************/
 
-static void
-classcache_remove_class_entry(
-       classcache_name_entry * entry,
-       classcache_class_entry * clsen)
+static void classcache_remove_class_entry(classcache_name_entry * entry,
+                                                                                 classcache_class_entry * clsen)
 {
        classcache_class_entry **chain;
 
-       CLASSCACHE_ASSERT(entry != NULL);
-       CLASSCACHE_ASSERT(clsen != NULL);
+       assert(entry);
+       assert(clsen);
 
        chain = &(entry->classes);
        while (*chain) {
@@ -768,14 +1097,12 @@ classcache_remove_class_entry(
           
 *******************************************************************************/
 
-static void
-classcache_free_name_entry(
-       classcache_name_entry * entry)
+static void classcache_free_name_entry(classcache_name_entry * entry)
 {
        classcache_class_entry *clsen;
        classcache_class_entry *next;
 
-       CLASSCACHE_ASSERT(entry != NULL);
+       assert(entry);
 
        for (clsen = entry->classes; clsen; clsen = next) {
                next = clsen->next;
@@ -797,26 +1124,23 @@ classcache_free_name_entry(
   
 *******************************************************************************/
 
-void
-classcache_free(
-       )
-       /*@globals killed classcache_hash@*/
+void classcache_free(void)
 {
        u4 slot;
        classcache_name_entry *entry;
        classcache_name_entry *next;
 
-       for (slot = 0; slot < classcache_hash.size; ++slot) {
-               for (entry = (classcache_name_entry *) classcache_hash.ptr[slot]; entry; entry = next) {
+       for (slot = 0; slot < hashtable_classcache.size; ++slot) {
+               for (entry = (classcache_name_entry *) hashtable_classcache.ptr[slot]; entry; entry = next) {
                        next = entry->hashlink;
                        classcache_free_name_entry(entry);
                }
        }
 
-       MFREE(classcache_hash.ptr, voidptr, classcache_hash.size);
-       classcache_hash.size = 0;
-       classcache_hash.entries = 0;
-       classcache_hash.ptr = NULL;
+       MFREE(hashtable_classcache.ptr, void*, hashtable_classcache.size);
+       hashtable_classcache.size = 0;
+       hashtable_classcache.entries = 0;
+       hashtable_classcache.ptr = NULL;
 }
 
 /* classcache_add_constraint ***************************************************
@@ -836,68 +1160,62 @@ classcache_free(
    
 *******************************************************************************/
 
-bool
-classcache_add_constraint(
-       /*@shared@*/ /*@null@*/ classloader * a,
-       /*@shared@*/ /*@null@*/ classloader * b,
-       /*@shared@*/ utf * classname)
+#if defined(ENABLE_VERIFIER)
+bool classcache_add_constraint(classloader_t * a,
+                                                          classloader_t * b,
+                                                          utf * classname)
 {
        classcache_name_entry *en;
        classcache_class_entry *clsenA;
        classcache_class_entry *clsenB;
 
-       CLASSCACHE_ASSERT(classname != NULL);
+       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);
 
-       CLASSCACHE_ASSERT(en != NULL);
+       assert(en);
+       CLASSCACHE_COUNT(stat_nontriv_constraints);
 
        /* find the entry loaded by / constrained to each loader */
        clsenA = classcache_find_loader(en, a);
        clsenB = classcache_find_loader(en, b);
 
-       if (clsenA != NULL && clsenB != NULL) {
+       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)
                        goto return_success;
 
                /* check if the entries can be merged */
-               if (clsenA->classobj != NULL && clsenB->classobj != NULL
+               if (clsenA->classobj && clsenB->classobj
                        && clsenA->classobj != clsenB->classobj) {
                        /* no, the constraint is violated */
-                       *exceptionptr = new_exception_message(string_java_lang_LinkageError,
-                                                                                                 "loading constraint violated XXX add message");
+                       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 } */
@@ -908,6 +1226,7 @@ classcache_add_constraint(
 
                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);
@@ -920,6 +1239,8 @@ classcache_add_constraint(
                        en->classes = clsenA;
                }
                else {
+                       CLASSCACHE_COUNT(stat_nontriv_constraints_one);
+
                        /* make b the loader that has no corresponding entry */
                        if (clsenB)
                                b = a;
@@ -937,6 +1258,237 @@ classcache_add_constraint(
        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                                                                */
@@ -948,14 +1500,16 @@ classcache_add_constraint(
   
    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
    
 *******************************************************************************/
 
-void
-classcache_debug_dump(
-       /*@shared@*/ FILE * file)
+#ifndef NDEBUG
+void classcache_debug_dump(FILE * file,utf *only)
 {
        classcache_name_entry *c;
        classcache_class_entry *clsen;
@@ -964,42 +1518,58 @@ classcache_debug_dump(
 
        CLASSCACHE_LOCK();
 
-       fprintf(file, "\n=== [loaded class cache] =====================================\n\n");
-       fprintf(file, "hash size   : %d\n", (int) classcache_hash.size);
-       fprintf(file, "hash entries: %d\n", (int) classcache_hash.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("");
 
-       for (slot = 0; slot < classcache_hash.size; ++slot) {
-               c = (classcache_name_entry *) classcache_hash.ptr[slot];
+       if (only) {
+               c = classcache_lookup_name(only);
+               slot = 0; /* avoid compiler warning */
+               goto dump_it;
+       }
 
-               for (; c != NULL; c = c->hashlink) {
-                       utf_fprint_classname(file, c->name);
+       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_printable_ascii_classname(file, c->name);
                        fprintf(file, "\n");
 
                        /* iterate over all class entries */
-                       for (clsen = c->classes; clsen != NULL; clsen = clsen->next) {
+                       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:");
-                               for (lden = clsen->loaders; lden != NULL; lden = lden->next) {
-                                       fprintf(file, "<%p> %p", (void *) lden, (void *) lden->loader);
+
+                               log_start();
+                               log_print("        loaders: ");
+                               for (lden = clsen->loaders; lden; lden = lden->next) {
+                                       log_print("<%p> %p ", (void *) lden, (void *) lden->loader);
                                }
-                               fprintf(file, "\n        constraints:");
-                               for (lden = clsen->constraints; lden != NULL; lden = lden->next) {
-                                       fprintf(file, "<%p> %p", (void *) lden, (void *) lden->loader);
+                               log_finish();
+
+                               log_start();
+                               log_print("        constraints: ");
+                               for (lden = clsen->constraints; lden; lden = lden->next) {
+                                       log_print("<%p> %p ", (void *) lden, (void *) lden->loader);
                                }
-                               fprintf(file, "\n");
+                               log_finish();
                        }
                }
+
+               if (only)
+                       break;
        }
        fprintf(file, "\n==============================================================\n\n");
 
        CLASSCACHE_UNLOCK();
 }
+#endif /* NDEBUG */
 
 /*
  * These are local overrides for various environment variables in Emacs.