File:  [Apple Darwin 0.x] / objc / objc-sel.m
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 19:13:57 2018 UTC (8 years, 2 months ago) by root
Branches: MAIN, Apple
CVS tags: HEAD, Darwin03, Darwin01
Darwin 0.1 In-kernel Objective-C runtime

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 *	objc-sel.m
 *	Copyright 1988, NeXT, Inc.
 *	Author:	s. naroff
 *
 *	Public functions:		
 *	--------------
 *
 *	sel_getName
 *	sel_getUid
 *	sel_isMapped
 *	sel_registerName
 *	_strhash			(undocumented)
 *
 *	Private functions:
 *	---------------
 *
 *	_sel_init			(private extern)
 *	_sel_unloadSelectors		(private extern)
 *
 */

#ifdef SHLIB
#import "shlib.h"
#endif SHLIB

#import "objc-private.h"

/* Number of hashtable buckets to malloc in a chunk */

#define HASH_ALLOC_LIST_SIZE 40

/* Size of the dynamic and freeze-dried hashtables */

#define SIZEHASHTABLE 821

   
typedef struct _hashEntry
{
  struct _hashEntry *next;
  const char *sel;
} HASH, *PHASH;

typedef struct _hashTable
{
  const struct mach_header *header;
  unsigned int count;
  unsigned int entries;
  const char *min;
  const char *max;
  PHASH *list;
  struct _hashTable *next;
} hashTable;

/* The dynamic hashtable contains all selectors which are not present in
   any freeze-dried hashtable.  Space for it is allocated only on demand.
   We use a sentinel to avoid extra tests for NULL. */

static const PHASH sentinel = NULL;

static hashTable dynamicHashTable =
{
  NULL,
  1,		// count
  0,		// entries
  NULL,		// Range checking does not work for the dynamic hashtable.
  NULL,
  (PHASH *) &sentinel,
  NULL,		// next
};


/* The list of all hashtables.  The freeze-dried hashtables are placed on
   the list in order of size, and the dynamic hashtable is always last. */

static hashTable *hashTableChain = &dynamicHashTable;

#ifndef FREEZE
/* Lock for all static data in objc-sel.m */

static OBJC_DECLARE_LOCK (selectorLock);
#endif

/* This is a duplicate of a static function in objc-class.m!  We should
   make this a private extern. */

static void *objc_malloc (unsigned int size)
{
  NXZone *zone;
  void *space;
  
#ifdef FREEZE
  zone = NXDefaultMallocZone ();
#else
  zone = _objc_create_zone ();
#endif
  
  space = NXZoneMalloc (zone, size);
  
  if (space == 0 && size != 0)
    __S(_objc_fatal) ("unable to allocate space");

  return space;
}


/* For compatibility only.  This function is public even though it never
   should have been. */

#ifndef FREEZE
unsigned int _strhash (const unsigned char *s)
{
  return _objc_strhash (s);
}
#endif

/* Allocate and initialize a new bucket for the dynamic hashtable.
   The selector lock is assumed to be already taken out. */

static inline PHASH opthashNew (const char *key, PHASH link)
{
  static PHASH HASH_alloc_list = 0;
  static int HASH_alloc_index = 0;
  PHASH new;

  if (!HASH_alloc_list || HASH_alloc_index >= HASH_ALLOC_LIST_SIZE)
    {
      HASH_alloc_list = objc_malloc (sizeof (HASH) * HASH_ALLOC_LIST_SIZE);
      HASH_alloc_index = 0;
    }
  
  new = &HASH_alloc_list[HASH_alloc_index++];
  new->next = link;
  new->sel = key;
  return new;
}


#ifndef FREEZE
/* This is no longer used.  We should remove this from the API. */

BOOL sel_isMapped (SEL sel)
{
  hashTable *table;
  
  if (sel == (SEL) 0) 
    return NO;
  
  OBJC_LOCK (&selectorLock);
  
  for (table = hashTableChain; table; table = table->next)
    {
      if ((const char *) sel >= table->min && (const char *) sel < table->max)
        {
	  OBJC_UNLOCK (&selectorLock);
	  return YES;
	}
      else if (table == &dynamicHashTable)
        {
	  unsigned int slot;
	  
	  for (slot = 0; slot < table->count; slot++)
	    {
	      PHASH target;
	      
	      for (target = table->list[slot]; target; target = target->next)
		if (sel == (SEL)target->sel)
		  {
		    OBJC_UNLOCK (&selectorLock);
		    return YES;
		  }
	    }
	}
    }
  
  OBJC_UNLOCK (&selectorLock);
  
  return NO;
}
#endif

#ifndef FREEZE
/* This is now very fast!
   Note that previously we would return NULL if passed an invalid selector. */

const char *sel_getName (SEL sel)
{
  return (const char *) sel;
}
#endif

/* Add a new selector if not already present.  Formerly _sel_registerName(). 
   Now again _sel_registerName(), and a public safe variant added. */

SEL __S(_sel_registerName) (const char *key)
{
  unsigned int hash;
  hashTable *table;
  
  if (!key) 
    return (SEL) 0;
  
  hash = _objc_strhash (key);
  
  OBJC_LOCK (&selectorLock);
  
  for (table = hashTableChain; table; table = table->next)
    {
      if (key >= table->min && key < table->max)
        {
	  OBJC_UNLOCK (&selectorLock);
	  return (SEL) key;
	}
      else
        {
	  PHASH target;
	  unsigned int slot = hash % table->count;
	  
	  for (target = table->list[slot]; target; target = target->next)
	    if (key[0] == target->sel[0] &&
		strcmp (key, target->sel) == 0)
	      {
		OBJC_UNLOCK (&selectorLock);
		return (SEL) target->sel;
	      }
	  
	  /* If we have reached the dynamic hashtable, insert a new entry. */
	  if (table == &dynamicHashTable)
	    {
	      table->entries++;
	      
	      /* Allocate hashtable if needed. */
	      if (table->list == &sentinel)
		{
		  /* Allocate a new empty array. */
		  table->count = SIZEHASHTABLE;
		  table->list = objc_malloc (table->count * sizeof (PHASH));
		  bzero (table->list, table->count * sizeof (PHASH));
		  slot = hash % table->count;
		}
	      
	      table->list[slot] = opthashNew (key, table->list[slot]);
	      OBJC_UNLOCK (&selectorLock);
	      return (SEL) key;
	    }
	}
    }
  
  /* We searched all hashtables without finding the dynamic hashtable! */
  abort ();
}

/*
 *  Public version of _sel_registerName, which assures that the
 *  argument key is copied if nessecary.
 */

#ifndef FREEZE
SEL sel_registerName (const char *key)
{
   SEL s = sel_getUid (key);
   if (s) 
     return s;
   else
     return __S(_sel_registerName) (NXUniqueString (key));
}
#endif

/* Remove all of the selectors associated with a dynamically loaded module.
  We currently leak the removed entries (because they are allocated in
  chunks).  We could instead build a free chain out of them to reuse. */

#ifndef FREEZE
void __S(_sel_unloadSelectors) (const char *min, const char *max)
{
  hashTable *table = &dynamicHashTable;
  unsigned int slot;
  
  OBJC_LOCK (&selectorLock);
  
  for (slot = 0; slot < table->count; slot++)
    {
      PHASH *ptr = &table->list[slot];
      
      while (*ptr)
        {
	  PHASH target = *ptr;
	  
	  if (target->sel >= min && target->sel < max)
	    *ptr = target->next;	/* Remove this entry */
	  else
	    ptr = &target->next;	/* Skip */
        }
    }
  
  OBJC_UNLOCK (&selectorLock);
}
#endif

/* Search each hashtable in the chain for the string. */

#ifndef FREEZE
SEL sel_getUid (const char *key)
{
  unsigned int hash;
  hashTable *table;
  
  if (!key) 
    return (SEL) 0;
  
  hash = _objc_strhash (key);
  
  OBJC_LOCK (&selectorLock);
  
  for (table = hashTableChain; table; table = table->next)
    {
      if (key >= table->min && key < table->max)
        {
	  OBJC_UNLOCK (&selectorLock);
	  return (SEL) key;
	}
      else
        {
	  PHASH target;
	  unsigned int slot = hash % table->count;
	  
	  for (target = table->list[slot]; target; target = target->next)
	    if (key[0] == target->sel[0] &&
		strcmp (key, target->sel) == 0)
	      {
		OBJC_UNLOCK (&selectorLock);
		return (SEL) target->sel;
	      }
	}
    }
  
  OBJC_UNLOCK (&selectorLock);
  
  return (SEL) 0;
}
#endif

/* Register a freeze-dried hashtable. */

void __S(_sel_init) (const struct mach_header *header,
		const char *strings,
		unsigned int stringSize,
		void *frozenHashTable)
{
  hashTable *frozenTable = NXZoneMalloc (NXDefaultMallocZone(), sizeof (hashTable));
  hashTable **tablePtr;
  
  frozenTable->header = header;
  frozenTable->count = SIZEHASHTABLE;
  frozenTable->entries = 0;
  frozenTable->min = strings;
  frozenTable->max = strings + stringSize;
  frozenTable->list = frozenHashTable;
  
  /* Insert the freeze-dried hashtable at the end of the hashtable chain. */
  
  for (tablePtr = &hashTableChain; *tablePtr; tablePtr = &(*tablePtr)->next)
    if (*tablePtr == &dynamicHashTable)
      {
        frozenTable->next = &dynamicHashTable;
	*tablePtr = frozenTable;
	break;
      }
}


#if  !defined(KERNEL) && !defined(FREEZE)

void _sel_printHashTable (void)
{
  hashTable *table;
  
  for (table = hashTableChain; table; table = table->next)
    {
      unsigned int slot;
      
      printf ("%s selector hashtable for %s:\n",
	      (table == &dynamicHashTable) ? "dynamic" : "freeze-dried",
	      __S(_nameForHeader) (table->header));
      
      printf ("\n");
      
      for (slot = 0; slot < table->count; slot++)
	{
	  PHASH target;
	  
	  printf ("slot[%u]: ", slot);
	  
	  for (target = table->list[slot]; target; target = target->next)
	    printf (" %s", target->sel);
	  
	  printf ("\n");
	}
      
      printf ("\n");
    }
}


void _sel_printStatistics (void)
{
  hashTable *table;
  unsigned int totalCount = 0;
  unsigned int totalEntries = 0;
  unsigned int totalUniqueEntries = 0;
  unsigned int totalEmptySlots = 0;
  unsigned int grandTotalChain = 0;
  unsigned int grandTotalMissChain = 0;
  unsigned int maxMaxChain = 0;
  unsigned int maxMaxMissChain = 0;
  unsigned int totalMallocedData = 0;
  unsigned int totalSharedData = 0;
  unsigned int depth = 0;
  unsigned int totalDepth = 0;
  
  for (table = hashTableChain; table; table = table->next)
    {
      unsigned int slot;
      unsigned int entries = 0;
      unsigned int uniqueEntries = 0;
      unsigned int emptySlots = 0;
      unsigned int totalChain = 0;
      unsigned int totalMissChain = 0;
      unsigned int maxChain = 0;
      unsigned int maxMissChain = 0;
      unsigned int mallocedData;
      unsigned int sharedData;
      
      for (slot = 0; slot < table->count; slot++)
	{
	  PHASH target;
	  unsigned int chain = 0;
	  
	  for (target = table->list[slot]; target; target = target->next)
	    {
	      entries++;
	      
	      if (sel_getUid (target->sel) == (SEL) target->sel)
		{
		  uniqueEntries++;
	      
		  chain++;
		  
		  if (chain > maxChain)
		    maxChain = chain;
		  
		  totalChain += chain;
		}
	    }
	  
	  if (chain > maxMissChain)
	    maxMissChain = chain;
	  
	  totalMissChain += chain;
	  
	  if (chain == 0)
	    emptySlots++;
	}
      
      printf ("%s selector hashtable for %s (depth = %u):\n",
	      (table == &dynamicHashTable) ? "dynamic" : "freeze-dried",
	      __S(_nameForHeader) (table->header),
	      depth);
      
      printf ("%u entries in %u slots (%.2f entries/slot)\n",
	      entries, table->count, (float) entries / (float) table->count);
      printf ("%u empty slots (%u%%)\n",
	      emptySlots, (100 * emptySlots + 50) / table->count);
      printf ("%u redundant entries (%u%%)\n",
	      entries - uniqueEntries,
	      (100 * (entries - uniqueEntries) + 50) / entries);
      printf ("%.2f average lookup chain (%u maximum)\n",
	      (float) totalChain / (float) uniqueEntries, maxChain);
      printf ("%.2f average miss chain (%u maximum)\n",
	      (float) totalMissChain / (float) table->count, maxMissChain);
      if (table == &dynamicHashTable)
	{
	  unsigned int chunks = (entries + HASH_ALLOC_LIST_SIZE - 1) /
				HASH_ALLOC_LIST_SIZE;
	  
	  mallocedData = table->count * sizeof (PHASH) +
			 chunks * HASH_ALLOC_LIST_SIZE * sizeof (HASH);
	  sharedData = 0;
	}
      else
        {
	  mallocedData = sizeof (hashTable);
	  sharedData = table->count * sizeof (PHASH) + entries * sizeof (HASH);
        }
      
      if (sharedData)
	printf ("%u bytes malloced data + %u bytes shared data\n",
		mallocedData, sharedData);
      else
	printf ("%u bytes malloced data\n", mallocedData);
      
      totalCount += table->count;
      totalEntries += entries;
      totalUniqueEntries += uniqueEntries;
      totalEmptySlots += emptySlots;
      grandTotalChain += totalChain;
      grandTotalMissChain += totalMissChain;
      if (maxChain > maxMaxChain)
        maxMaxChain = maxChain;
      if (maxMissChain > maxMaxMissChain)
        maxMaxMissChain = maxMissChain;
      totalMallocedData += mallocedData;
      totalSharedData += sharedData;
      totalDepth += depth * uniqueEntries;
      depth++;
      
      printf ("\n");
    }
  
  printf ("cummulative selector hashtable statistics:\n");
  
  printf ("%u entries in %u slots (%.2f entries/slot)\n",
	  totalEntries, totalCount, (float) totalEntries / (float) totalCount);
  printf ("%u empty slots (%u%%)\n",
	  totalEmptySlots, (100 * totalEmptySlots + 50) / totalCount);
  printf ("%u redundant entries (%u%%)\n",
	  totalEntries - totalUniqueEntries,
	  (100 * (totalEntries - totalUniqueEntries) + 50) / totalEntries);
  printf ("%.2f average lookup depth (%u maximum)\n",
	  (float) totalDepth / (float) totalUniqueEntries,
	  depth - 1);
  printf ("%.2f average lookup chain (%u maximum)\n",
	  (float) grandTotalChain / (float) totalUniqueEntries,
	  maxMaxChain);
  printf ("%u miss depth\n", depth - 1);
  printf ("%.2f average miss chain (%u maximum)\n",
	  (float) grandTotalMissChain / (float) totalCount,
	  maxMaxMissChain);
  
  if (totalSharedData)
    printf ("%u bytes malloced data + %u bytes shared data\n",
	    totalMallocedData, totalSharedData);
  else
    printf ("%u bytes malloced data\n", totalMallocedData);
}

#endif /* KERNEL */

#ifdef FREEZE

void volatile __S(_objc_fatal) (const char *msg)
{
  printf ("objc fatal: %s\n", msg);
  exit (1);
}


/* used by `objcopt' to freeze dry a hash table */

void __S(_sel_writeHashTable) (int start_addr, 
			  char *myAddressSpace,
			  char *shlibAddressSpace,
			  void **addr, int *size)
{
  hashTable *table = &dynamicHashTable;
  unsigned int frozenSize = table->count * sizeof (PHASH) +
			    table->entries * sizeof (HASH);
  PHASH *frozenTable = NXZoneMalloc (NXDefaultMallocZone(), frozenSize);
  PHASH frozenEntries = (PHASH) (frozenTable + table->count);
  PHASH vm_hashEntryList = (PHASH) (start_addr +
				    table->count * sizeof (PHASH));
  unsigned int slot;
  
  bzero (frozenTable, frozenSize);
  
  for (slot = 0; slot < table->count; slot++)
    if (table->list[slot])
      {
	PHASH target;
	
	frozenTable[slot] = vm_hashEntryList;
	
	for (target = table->list[slot]; target; target = target->next)
	  {
	    frozenEntries->sel = (target->sel - myAddressSpace) +
				 shlibAddressSpace;
	    if (target->next)
	      frozenEntries->next = vm_hashEntryList + 1;
	    
	    frozenEntries++;
	    vm_hashEntryList++;
	  }
      }
  
  *addr = frozenTable;
  *size = frozenSize;
}

const char *__S(_nameForHeader) (const struct mach_header *header)
{
  return "objcopt";
}

#endif /* FREEZE */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.