added new code for the loaded class cache and loading constraints
authoredwin <none@none>
Fri, 25 Mar 2005 12:34:09 +0000 (12:34 +0000)
committeredwin <none@none>
Fri, 25 Mar 2005 12:34:09 +0000 (12:34 +0000)
src/vm/classcache.c [new file with mode: 0644]
src/vm/classcache.h [new file with mode: 0644]

diff --git a/src/vm/classcache.c b/src/vm/classcache.c
new file mode 100644 (file)
index 0000000..11ee6cb
--- /dev/null
@@ -0,0 +1,748 @@
+/* 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
+
+   This file is part of CACAO.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.
+
+   Contact: cacao@complang.tuwien.ac.at
+
+   Authors: Edwin Steiner
+
+   Changes:
+
+   $Id: classcache.c 2076 2005-03-25 12:34:09Z edwin $
+
+*/
+
+#include <assert.h>
+#include "vm/classcache.h"
+#include "vm/tables.h"
+#include "vm/utf8.h"
+#include "vm/exceptions.h"
+#include "mm/memory.h"
+
+/* initial number of slots in the classcache hash table */
+#define CLASSCACHE_INIT_SIZE  2048
+
+/*============================================================================*/
+/* DEBUG HELPERS                                                              */
+/*============================================================================*/
+
+#ifndef NDEBUG
+#define CLASSCACHE_DEBUG
+#endif
+
+#ifdef CLASSCACHE_DEBUG
+#define CLASSCACHE_ASSERT(cond)  assert(cond)
+#else
+#define CLASSCACHE_ASSERT(cond)
+#endif
+
+/*============================================================================*/
+/* GLOBAL VARIABLES                                                           */
+/*============================================================================*/
+
+static hashtable classcache_hash;
+
+/*============================================================================*/
+/*                                                                            */
+/*============================================================================*/
+
+/* classcache_init *************************************************************
+   Initialize the loaded class cache
+  
+*******************************************************************************/
+
+void 
+classcache_init()
+{
+       init_hashtable(&classcache_hash,CLASSCACHE_INIT_SIZE);
+}
+
+/* classcache_new_loader_entry *************************************************
+   Create a new classcache_loader_entry struct
+   (internally used helper function)
+  
+   IN:
+       loader...........the ClassLoader object
+          next.............the next classcache_loader_entry
+
+   RETURN VALUE:
+       the new classcache_loader_entry
+  
+*******************************************************************************/
+
+static classcache_loader_entry *
+classcache_new_loader_entry(classloader *loader,classcache_loader_entry *next)
+{
+       classcache_loader_entry *lden;
+
+       lden = NEW(classcache_loader_entry);
+       lden->loader = loader;
+       lden->next = next;
+
+       return lden;
+}
+
+/* classcache_merge_loaders ****************************************************
+   Merge two lists of loaders into one
+   (internally used helper function)
+  
+   IN:
+       lista............first list (may be NULL)
+          listb............second list (may be NULL)
+
+   RETURN VALUE:
+       the merged list (may be NULL)
+
+   NOTE:
+       The lists given as arguments are destroyed!
+  
+*******************************************************************************/
+
+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;
+       
+       /* 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. */
+
+       result = NULL;
+       chain = &result;
+
+       for (ldenA=lista; ldenA; ldenA=ldenA->next) {
+               
+               for (ldenB=listb; ldenB; ldenB=ldenB->next) {
+                       if (ldenB->loader == ldenA->loader)
+                               goto common_element;
+               }
+
+               /* this loader is only in lista */
+               *chain = ldenA;
+               chain = &(ldenA->next);
+
+common_element:
+               /* XXX free the duplicated element */
+               ;
+       }
+
+       /* concat listb to the result */
+       *chain = listb;
+
+       return result;
+}
+
+/* classcache_lookup_name ******************************************************
+   Lookup a name in the first level of the cache
+   (internally used helper function)
+   
+   IN:
+       name.............the name to look up
+  
+   RETURN VALUE:
+       a pointer to the classcache_name_entry for this name, or
+       null if no entry was found.
+          
+*******************************************************************************/
+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               */
+       u2 i;
+
+       key  = utf_hashkey(name->text, name->blength);
+       slot = key & (classcache_hash.size - 1);
+       c    = classcache_hash.ptr[slot];
+
+       /* search external hash chain for the entry */
+       while (c) {
+               if (c->name->blength == name->blength) {
+                       for (i = 0; i < name->blength; i++)
+                               if (name->text[i] != c->name->text[i]) goto nomatch;
+                                               
+                       /* entry found in hashtable */
+                       return c;
+               }
+                       
+       nomatch:
+               c = c->hashlink; /* next element in external chain */
+       }
+
+       /* not found */
+       return NULL;
+}
+
+/* classcache_new_name *********************************************************
+   Return a classcache_name_entry for the given name. The entry is created
+   if it is not already in the cache.
+   (internally used helper function)
+   
+   IN:
+       name.............the name to look up / create an entry for
+  
+   RETURN VALUE:
+       a pointer to the classcache_name_entry for this 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               */
+       u2 i;
+
+       key  = utf_hashkey(name->text, name->blength);
+       slot = key & (classcache_hash.size - 1);
+       c    = classcache_hash.ptr[slot];
+
+       /* search external hash chain for the entry */
+       while (c) {
+               if (c->name->blength == name->blength) {
+                       for (i = 0; i < name->blength; i++)
+                               if (name->text[i] != c->name->text[i]) goto nomatch;
+                                               
+                       /* entry found in hashtable */
+                       return c;
+               }
+                       
+       nomatch:
+               c = c->hashlink; /* next element in external chain */
+       }
+
+       /* location in hashtable found, create new entry */
+
+       c = NEW(classcache_name_entry);
+
+       c->name = name;
+       c->classes = NULL;
+
+       /* insert entry into hashtable */
+       c->hashlink = classcache_hash.ptr[slot];
+       classcache_hash.ptr[slot] = c;
+
+       /* update number of hashtable-entries */
+       classcache_hash.entries++;
+
+       if (classcache_hash.entries > (classcache_hash.size * 2)) {
+
+               /* reorganization of hashtable, average length of 
+                  the external chains is approx. 2                */  
+
+               u4 i;
+               classcache_name_entry *c;
+               hashtable newhash;  /* the new hashtable */
+
+               /* create new hashtable, double the size */
+               init_hashtable(&newhash, classcache_hash.size * 2);
+               newhash.entries = classcache_hash.entries;
+
+               /* transfer elements to new hashtable */
+               for (i = 0; i < classcache_hash.size; i++) {
+                       c = (classcache_name_entry *) classcache_hash.ptr[i];
+                       while (c) {
+                               classcache_name_entry *nextc = c->hashlink;
+                               u4 slot = (utf_hashkey(c->name->text, c->name->blength)) & (newhash.size - 1);
+                                               
+                               c->hashlink = newhash.ptr[slot];
+                               newhash.ptr[slot] = c;
+
+                               c = nextc;
+                       }
+               }
+       
+               /* dispose old table */ 
+               MFREE(classcache_hash.ptr, void*, classcache_hash.size);
+               classcache_hash = newhash;
+       }
+
+       return c;
+}
+
+/* classcache_lookup ***********************************************************
+   Lookup a possibly loaded class
+  
+   IN:
+       initloader.......initiating loader for resolving the class name
+       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.
+   
+*******************************************************************************/
+
+classinfo *
+classcache_lookup(classloader *initloader,utf *classname)
+{
+       classcache_name_entry *en;
+       classcache_class_entry *clsen;
+       classcache_loader_entry *lden;
+
+       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 loaded by initloader */
+                       for (lden=clsen->loaders; lden; lden=lden->next) {
+                               if (lden->loader == initloader) {
+                                       /* found the loaded class entry */
+                                       CLASSCACHE_ASSERT(clsen->classobj);
+                                       return clsen->classobj;
+                               }
+                       }
+               }
+       }
+       
+       /* not found */
+       return NULL;
+}
+
+/* classcache_store ************************************************************
+   
+   Store a loaded class
+  
+   IN:
+       initloader.......initiating loader used to load the class
+       cls..............class object to cache
+  
+   RETURN VALUE:
+       true.............everything ok, the class was stored in
+                        the cache if necessary,
+       false............an exception has been thrown.
+   
+*******************************************************************************/
+
+bool
+classcache_store(classloader *initloader,classinfo *cls)
+{
+       classcache_name_entry *en;
+       classcache_class_entry *clsen;
+       classcache_loader_entry *lden;
+
+       CLASSCACHE_ASSERT(cls);
+
+       /* XXX lock table */
+
+       en = classcache_new_name(cls->name);
+
+       CLASSCACHE_ASSERT(en);
+       
+       /* iterate over all class entries */
+       for (clsen=en->classes; clsen; clsen=clsen->next) {
+               
+#ifdef CLASSCACHE_DEBUG
+               /* check if this entry has already been loaded by initloader */
+               /* It never should have been loaded before! */
+               for (lden=clsen->loaders; lden; lden=lden->next) {
+                       if (lden->loader == initloader)
+                               CLASSCACHE_ASSERT(false);
+               }
+#endif
+
+               /* 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 = new_exception_message(string_java_lang_LinkageError,
+                                                       "loading constraint violated XXX add message");
+                                       return false; /* exception */
+                               }
+
+                               /* record initloader as initiating loader */
+                               clsen->loaders = classcache_new_loader_entry(initloader,clsen->loaders);
+
+                               /* record the loaded class object */
+                               clsen->classobj = cls;
+
+                               /* done */
+                               return true;
+                       }
+               }
+               
+       }
+
+       /* create a new class entry for this class object with */
+       /* initiating loader initloader                        */
+
+       clsen = NEW(classcache_class_entry);
+       clsen->classobj = cls;
+       clsen->loaders = classcache_new_loader_entry(initloader,NULL);
+       clsen->constraints = NULL;
+
+       clsen->next = en->classes;
+       en->classes = clsen;
+
+       /* done */
+       return true;
+}
+
+/* classcache_find_loader ******************************************************
+   Find the class entry loaded by or constrained to a given loader
+   (internally used helper function)
+  
+   IN:
+       entry............the classcache_name_entry
+       loader...........the loader to look for
+  
+   RETURN VALUE:
+       the classcache_class_entry for the given loader, or
+          NULL if no entry was found
+   
+*******************************************************************************/
+
+static classcache_class_entry *
+classcache_find_loader(classcache_name_entry *entry,classloader *loader)
+{
+       classcache_class_entry *clsen;
+       classcache_loader_entry *lden;
+       
+       CLASSCACHE_ASSERT(entry);
+
+       /* iterate over all class entries */
+       for (clsen=entry->classes; clsen; clsen=clsen->next) {
+               
+               /* check if this entry has already been loaded by initloader */
+               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; lden=lden->next) {
+                       if (lden->loader == loader)
+                               return clsen; /* found */
+               }
+       }
+
+       /* not found */
+       return NULL;
+}
+
+/* classcache_free_class_entry *************************************************
+   Free the memory used by a class entry
+  
+   IN:
+       clsen............the classcache_class_entry to free  
+          
+*******************************************************************************/
+
+static void
+classcache_free_class_entry(classcache_class_entry *clsen)
+{
+       classcache_loader_entry *lden;
+       classcache_loader_entry *next;
+       
+       CLASSCACHE_ASSERT(clsen);
+
+       for (lden=clsen->loaders; lden; lden=next) {
+               next = lden->next;
+               FREE(lden,classcache_loader_entry);
+       }
+       for (lden=clsen->constraints; lden; lden=next) {
+               next = lden->next;
+               FREE(lden,classcache_loader_entry);
+       }
+
+       FREE(clsen,classcache_class_entry);
+}
+
+/* classcache_remove_class_entry ***********************************************
+   Remove a classcache_class_entry from the list of possible resolution of
+   a name entry
+   (internally used helper function)
+  
+   IN:
+       entry............the classcache_name_entry
+       clsen............the classcache_class_entry to remove
+  
+*******************************************************************************/
+
+static void
+classcache_remove_class_entry(classcache_name_entry *entry,
+                                                         classcache_class_entry *clsen)
+{
+       classcache_class_entry **chain;
+
+       CLASSCACHE_ASSERT(entry);
+       CLASSCACHE_ASSERT(clsen);
+
+       chain = &(entry->classes);
+       while (*chain) {
+               if (*chain == clsen) {
+                       *chain = clsen->next;
+                       classcache_free_class_entry(clsen);
+                       return;
+               }
+               chain = &((*chain)->next);
+       }
+}
+
+/* classcache_free_name_entry **************************************************
+   Free the memory used by a name entry
+  
+   IN:
+       entry............the classcache_name_entry to free  
+          
+*******************************************************************************/
+
+static void
+classcache_free_name_entry(classcache_name_entry *entry)
+{
+       classcache_class_entry *clsen;
+       classcache_class_entry *next;
+       
+       CLASSCACHE_ASSERT(entry);
+
+       for (clsen=entry->classes; clsen; clsen=next) {
+               next = clsen->next;
+               classcache_free_class_entry(clsen);
+       }
+
+       FREE(entry,classcache_name_entry);
+}
+
+/* classcache_free *************************************************************
+   Free the memory used by the class cache
+
+   NOTE:
+       The class cache may not be used any more after this call, except
+          when it is reinitialized with classcache_init.
+  
+*******************************************************************************/
+
+void 
+classcache_free()
+{
+       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)
+               {
+                       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;
+}
+
+/* classcache_add_constraint ***************************************************
+   Add a loading constraint
+  
+   IN:
+       a................first initiating loader
+       b................second initiating loader
+       classname........class name
+  
+   RETURN VALUE:
+       true.............everything ok, the constraint has been added,
+       false............an exception has been thrown.
+   
+*******************************************************************************/
+
+bool
+classcache_add_constraint(classloader *a,classloader *b,utf *classname)
+{
+       classcache_name_entry *en;
+       classcache_class_entry *clsenA;
+       classcache_class_entry *clsenB;
+
+       CLASSCACHE_ASSERT(classname);
+
+#ifdef CLASSCACHE_DEBUG
+       fprintf(stderr,"classcache_add_constraint(%p,%p,",a,b);
+       utf_fprint_classname(stderr,classname);
+       fprintf(stderr,")\n");
+#endif
+
+       /* a constraint with a == b is trivially satisfied */
+       if (a == b)
+               return true;
+
+       /* XXX lock table */
+
+       en = classcache_new_name(classname);
+
+       CLASSCACHE_ASSERT(en);
+       
+       /* find the entry loaded by / constrained to each loader */
+       clsenA = classcache_find_loader(en,a);
+       clsenB = classcache_find_loader(en,b);
+
+       if (clsenA && clsenB) {
+               /* { both loaders have corresponding entries } */
+
+               /* if the entries are the same, the constraint is already recorded */
+               if (clsenA == clsenB)
+                       return true;
+
+               /* check if the entries can be merged */
+               if (clsenA->classobj != clsenB->classobj) {
+                       /* no, the constraint is violated */
+                       *exceptionptr = new_exception_message(string_java_lang_LinkageError,
+                                       "loading constraint violated XXX add message");
+                       return false; /* exception */
+               }
+
+               /* yes, merge the entries */
+               /* clsenB will be merged into clsenA */
+               clsenA->loaders = classcache_merge_loaders(clsenA->loaders,
+                                                                                                  clsenB->loaders);
+               
+               clsenA->constraints = classcache_merge_loaders(clsenA->constraints,
+                                                                                                          clsenB->constraints);
+
+               /* remove clsenB from the list of class entries */
+               classcache_remove_class_entry(en,clsenB);
+       }
+       else {
+               /* { at most one of the loaders has a corresponding entry } */
+
+               /* set clsenA to the single class entry we have */
+               if (!clsenA)
+                       clsenA = clsenB;
+
+               if (!clsenA) {
+                       /* { no loader has a corresponding entry } */
+
+                       /* create a new class entry with the constraint (a,b,en->name) */
+                       clsenA = NEW(classcache_class_entry);
+                       clsenA->classobj = NULL;
+                       clsenA->loaders = NULL;
+                       clsenA->constraints = classcache_new_loader_entry(b,NULL);
+                       clsenA->constraints = classcache_new_loader_entry(a,clsenA->constraints);
+
+                       clsenA->next = en->classes;
+                       en->classes = clsenA;
+               }
+               else {
+                       /* make b the loader that has no corresponding entry */
+                       if (clsenB)
+                               b = a;
+                       
+                       /* loader b must be added to entry clsenA */
+                       clsenA->constraints = classcache_new_loader_entry(b,clsenA->constraints);
+               }
+       }
+
+       /* done */
+       return true;
+}
+
+/*============================================================================*/
+/* DEBUG DUMPS                                                                */
+/*============================================================================*/
+
+/* classcache_debug_dump *******************************************************
+   Print the contents of the loaded class cache to a stream
+  
+   IN:
+       file.............output stream
+  
+*******************************************************************************/
+
+void
+classcache_debug_dump(FILE *file)
+{
+       classcache_name_entry *c;
+       classcache_class_entry *clsen;
+       classcache_loader_entry *lden;
+       u4 slot;
+
+       fprintf(file,"\n=== [loaded class cache] =====================================\n\n");
+       fprintf(file,"hash size   : %d\n",classcache_hash.size);
+       fprintf(file,"hash entries: %d\n",classcache_hash.entries);
+       fprintf(file,"\n");
+
+       for (slot=0; slot<classcache_hash.size; ++slot) {
+               c = (classcache_name_entry *) classcache_hash.ptr[slot];
+
+               for (; c; c=c->hashlink) {
+                       utf_fprint_classname(file,c->name);
+                       fprintf(file,"\n");
+
+                       /* iterate over all class entries */
+                       for (clsen=c->classes; clsen; clsen=clsen->next) {
+                               fprintf(file,"    %s\n",(clsen->classobj) ? "loaded" : "unresolved");
+                               fprintf(file,"    loaders:");
+                               for (lden=clsen->loaders; lden; lden=lden->next) {
+                                       fprintf(file," %p",(void *)lden->loader);
+                               }
+                               fprintf(file,"\n    constraints:");
+                               for (lden=clsen->constraints; lden; lden=lden->next) {
+                                       fprintf(file," %p",(void *)lden->loader);
+                               }
+                               fprintf(file,"\n");
+                       }
+               }
+       }
+       fprintf(file,"\n==============================================================\n\n");
+}
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+
diff --git a/src/vm/classcache.h b/src/vm/classcache.h
new file mode 100644 (file)
index 0000000..63660b9
--- /dev/null
@@ -0,0 +1,208 @@
+/* vm/classcache.h - 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
+
+   This file is part of CACAO.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.
+
+   Contact: cacao@complang.tuwien.ac.at
+
+   Authors: Edwin Steiner
+
+   Changes:
+
+   $Id: classcache.h 2076 2005-03-25 12:34:09Z edwin $
+
+*/
+
+#ifndef _CLASSCACHE_H
+#define _CLASSCACHE_H
+
+#include <stdio.h>  /* for FILE */
+#include "vm/global.h"
+
+/* forward declarations *******************************************************/
+
+typedef struct classcache_name_entry classcache_name_entry;
+typedef struct classcache_class_entry classcache_class_entry;
+typedef struct classcache_loader_entry classcache_loader_entry;
+
+typedef java_objectheader classloader;
+
+/* structs ********************************************************************/
+
+
+/*----------------------------------------------------------------------------*/
+/* The Loaded Class Cache                                                     */
+/*                                                                            */
+/* The loaded class cache is implemented as a two-level data structure.       */
+/*                                                                            */
+/* The first level is a hash table indexed by class names. For each class     */
+/* name in the cache there is a classcache_name_entry, which collects all     */
+/* information about classes with this class name.                            */
+/*                                                                            */
+/* Second level: For each classcache_name_entry there is a list of            */
+/* classcache_class_entry:s representing the possible different resolutions   */
+/* of the class name.                                                         */
+/*                                                                            */
+/* A classcache_class_entry records the following:                            */
+/*                                                                            */
+/* - the loaded class object, if this entry has been resolved, otherwise NULL */
+/* - the list of initiating loaders which have resolved the class name to     */
+/*   this class object                                                        */
+/* - the list of initiating loaders which are constrained to resolve this     */
+/*   class name to this class object in the future                            */
+/*                                                                            */
+/* The classcache_class_entry:s approximate the equivalence classes created   */
+/* by the loading constraints and (XXX?) the equivalence of loaded classes.   */
+/*                                                                            */
+/* When a loading constraint (loaderA,loaderB,NAME) is added, then the        */
+/* classcache_class_entry:s for NAME containing loaderA and loaderB resp.     */
+/* must be merged into one entry. If this is impossible, because the entries  */
+/* have already been resolved to different class objects, then the constraint */
+/* is violated and an expception must be thrown.                              */
+/*----------------------------------------------------------------------------*/
+
+
+/* classcache_name_entry
+ *
+ * For each classname a classcache_name_entry struct is created.
+ */
+
+struct classcache_name_entry
+{
+       utf                     *name;        /* class name                       */
+       classcache_name_entry   *hashlink;        /* link for external chaining       */
+       classcache_class_entry  *classes;     /* equivalence classes for this name*/
+};
+
+struct classcache_class_entry
+{
+       classinfo               *classobj;    /* the loaded class object, or NULL */
+       classcache_loader_entry *loaders;
+       classcache_loader_entry *constraints;
+       classcache_class_entry  *next;        /* next class entry for same name   */
+};
+
+struct classcache_loader_entry
+{
+       classloader              *loader;     /* class loader object              */
+       classcache_loader_entry  *next;       /* next loader entry in the list    */
+};
+
+/* function prototypes ********************************************************/
+
+/* classcache_init *************************************************************
+   Initialize the loaded class cache
+  
+*******************************************************************************/
+
+void classcache_init();
+
+/* classcache_free *************************************************************
+   Free the memory used by the class cache
+
+   NOTE:
+       The class cache may not be used any more after this call, except
+          when it is reinitialized with classcache_init.
+  
+*******************************************************************************/
+
+void classcache_free();
+
+/* classcache_lookup ***********************************************************
+   Lookup a possibly loaded class
+  
+   IN:
+       initloader.......initiating loader for resolving the class name
+       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.
+   
+*******************************************************************************/
+
+classinfo * classcache_lookup(classloader *initloader,utf *classname);
+
+/* classcache_store ************************************************************
+   
+   Store a loaded class
+  
+   IN:
+       initloader.......initiating loader used to load the class
+       cls..............class object to cache
+  
+   RETURN VALUE:
+       true.............everything ok, the class was stored in
+                        the cache if necessary,
+       false............an exception has been thrown.
+   
+*******************************************************************************/
+
+bool classcache_store(classloader *initloader,classinfo *cls);
+
+/* classcache_add_constraint ***************************************************
+   Add a loading constraint
+  
+   IN:
+       a................first initiating loader
+       b................second initiating loader
+       classname........class name
+  
+   RETURN VALUE:
+       true.............everything ok, the constraint has been added,
+       false............an exception has been thrown.
+   
+*******************************************************************************/
+
+bool classcache_add_constraint(classloader *a,classloader *b,utf *classname);
+
+/* classcache_debug_dump *******************************************************
+   Print the contents of the loaded class cache to a stream
+  
+   IN:
+       file.............output stream
+  
+*******************************************************************************/
+
+void classcache_debug_dump(FILE *file);
+       
+#endif /* _CLASSCACHE_H */
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+