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