Reviewed by Ken.
[WebKit-https.git] / JavaScriptCore / kjs / collector.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 2003 Apple Computer, Inc.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "collector.h"
23
24 #include "value.h"
25 #include "internal.h"
26
27 #if APPLE_CHANGES
28 #include <CoreFoundation/CoreFoundation.h>
29 #include <cxxabi.h>
30 #endif
31
32 #include <collector.h>
33 #include <value.h>
34 #include <internal.h>
35
36 #if APPLE_CHANGES
37 #include <pthread.h>
38 #include <mach/mach_port.h>
39 #include <mach/task.h>
40 #include <mach/thread_act.h>
41 #endif
42
43 namespace KJS {
44
45 // tunable parameters
46 const int MINIMUM_CELL_SIZE = 56;
47 const int BLOCK_SIZE = (8 * 4096);
48 const int SPARE_EMPTY_BLOCKS = 2;
49 const int MIN_ARRAY_SIZE = 14;
50 const int GROWTH_FACTOR = 2;
51 const int LOW_WATER_FACTOR = 4;
52 const int ALLOCATIONS_PER_COLLECTION = 1000;
53
54 // derived constants
55 const int CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0);
56 const int CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double);
57 const int CELLS_PER_BLOCK = ((BLOCK_SIZE * 8 - sizeof(int32_t) * 8 - sizeof(void *) * 8) / (CELL_SIZE * 8));
58
59
60
61 struct CollectorCell {
62   union {
63     double memory[CELL_ARRAY_LENGTH];
64     struct {
65       void *zeroIfFree;
66       CollectorCell *next;
67     } freeCell;
68   } u;
69 };
70
71
72 struct CollectorBlock {
73   CollectorCell cells[CELLS_PER_BLOCK];
74   int32_t usedCells;
75   CollectorCell *freeList;
76 };
77
78 struct CollectorHeap {
79   CollectorBlock **blocks;
80   int numBlocks;
81   int usedBlocks;
82   int firstBlockWithPossibleSpace;
83   
84   CollectorCell **oversizeCells;
85   int numOversizeCells;
86   int usedOversizeCells;
87
88   int numLiveObjects;
89   int numAllocationsSinceLastCollect;
90 };
91
92 static CollectorHeap heap = {NULL, 0, 0, 0, NULL, 0, 0, 0, 0};
93
94 bool Collector::memoryFull = false;
95
96 void* Collector::allocate(size_t s)
97 {
98   assert(Interpreter::lockCount() > 0);
99
100   if (s == 0)
101     return 0L;
102   
103   // collect if needed
104   if (++heap.numAllocationsSinceLastCollect >= ALLOCATIONS_PER_COLLECTION) {
105     collect();
106   }
107   
108   if (s > (unsigned)CELL_SIZE) {
109     // oversize allocator
110     if (heap.usedOversizeCells == heap.numOversizeCells) {
111       heap.numOversizeCells = MAX(MIN_ARRAY_SIZE, heap.numOversizeCells * GROWTH_FACTOR);
112       heap.oversizeCells = (CollectorCell **)realloc(heap.oversizeCells, heap.numOversizeCells * sizeof(CollectorCell *));
113     }
114     
115     void *newCell = malloc(s);
116     heap.oversizeCells[heap.usedOversizeCells] = (CollectorCell *)newCell;
117     heap.usedOversizeCells++;
118     heap.numLiveObjects++;
119
120 #if !USE_CONSERVATIVE_GC
121     ((ValueImp *)(newCell))->_flags = 0;
122 #endif
123     return newCell;
124   }
125   
126   // slab allocator
127   
128   CollectorBlock *targetBlock = NULL;
129   
130   int i;
131   for (i = heap.firstBlockWithPossibleSpace; i < heap.usedBlocks; i++) {
132     if (heap.blocks[i]->usedCells < CELLS_PER_BLOCK) {
133       targetBlock = heap.blocks[i];
134       break;
135     }
136   }
137
138   heap.firstBlockWithPossibleSpace = i;
139   
140   if (targetBlock == NULL) {
141     // didn't find one, need to allocate a new block
142     
143     if (heap.usedBlocks == heap.numBlocks) {
144       heap.numBlocks = MAX(MIN_ARRAY_SIZE, heap.numBlocks * GROWTH_FACTOR);
145       heap.blocks = (CollectorBlock **)realloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock *));
146     }
147     
148     targetBlock = (CollectorBlock *)calloc(1, sizeof(CollectorBlock));
149     targetBlock->freeList = targetBlock->cells;
150     heap.blocks[heap.usedBlocks] = targetBlock;
151     heap.usedBlocks++;
152   }
153   
154   // find a free spot in the block and detach it from the free list
155   CollectorCell *newCell = targetBlock->freeList;
156
157   if (newCell->u.freeCell.next != NULL) {
158     targetBlock->freeList = newCell->u.freeCell.next;
159   } else if (targetBlock->usedCells == (CELLS_PER_BLOCK - 1)) {
160     // last cell in this block
161     targetBlock->freeList = NULL;
162   } else {
163     // all next pointers are initially 0, meaning "next cell"
164     targetBlock->freeList = newCell + 1;
165   }
166
167   targetBlock->usedCells++;
168   heap.numLiveObjects++;
169
170 #if !USE_CONSERVATIVE_GC
171   ((ValueImp *)(newCell))->_flags = 0;
172 #endif
173   return (void *)(newCell);
174 }
175
176 #if TEST_CONSERVATIVE_GC || USE_CONSERVATIVE_GC
177
178 struct Collector::Thread {
179   Thread(pthread_t pthread, mach_port_t mthread) : posixThread(pthread), machThread(mthread) {}
180   Thread *next;
181   pthread_t posixThread;
182   mach_port_t machThread;
183 };
184
185 pthread_key_t registeredThreadKey;
186 pthread_once_t registeredThreadKeyOnce = PTHREAD_ONCE_INIT;
187 Collector::Thread *registeredThreads;
188   
189 static void destroyRegisteredThread(void *data) 
190 {
191   Collector::Thread *thread = (Collector::Thread *)data;
192
193   if (registeredThreads == thread) {
194     registeredThreads = registeredThreads->next;
195   } else {
196     Collector::Thread *last = registeredThreads;
197     for (Collector::Thread *t = registeredThreads->next; t != NULL; t = t->next) {
198       if (t == thread) {
199         last->next = t->next;
200           break;
201       }
202       last = t;
203     }
204   }
205
206   delete thread;
207 }
208
209 static void initializeRegisteredThreadKey()
210 {
211   pthread_key_create(&registeredThreadKey, destroyRegisteredThread);
212 }
213
214 void Collector::registerThread()
215 {
216   pthread_once(&registeredThreadKeyOnce, initializeRegisteredThreadKey);
217
218   if (!pthread_getspecific(registeredThreadKey)) {
219     pthread_t pthread = pthread_self();
220     Collector::Thread *thread = new Collector::Thread(pthread, pthread_mach_thread_np(pthread));
221     thread->next = registeredThreads;
222     registeredThreads = thread;
223     pthread_setspecific(registeredThreadKey, thread);
224   }
225 }
226
227
228 // cells are 8-byte aligned 
229 #define IS_POINTER_ALIGNED(p) (((int)(p) & 7) == 0)
230
231 void Collector::markStackObjectsConservatively(void *start, void *end)
232 {
233   if (start > end) {
234     void *tmp = start;
235     start = end;
236     end = tmp;
237   }
238
239   assert(((char *)end - (char *)start) < 0x1000000);
240   assert(IS_POINTER_ALIGNED(start));
241   assert(IS_POINTER_ALIGNED(end));
242   
243   char **p = (char **)start;
244   char **e = (char **)end;
245   
246   while (p != e) {
247     char *x = *p++;
248     if (IS_POINTER_ALIGNED(x) && x) {
249       bool good = false;
250       for (int block = 0; block < heap.usedBlocks; block++) {
251         size_t offset = x - (char *)heap.blocks[block];
252         const size_t lastCellOffset = sizeof(CollectorCell) * (CELLS_PER_BLOCK - 1);
253         if (offset <= lastCellOffset && offset % sizeof(CollectorCell) == 0) {
254           good = true;
255           break;
256         }
257       }
258       
259       if (!good) {
260         int n = heap.usedOversizeCells;
261         for (int i = 0; i != n; i++) {
262           if (x == (char *)heap.oversizeCells[i]) {
263             good = true;
264             break;
265           }
266         }
267       }
268       
269       if (good && ((CollectorCell *)x)->u.freeCell.zeroIfFree != 0) {
270         ValueImp *imp = (ValueImp *)x;
271         if (!imp->marked())
272           imp->mark();
273       }
274     }
275   }
276 }
277
278 void Collector::markCurrentThreadConservatively()
279 {
280   jmp_buf registers;
281   setjmp(registers);
282
283   pthread_t thread = pthread_self();
284   void *stackBase = pthread_get_stackaddr_np(thread);
285   int dummy;
286   void *stackPointer = &dummy;
287   markStackObjectsConservatively(stackPointer, stackBase);
288 }
289
290 typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
291
292 void Collector::markOtherThreadConservatively(Thread *thread)
293 {
294   thread_suspend(thread->machThread);
295
296 #if defined(__i386__)
297   i386_thread_state_t regs;
298   unsigned user_count = sizeof(regs)/sizeof(int);
299   thread_state_flavor_t flavor = i386_THREAD_STATE;
300 #elif defined(__ppc__)
301   ppc_thread_state_t  regs;
302   unsigned user_count = PPC_THREAD_STATE_COUNT;
303   thread_state_flavor_t flavor = PPC_THREAD_STATE;
304 #elif defined(__ppc64__)
305   ppc_thread_state64_t  regs;
306   unsigned user_count = PPC_THREAD_STATE64_COUNT;
307   thread_state_flavor_t flavor = PPC_THREAD_STATE64;
308 #else
309 #error Unknown Architecture
310 #endif
311   // get the thread register state
312   thread_get_state(thread->machThread, flavor, (thread_state_t)&regs, &user_count);
313   
314   // scan the registers
315   markStackObjectsConservatively((void *)&regs, (void *)((char *)&regs + (user_count * sizeof(usword_t))));
316   
317   // scan the stack
318 #if defined(__i386__)
319   markStackObjectsConservatively((void *)regs.esp, pthread_get_stackaddr_np(thread->posixThread));
320 #elif defined(__ppc__) || defined(__ppc64__)
321   markStackObjectsConservatively((void *)regs.r1, pthread_get_stackaddr_np(thread->posixThread));
322 #else
323 #error Unknown Architecture
324 #endif
325
326   thread_resume(thread->machThread);
327 }
328
329 void Collector::markStackObjectsConservatively()
330 {
331   markCurrentThreadConservatively();
332
333   for (Thread *thread = registeredThreads; thread != NULL; thread = thread->next) {
334     if (thread->posixThread != pthread_self()) {
335       markOtherThreadConservatively(thread);
336     }
337   }
338 }
339
340 void Collector::markProtectedObjects()
341 {
342   for (int i = 0; i < ProtectedValues::_tableSize; i++) {
343     ValueImp *val = ProtectedValues::_table[i].key;
344     if (val && !val->marked()) {
345       val->mark();
346     }
347   }
348 }
349
350 #endif
351
352 bool Collector::collect()
353 {
354   assert(Interpreter::lockCount() > 0);
355
356   bool deleted = false;
357
358 #if TEST_CONSERVATIVE_GC
359   // CONSERVATIVE MARK: mark the root set using conservative GC bit (will compare later)
360   ValueImp::useConservativeMark(true);
361 #endif
362
363 #if USE_CONSERVATIVE_GC || TEST_CONSERVATIVE_GC
364   if (InterpreterImp::s_hook) {
365     InterpreterImp *scr = InterpreterImp::s_hook;
366     do {
367       //fprintf( stderr, "Collector marking interpreter %p\n",(void*)scr);
368       scr->mark();
369       scr = scr->next;
370     } while (scr != InterpreterImp::s_hook);
371   }
372
373   markStackObjectsConservatively();
374   markProtectedObjects();
375 #endif
376
377 #if TEST_CONSERVATIVE_GC
378   ValueImp::useConservativeMark(false);
379 #endif
380
381 #if !USE_CONSERVATIVE_GC
382   // MARK: first mark all referenced objects recursively
383   // starting out from the set of root objects
384   if (InterpreterImp::s_hook) {
385     InterpreterImp *scr = InterpreterImp::s_hook;
386     do {
387       //fprintf( stderr, "Collector marking interpreter %p\n",(void*)scr);
388       scr->mark();
389       scr = scr->next;
390     } while (scr != InterpreterImp::s_hook);
391   }
392   
393   // mark any other objects that we wouldn't delete anyway
394   for (int block = 0; block < heap.usedBlocks; block++) {
395
396     int minimumCellsToProcess = heap.blocks[block]->usedCells;
397     CollectorBlock *curBlock = heap.blocks[block];
398
399     for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
400       if (minimumCellsToProcess < cell) {
401         goto skip_block_mark;
402       }
403         
404       ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
405
406       if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0) {
407         
408         if ((imp->_flags & (ValueImp::VI_CREATED|ValueImp::VI_MARKED)) == ValueImp::VI_CREATED &&
409             ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0)) {
410           imp->mark();
411         }
412       } else {
413         minimumCellsToProcess++;
414       }
415     }
416   skip_block_mark: ;
417   }
418   
419   for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
420     ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
421     if ((imp->_flags & (ValueImp::VI_CREATED|ValueImp::VI_MARKED)) == ValueImp::VI_CREATED &&
422         ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0)) {
423       imp->mark();
424     }
425   }
426 #endif
427
428   // SWEEP: delete everything with a zero refcount (garbage) and unmark everything else
429   
430   int emptyBlocks = 0;
431
432   for (int block = 0; block < heap.usedBlocks; block++) {
433     CollectorBlock *curBlock = heap.blocks[block];
434
435     int minimumCellsToProcess = curBlock->usedCells;
436
437     for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
438       if (minimumCellsToProcess < cell) {
439         goto skip_block_sweep;
440       }
441
442       ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
443
444       if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0) {
445 #if USE_CONSERVATIVE_GC
446         if (!imp->_marked)
447 #else
448         if (!imp->refcount && imp->_flags == (ValueImp::VI_GCALLOWED | ValueImp::VI_CREATED))
449 #endif
450         {
451           //fprintf( stderr, "Collector::deleting ValueImp %p (%s)\n", (void*)imp, typeid(*imp).name());
452           // emulate destructing part of 'operator delete()'
453           imp->~ValueImp();
454           curBlock->usedCells--;
455           heap.numLiveObjects--;
456           deleted = true;
457
458           // put it on the free list
459           ((CollectorCell *)imp)->u.freeCell.zeroIfFree = 0;
460           ((CollectorCell *)imp)->u.freeCell.next = curBlock->freeList;
461           curBlock->freeList = (CollectorCell *)imp;
462
463         } else {
464 #if USE_CONSERVATIVE_GC
465           imp->_marked = 0;
466 #elif TEST_CONSERVATIVE_GC
467           imp->_flags &= ~(ValueImp::VI_MARKED | ValueImp::VI_CONSERVATIVE_MARKED);
468 #else
469           imp->_flags &= ~ValueImp::VI_MARKED;
470 #endif
471         }
472       } else {
473         minimumCellsToProcess++;
474       }
475     }
476
477   skip_block_sweep:
478
479     if (heap.blocks[block]->usedCells == 0) {
480       emptyBlocks++;
481       if (emptyBlocks > SPARE_EMPTY_BLOCKS) {
482 #if !DEBUG_COLLECTOR
483         free(heap.blocks[block]);
484 #endif
485         // swap with the last block so we compact as we go
486         heap.blocks[block] = heap.blocks[heap.usedBlocks - 1];
487         heap.usedBlocks--;
488         block--; // Don't move forward a step in this case
489
490         if (heap.numBlocks > MIN_ARRAY_SIZE && heap.usedBlocks < heap.numBlocks / LOW_WATER_FACTOR) {
491           heap.numBlocks = heap.numBlocks / GROWTH_FACTOR; 
492           heap.blocks = (CollectorBlock **)realloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock *));
493         }
494       } 
495     }
496   }
497
498   if (deleted) {
499     heap.firstBlockWithPossibleSpace = 0;
500   }
501   
502   int cell = 0;
503   while (cell < heap.usedOversizeCells) {
504     ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
505     
506 #if USE_CONSERVATIVE_GC
507     if (!imp->_marked) {
508 #else
509     if (!imp->refcount && 
510         imp->_flags == (ValueImp::VI_GCALLOWED | ValueImp::VI_CREATED)) {
511 #endif
512
513       imp->~ValueImp();
514 #if DEBUG_COLLECTOR
515       heap.oversizeCells[cell]->u.freeCell.zeroIfFree = 0;
516 #else
517       free((void *)imp);
518 #endif
519
520       // swap with the last oversize cell so we compact as we go
521       heap.oversizeCells[cell] = heap.oversizeCells[heap.usedOversizeCells - 1];
522
523       heap.usedOversizeCells--;
524       deleted = true;
525       heap.numLiveObjects--;
526
527       if (heap.numOversizeCells > MIN_ARRAY_SIZE && heap.usedOversizeCells < heap.numOversizeCells / LOW_WATER_FACTOR) {
528         heap.numOversizeCells = heap.numOversizeCells / GROWTH_FACTOR; 
529         heap.oversizeCells = (CollectorCell **)realloc(heap.oversizeCells, heap.numOversizeCells * sizeof(CollectorCell *));
530       }
531
532     } else {
533 #if USE_CONSERVATIVE_GC
534       imp->_marked = 0;
535 #elif TEST_CONSERVATIVE_GC
536       imp->_flags &= ~(ValueImp::VI_MARKED | ValueImp::VI_CONSERVATIVE_MARKED);
537 #else
538       imp->_flags &= ~ValueImp::VI_MARKED;
539 #endif
540       cell++;
541     }
542   }
543   
544   heap.numAllocationsSinceLastCollect = 0;
545   
546   memoryFull = (heap.numLiveObjects >= KJS_MEM_LIMIT);
547
548   return deleted;
549 }
550
551 int Collector::size() 
552 {
553   return heap.numLiveObjects; 
554 }
555
556 #ifdef KJS_DEBUG_MEM
557 void Collector::finalCheck()
558 {
559 }
560 #endif
561
562 #if APPLE_CHANGES
563
564 int Collector::numInterpreters()
565 {
566   int count = 0;
567   if (InterpreterImp::s_hook) {
568     InterpreterImp *scr = InterpreterImp::s_hook;
569     do {
570       ++count;
571       scr = scr->next;
572     } while (scr != InterpreterImp::s_hook);
573   }
574   return count;
575 }
576
577 int Collector::numGCNotAllowedObjects()
578 {
579   int count = 0;
580 #if !USE_CONSERVATIVE_GC
581   for (int block = 0; block < heap.usedBlocks; block++) {
582     CollectorBlock *curBlock = heap.blocks[block];
583
584     for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
585       ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
586       
587       if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0 &&
588           (imp->_flags & ValueImp::VI_GCALLOWED) == 0) {
589         ++count;
590       }
591     }
592   }
593   
594   for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
595     ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
596     if ((imp->_flags & ValueImp::VI_GCALLOWED) == 0) {
597       ++count;
598     }
599   }
600 #endif
601
602   return count;
603 }
604
605 int Collector::numReferencedObjects()
606 {
607   int count = 0;
608
609 #if USE_CONSERVATIVE_GC
610   for (int i = 0; i < ProtectedValues::_tableSize; i++) {
611     ValueImp *val = ProtectedValues::_table[i].key;
612     if (val) {
613       ++count;
614     }
615   }
616
617 #else
618
619   for (int block = 0; block < heap.usedBlocks; block++) {
620     CollectorBlock *curBlock = heap.blocks[block];
621
622     for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
623       ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
624       
625       if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0 &&
626           imp->refcount != 0) {
627         ++count;
628       }
629     }
630   }
631   
632   for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
633     ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
634       if (imp->refcount != 0) {
635         ++count;
636       }
637   }
638 #endif
639
640   return count;
641 }
642
643 const void *Collector::rootObjectClasses()
644 {
645   CFMutableSetRef classes = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
646
647 #if USE_CONSERVATIVE_GC
648   for (int i = 0; i < ProtectedValues::_tableSize; i++) {
649     ValueImp *val = ProtectedValues::_table[i].key;
650     if (val) {
651       const char *mangled_name = typeid(*val).name();
652       int status;
653       char *demangled_name = __cxxabiv1::__cxa_demangle (mangled_name, NULL, NULL, &status);
654       
655       CFStringRef className = CFStringCreateWithCString(NULL, demangled_name, kCFStringEncodingASCII);
656       free(demangled_name);
657       CFSetAddValue(classes, className);
658       CFRelease(className);
659     }
660   }
661 #else
662   for (int block = 0; block < heap.usedBlocks; block++) {
663     CollectorBlock *curBlock = heap.blocks[block];
664     for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
665       ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
666       
667       if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0 &&
668           ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0)) {
669         const char *mangled_name = typeid(*imp).name();
670         int status;
671         char *demangled_name = __cxxabiv1::__cxa_demangle (mangled_name, NULL, NULL, &status);
672         
673         CFStringRef className = CFStringCreateWithCString(NULL, demangled_name, kCFStringEncodingASCII);
674         free(demangled_name);
675         CFSetAddValue(classes, className);
676         CFRelease(className);
677       }
678     }
679   }
680   
681   for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
682     ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
683     
684     if ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0) {
685       const char *mangled_name = typeid(*imp).name();
686       int status;
687       char *demangled_name = __cxxabiv1::__cxa_demangle (mangled_name, NULL, NULL, &status);
688       
689       CFStringRef className = CFStringCreateWithCString(NULL, demangled_name, kCFStringEncodingASCII);
690       free(demangled_name);
691       CFSetAddValue(classes, className);
692       CFRelease(className);
693     }
694   }
695 #endif
696   
697   return classes;
698 }
699
700 #endif // APPLE_CHANGES
701
702 } // namespace KJS