Moved global string definitions to string.c for 2 reasons: it seems to be
[cacao.git] / src / vm / classcache.c
1 /* src/vm/classcache.c - loaded class cache and loading constraints
2
3    Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates,
4    R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
5    C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
6    Institut f. Computersprachen - TU Wien
7
8    This file is part of CACAO.
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2, or (at
13    your option) any later version.
14
15    This program is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23    02111-1307, USA.
24
25    Contact: cacao@complang.tuwien.ac.at
26
27    Authors: Edwin Steiner
28
29    Changes: Christian Thalinger
30
31    $Id: classcache.c 2458 2005-05-12 23:02:07Z twisti $
32
33 */
34
35
36 #include <assert.h>
37
38 #include "mm/memory.h"
39 #include "vm/classcache.h"
40 #include "vm/exceptions.h"
41 #include "vm/stringlocal.h"
42 #include "vm/tables.h"
43 #include "vm/utf8.h"
44
45
46 /* initial number of slots in the classcache hash table */
47 #define CLASSCACHE_INIT_SIZE  2048
48
49 /*============================================================================*/
50 /* DEBUG HELPERS                                                              */
51 /*============================================================================*/
52
53 /*#define CLASSCACHE_VERBOSE*/
54
55 #ifndef NDEBUG
56 #define CLASSCACHE_DEBUG
57 #endif
58
59 #ifdef CLASSCACHE_DEBUG
60 #define CLASSCACHE_ASSERT(cond)  assert(cond)
61 #else
62 #define CLASSCACHE_ASSERT(cond)
63 #endif
64
65 /*============================================================================*/
66 /* THREAD-SAFE LOCKING                                                        */
67 /*============================================================================*/
68
69         /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
70         /* CAUTION: The static functions below are */
71         /*          NOT synchronized!              */
72         /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
73
74 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
75 #  define CLASSCACHE_LOCK()    tables_lock()
76 #  define CLASSCACHE_UNLOCK()  tables_unlock()
77 #else
78 #  define CLASSCACHE_LOCK()
79 #  define CLASSCACHE_UNLOCK()
80 #endif
81
82 /*============================================================================*/
83 /* GLOBAL VARIABLES                                                           */
84 /*============================================================================*/
85
86 hashtable classcache_hash;
87
88 /*============================================================================*/
89 /*                                                                            */
90 /*============================================================================*/
91
92 /* classcache_init *************************************************************
93  
94    Initialize the loaded class cache
95
96    Note: NOT synchronized!
97   
98 *******************************************************************************/
99
100 void
101 classcache_init(
102         )
103 {
104         init_hashtable(&classcache_hash, CLASSCACHE_INIT_SIZE);
105 }
106
107 /* classcache_new_loader_entry *************************************************
108  
109    Create a new classcache_loader_entry struct
110    (internally used helper function)
111   
112    IN:
113        loader...........the ClassLoader object
114            next.............the next classcache_loader_entry
115
116    RETURN VALUE:
117        the new classcache_loader_entry
118   
119 *******************************************************************************/
120
121 static classcache_loader_entry *
122 classcache_new_loader_entry(
123         /*@shared@*/ /*@null@*/ classloader * loader,
124         /*@only  @*/ /*@null@*/ classcache_loader_entry * next)
125 {
126         classcache_loader_entry *lden;
127
128         lden = NEW(classcache_loader_entry);
129         lden->loader = loader;
130         lden->next = next;
131
132         return lden;
133 }
134
135 /* classcache_merge_loaders ****************************************************
136  
137    Merge two lists of loaders into one
138    (internally used helper function)
139   
140    IN:
141        lista............first list (may be NULL)
142            listb............second list (may be NULL)
143
144    RETURN VALUE:
145        the merged list (may be NULL)
146
147    NOTE:
148        The lists given as arguments are destroyed!
149   
150 *******************************************************************************/
151
152 static /*@null@*/ classcache_loader_entry *
153 classcache_merge_loaders(
154         /*@null@*/ classcache_loader_entry * lista,
155         /*@null@*/ classcache_loader_entry * listb)
156 {
157         classcache_loader_entry *result;
158         classcache_loader_entry *ldenA;
159         classcache_loader_entry *ldenB;
160         classcache_loader_entry **chain;
161
162         /* XXX This is a quadratic algorithm. If this ever
163          * becomes a problem, the loader lists should be
164          * stored as sorted lists and merged in linear time. */
165
166         result = NULL;
167         chain = &result;
168
169         for (ldenA = lista; ldenA != NULL; ldenA = ldenA->next) {
170
171                 for (ldenB = listb; ldenB != NULL; ldenB = ldenB->next) {
172                         if (ldenB->loader == ldenA->loader)
173                                 goto common_element;
174                 }
175
176                 /* this loader is only in lista */
177                 *chain = ldenA;
178                 chain = &(ldenA->next);
179
180           common_element:
181                 /* XXX free the duplicated element */
182                 ;
183         }
184
185         /* concat listb to the result */
186         *chain = listb;
187
188         return result;
189 }
190
191 /* classcache_lookup_name ******************************************************
192  
193    Lookup a name in the first level of the cache
194    (internally used helper function)
195    
196    IN:
197        name.............the name to look up
198   
199    RETURN VALUE:
200        a pointer to the classcache_name_entry for this name, or
201        null if no entry was found.
202            
203 *******************************************************************************/
204
205 static /*@exposed@*/ /*@null@*/ classcache_name_entry *
206 classcache_lookup_name(
207         /*@shared@*/ utf * name)
208 {
209         classcache_name_entry *c;       /* hash table element */
210         u4 key;                                         /* hashkey computed from classname */
211         u4 slot;                                        /* slot in hashtable               */
212         u4 i;
213
214         key = utf_hashkey(name->text, (u4) name->blength);
215         slot = key & (classcache_hash.size - 1);
216         c = classcache_hash.ptr[slot];
217
218         /* search external hash chain for the entry */
219         while (c) {
220                 if (c->name->blength == name->blength) {
221                         for (i = 0; i < (u4) name->blength; i++)
222                                 if (name->text[i] != c->name->text[i])
223                                         goto nomatch;
224
225                         /* entry found in hashtable */
226                         return c;
227                 }
228
229           nomatch:
230                 c = c->hashlink;                /* next element in external chain */
231         }
232
233         /* not found */
234         return NULL;
235 }
236
237 /* classcache_new_name *********************************************************
238  
239    Return a classcache_name_entry for the given name. The entry is created
240    if it is not already in the cache.
241    (internally used helper function)
242    
243    IN:
244        name.............the name to look up / create an entry for
245   
246    RETURN VALUE:
247        a pointer to the classcache_name_entry for this name
248            
249 *******************************************************************************/
250
251 static /*@exposed@*/ classcache_name_entry *
252 classcache_new_name(
253         /*@shared@*/ utf * name)
254 {
255         classcache_name_entry *c;       /* hash table element */
256         u4 key;                                         /* hashkey computed from classname */
257         u4 slot;                                        /* slot in hashtable               */
258         u4 i;
259
260         key = utf_hashkey(name->text, (u4) name->blength);
261         slot = key & (classcache_hash.size - 1);
262         c = classcache_hash.ptr[slot];
263
264         /* search external hash chain for the entry */
265         while (c) {
266                 if (c->name->blength == name->blength) {
267                         for (i = 0; i < (u4) name->blength; i++)
268                                 if (name->text[i] != c->name->text[i])
269                                         goto nomatch;
270
271                         /* entry found in hashtable */
272                         return c;
273                 }
274
275           nomatch:
276                 c = c->hashlink;                /* next element in external chain */
277         }
278
279         /* location in hashtable found, create new entry */
280
281         c = NEW(classcache_name_entry);
282
283         c->name = name;
284         c->classes = NULL;
285
286         /* insert entry into hashtable */
287         c->hashlink = (classcache_name_entry *) classcache_hash.ptr[slot];
288         classcache_hash.ptr[slot] = c;
289
290         /* update number of hashtable-entries */
291         classcache_hash.entries++;
292
293         if (classcache_hash.entries > (classcache_hash.size * 2)) {
294
295                 /* reorganization of hashtable, average length of 
296                    the external chains is approx. 2                */
297
298                 classcache_name_entry *c2;
299                 hashtable newhash;              /* the new hashtable */
300
301                 /* create new hashtable, double the size */
302                 init_hashtable(&newhash, classcache_hash.size * 2);
303                 newhash.entries = classcache_hash.entries;
304
305                 /* transfer elements to new hashtable */
306                 for (i = 0; i < classcache_hash.size; i++) {
307                         c2 = (classcache_name_entry *) classcache_hash.ptr[i];
308                         while (c2) {
309                                 classcache_name_entry *nextc = c2->hashlink;
310                                 u4 newslot =
311                                         (utf_hashkey(c2->name->text, (u4) c2->name->blength)) & (newhash.size - 1);
312
313                                 c2->hashlink = (classcache_name_entry *) newhash.ptr[newslot];
314                                 newhash.ptr[newslot] = c2;
315
316                                 c2 = nextc;
317                         }
318                 }
319
320                 /* dispose old table */
321                 MFREE(classcache_hash.ptr, void *,
322                           classcache_hash.size);
323                 classcache_hash = newhash;
324         }
325
326         return c;
327 }
328
329 /* classcache_lookup ***********************************************************
330  
331    Lookup a possibly loaded class
332   
333    IN:
334        initloader.......initiating loader for resolving the class name
335        classname........class name to look up
336   
337    RETURN VALUE:
338        The return value is a pointer to the cached class object,
339        or NULL, if the class is not in the cache.
340
341    Note: synchronized with global tablelock
342    
343 *******************************************************************************/
344
345 /*@null@*/ classinfo *
346 classcache_lookup(
347         /*@shared@*/ classloader * initloader,
348         /*@shared@*/ utf * classname)
349 {
350         classcache_name_entry *en;
351         classcache_class_entry *clsen;
352         classcache_loader_entry *lden;
353         classinfo *cls = NULL;
354
355         CLASSCACHE_LOCK();
356
357         en = classcache_lookup_name(classname);
358
359         if (en) {
360                 /* iterate over all class entries */
361                 for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
362                         /* check if this entry has been loaded by initloader */
363                         for (lden = clsen->loaders; lden != NULL; lden = lden->next) {
364                                 if (lden->loader == initloader) {
365                                         /* found the loaded class entry */
366                                         CLASSCACHE_ASSERT(clsen->classobj != NULL);
367                                         cls = clsen->classobj;
368                                         goto found;
369                                 }
370                         }
371                 }
372         }
373   found:
374         CLASSCACHE_UNLOCK();
375         return cls;
376 }
377
378 /* classcache_lookup_defined ***************************************************
379  
380    Lookup a class with the given name and defining loader
381   
382    IN:
383        defloader........defining loader
384        classname........class name
385   
386    RETURN VALUE:
387        The return value is a pointer to the cached class object,
388        or NULL, if the class is not in the cache.
389    
390 *******************************************************************************/
391
392 /*@null@*/ classinfo *
393 classcache_lookup_defined(
394         /*@shared@*/ classloader * defloader,
395         /*@shared@*/ utf * classname)
396 {
397         classcache_name_entry *en;
398         classcache_class_entry *clsen;
399         classinfo *cls = NULL;
400
401         CLASSCACHE_LOCK();
402
403         en = classcache_lookup_name(classname);
404
405         if (en) {
406                 /* iterate over all class entries */
407                 for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
408                         if (!clsen->classobj)
409                                 continue;
410
411                         /* check if this entry has been defined by defloader */
412                         if (clsen->classobj->classloader == defloader) {
413                                 cls = clsen->classobj;
414                                 goto found;
415                         }
416                 }
417         }
418   found:
419         CLASSCACHE_UNLOCK();
420         return cls;
421 }
422
423 /* classcache_store ************************************************************
424    
425    Store a loaded class
426   
427    IN:
428        initloader.......initiating loader used to load the class
429        cls..............class object to cache
430   
431    RETURN VALUE:
432        true.............everything ok, the class was stored in
433                         the cache if necessary,
434        false............an exception has been thrown.
435    
436    Note: synchronized with global tablelock
437    
438 *******************************************************************************/
439
440 bool
441 classcache_store(
442         /*@shared@*/ /*@null@*/ classloader * initloader,
443         /*@shared@*/ classinfo * cls)
444 {
445         classcache_name_entry *en;
446         classcache_class_entry *clsen;
447         classcache_loader_entry *lden;
448
449         CLASSCACHE_ASSERT(cls != NULL);
450         CLASSCACHE_ASSERT(cls->loaded != 0);
451
452 #ifdef CLASSCACHE_VERBOSE
453         fprintf(stderr, "classcache_store(%p,", initloader);
454         utf_fprint_classname(stderr, cls->name);
455         fprintf(stderr, ")\n");
456 #endif
457
458         CLASSCACHE_LOCK();
459
460         en = classcache_new_name(cls->name);
461
462         CLASSCACHE_ASSERT(en != NULL);
463
464         /* iterate over all class entries */
465         for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
466
467 #ifdef CLASSCACHE_DEBUG
468                 /* check if this entry has already been loaded by initloader */
469                 /* It never should have been loaded before! */
470                 for (lden = clsen->loaders; lden != NULL; lden = lden->next) {
471                         if (lden->loader == initloader)
472                                 CLASSCACHE_ASSERT(false);
473                 }
474 #endif
475
476                 /* check if initloader is constrained to this entry */
477                 for (lden = clsen->constraints; lden != NULL; lden = lden->next) {
478                         if (lden->loader == initloader) {
479                                 /* we have to use this entry */
480                                 /* check if is has already been resolved to another class */
481                                 if (clsen->classobj != NULL && clsen->classobj != cls) {
482                                         /* a loading constraint is violated */
483                                         *exceptionptr = new_exception_message(string_java_lang_LinkageError,
484                                                                                                                   "loading constraint violated XXX add message");
485                                         goto return_exception;
486                                 }
487
488                                 /* record initloader as initiating loader */
489                                 clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
490
491                                 /* record the loaded class object */
492                                 clsen->classobj = cls;
493
494                                 /* done */
495                                 goto return_success;
496                         }
497                 }
498
499         }
500
501         /* create a new class entry for this class object with */
502         /* initiating loader initloader                        */
503
504         clsen = NEW(classcache_class_entry);
505         clsen->classobj = cls;
506         clsen->loaders = classcache_new_loader_entry(initloader, NULL);
507         clsen->constraints = NULL;
508
509         clsen->next = en->classes;
510         en->classes = clsen;
511
512   return_success:
513         CLASSCACHE_UNLOCK();
514         return true;
515
516   return_exception:
517         CLASSCACHE_UNLOCK();
518         return false;                           /* exception */
519 }
520
521 /* classcache_find_loader ******************************************************
522  
523    Find the class entry loaded by or constrained to a given loader
524    (internally used helper function)
525   
526    IN:
527        entry............the classcache_name_entry
528        loader...........the loader to look for
529   
530    RETURN VALUE:
531        the classcache_class_entry for the given loader, or
532            NULL if no entry was found
533    
534 *******************************************************************************/
535
536 static /*@exposed@*/ /*@null@*/ classcache_class_entry *
537 classcache_find_loader(
538         classcache_name_entry * entry,
539         /*@shared@*/ /*@null@*/ classloader * loader)
540 {
541         classcache_class_entry *clsen;
542         classcache_loader_entry *lden;
543
544         CLASSCACHE_ASSERT(entry != NULL);
545
546         /* iterate over all class entries */
547         for (clsen = entry->classes; clsen != NULL; clsen = clsen->next) {
548
549                 /* check if this entry has already been loaded by initloader */
550                 for (lden = clsen->loaders; lden != NULL; lden = lden->next) {
551                         if (lden->loader == loader)
552                                 return clsen;   /* found */
553                 }
554
555                 /* check if loader is constrained to this entry */
556                 for (lden = clsen->constraints; lden != NULL; lden = lden->next) {
557                         if (lden->loader == loader)
558                                 return clsen;   /* found */
559                 }
560         }
561
562         /* not found */
563         return NULL;
564 }
565
566 /* classcache_free_class_entry *************************************************
567  
568    Free the memory used by a class entry
569   
570    IN:
571        clsen............the classcache_class_entry to free  
572            
573 *******************************************************************************/
574
575 static void
576 classcache_free_class_entry(
577         classcache_class_entry * clsen)
578 {
579         classcache_loader_entry *lden;
580         classcache_loader_entry *next;
581
582         CLASSCACHE_ASSERT(clsen != NULL);
583
584         for (lden = clsen->loaders; lden != NULL; lden = next) {
585                 next = lden->next;
586                 FREE(lden, classcache_loader_entry);
587         }
588         for (lden = clsen->constraints; lden != NULL; lden = next) {
589                 next = lden->next;
590                 FREE(lden, classcache_loader_entry);
591         }
592
593         FREE(clsen, classcache_class_entry);
594 }
595
596 /* classcache_remove_class_entry ***********************************************
597  
598    Remove a classcache_class_entry from the list of possible resolution of
599    a name entry
600    (internally used helper function)
601   
602    IN:
603        entry............the classcache_name_entry
604        clsen............the classcache_class_entry to remove
605   
606 *******************************************************************************/
607
608 static void
609 classcache_remove_class_entry(
610         classcache_name_entry * entry,
611         classcache_class_entry * clsen)
612 {
613         classcache_class_entry **chain;
614
615         CLASSCACHE_ASSERT(entry != NULL);
616         CLASSCACHE_ASSERT(clsen != NULL);
617
618         chain = &(entry->classes);
619         while (*chain) {
620                 if (*chain == clsen) {
621                         *chain = clsen->next;
622                         classcache_free_class_entry(clsen);
623                         return;
624                 }
625                 chain = &((*chain)->next);
626         }
627 }
628
629 /* classcache_free_name_entry **************************************************
630  
631    Free the memory used by a name entry
632   
633    IN:
634        entry............the classcache_name_entry to free  
635            
636 *******************************************************************************/
637
638 static void
639 classcache_free_name_entry(
640         classcache_name_entry * entry)
641 {
642         classcache_class_entry *clsen;
643         classcache_class_entry *next;
644
645         CLASSCACHE_ASSERT(entry != NULL);
646
647         for (clsen = entry->classes; clsen; clsen = next) {
648                 next = clsen->next;
649                 classcache_free_class_entry(clsen);
650         }
651
652         FREE(entry, classcache_name_entry);
653 }
654
655 /* classcache_free *************************************************************
656  
657    Free the memory used by the class cache
658
659    NOTE:
660        The class cache may not be used any more after this call, except
661            when it is reinitialized with classcache_init.
662   
663    Note: NOT synchronized!
664   
665 *******************************************************************************/
666
667 void
668 classcache_free(
669         )
670         /*@globals killed classcache_hash@*/
671 {
672         u4 slot;
673         classcache_name_entry *entry;
674         classcache_name_entry *next;
675
676         for (slot = 0; slot < classcache_hash.size; ++slot) {
677                 for (entry = (classcache_name_entry *) classcache_hash.ptr[slot]; entry; entry = next) {
678                         next = entry->hashlink;
679                         classcache_free_name_entry(entry);
680                 }
681         }
682
683         MFREE(classcache_hash.ptr, voidptr, classcache_hash.size);
684         classcache_hash.size = 0;
685         classcache_hash.entries = 0;
686         classcache_hash.ptr = NULL;
687 }
688
689 /* classcache_add_constraint ***************************************************
690  
691    Add a loading constraint
692   
693    IN:
694        a................first initiating loader
695        b................second initiating loader
696        classname........class name
697   
698    RETURN VALUE:
699        true.............everything ok, the constraint has been added,
700        false............an exception has been thrown.
701    
702    Note: synchronized with global tablelock
703    
704 *******************************************************************************/
705
706 bool
707 classcache_add_constraint(
708         /*@shared@*/ /*@null@*/ classloader * a,
709         /*@shared@*/ /*@null@*/ classloader * b,
710         /*@shared@*/ utf * classname)
711 {
712         classcache_name_entry *en;
713         classcache_class_entry *clsenA;
714         classcache_class_entry *clsenB;
715
716         CLASSCACHE_ASSERT(classname != NULL);
717
718 #ifdef CLASSCACHE_VERBOSE
719         fprintf(stderr, "classcache_add_constraint(%p,%p,", (void *) a, (void *) b);
720         utf_fprint_classname(stderr, classname);
721         fprintf(stderr, ")\n");
722 #endif
723
724         /* a constraint with a == b is trivially satisfied */
725         if (a == b)
726                 return true;
727
728         CLASSCACHE_LOCK();
729
730         en = classcache_new_name(classname);
731
732         CLASSCACHE_ASSERT(en != NULL);
733
734         /* find the entry loaded by / constrained to each loader */
735         clsenA = classcache_find_loader(en, a);
736         clsenB = classcache_find_loader(en, b);
737
738         if (clsenA != NULL && clsenB != NULL) {
739                 /* { both loaders have corresponding entries } */
740
741                 /* if the entries are the same, the constraint is already recorded */
742                 if (clsenA == clsenB)
743                         goto return_success;
744
745                 /* check if the entries can be merged */
746                 if (clsenA->classobj != NULL && clsenB->classobj != NULL
747                         && clsenA->classobj != clsenB->classobj) {
748                         /* no, the constraint is violated */
749                         *exceptionptr = new_exception_message(string_java_lang_LinkageError,
750                                                                                                   "loading constraint violated XXX add message");
751                         goto return_exception;
752                 }
753
754                 /* yes, merge the entries */
755                 /* clsenB will be merged into clsenA */
756                 clsenA->loaders = classcache_merge_loaders(clsenA->loaders, clsenB->loaders);
757                 clsenB->loaders = NULL;
758
759                 clsenA->constraints = classcache_merge_loaders(clsenA->constraints,
760                                                                                                            clsenB->constraints);
761                 clsenB->constraints = NULL;
762
763                 if (!clsenA->classobj)
764                         clsenA->classobj = clsenB->classobj;
765
766                 /* remove clsenB from the list of class entries */
767                 classcache_remove_class_entry(en, clsenB);
768         }
769         else {
770                 /* { at most one of the loaders has a corresponding entry } */
771
772                 /* set clsenA to the single class entry we have */
773                 if (!clsenA)
774                         clsenA = clsenB;
775
776                 if (!clsenA) {
777                         /* { no loader has a corresponding entry } */
778
779                         /* create a new class entry with the constraint (a,b,en->name) */
780                         clsenA = NEW(classcache_class_entry);
781                         clsenA->classobj = NULL;
782                         clsenA->loaders = NULL;
783                         clsenA->constraints = classcache_new_loader_entry(b, NULL);
784                         clsenA->constraints = classcache_new_loader_entry(a, clsenA->constraints);
785
786                         clsenA->next = en->classes;
787                         en->classes = clsenA;
788                 }
789                 else {
790                         /* make b the loader that has no corresponding entry */
791                         if (clsenB)
792                                 b = a;
793
794                         /* loader b must be added to entry clsenA */
795                         clsenA->constraints = classcache_new_loader_entry(b, clsenA->constraints);
796                 }
797         }
798
799   return_success:
800         CLASSCACHE_UNLOCK();
801         return true;
802
803   return_exception:
804         CLASSCACHE_UNLOCK();
805         return false;                           /* exception */
806 }
807
808 /*============================================================================*/
809 /* DEBUG DUMPS                                                                */
810 /*============================================================================*/
811
812 /* classcache_debug_dump *******************************************************
813  
814    Print the contents of the loaded class cache to a stream
815   
816    IN:
817        file.............output stream
818   
819    Note: synchronized with global tablelock
820    
821 *******************************************************************************/
822
823 void
824 classcache_debug_dump(
825         /*@shared@*/ FILE * file)
826 {
827         classcache_name_entry *c;
828         classcache_class_entry *clsen;
829         classcache_loader_entry *lden;
830         u4 slot;
831
832         CLASSCACHE_LOCK();
833
834         fprintf(file, "\n=== [loaded class cache] =====================================\n\n");
835         fprintf(file, "hash size   : %d\n", (int) classcache_hash.size);
836         fprintf(file, "hash entries: %d\n", (int) classcache_hash.entries);
837         fprintf(file, "\n");
838
839         for (slot = 0; slot < classcache_hash.size; ++slot) {
840                 c = (classcache_name_entry *) classcache_hash.ptr[slot];
841
842                 for (; c != NULL; c = c->hashlink) {
843                         utf_fprint_classname(file, c->name);
844                         fprintf(file, "\n");
845
846                         /* iterate over all class entries */
847                         for (clsen = c->classes; clsen != NULL; clsen = clsen->next) {
848                                 if (clsen->classobj) {
849                                         fprintf(file, "    loaded %p\n", (void *) clsen->classobj);
850                                 }
851                                 else {
852                                         fprintf(file, "    unresolved\n");
853                                 }
854                                 fprintf(file, "        loaders:");
855                                 for (lden = clsen->loaders; lden != NULL; lden = lden->next) {
856                                         fprintf(file, "<%p> %p", (void *) lden, (void *) lden->loader);
857                                 }
858                                 fprintf(file, "\n        constraints:");
859                                 for (lden = clsen->constraints; lden != NULL; lden = lden->next) {
860                                         fprintf(file, "<%p> %p", (void *) lden, (void *) lden->loader);
861                                 }
862                                 fprintf(file, "\n");
863                         }
864                 }
865         }
866         fprintf(file, "\n==============================================================\n\n");
867
868         CLASSCACHE_UNLOCK();
869 }
870
871 /*
872  * These are local overrides for various environment variables in Emacs.
873  * Please do not remove this and leave it at the end of the file, where
874  * Emacs will automagically detect them.
875  * ---------------------------------------------------------------------
876  * Local variables:
877  * mode: c
878  * indent-tabs-mode: t
879  * c-basic-offset: 4
880  * tab-width: 4
881  * End:
882  * vim:noexpandtab:sw=4:ts=4:
883  */