Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.246
diff -u -p -r1.246 bootstrap.inc
--- includes/bootstrap.inc	7 Nov 2008 17:21:53 -0000	1.246
+++ includes/bootstrap.inc	11 Nov 2008 04:07:32 -0000
@@ -207,6 +207,16 @@ define('CHECK_PLAIN', 0);
 define('PASS_THROUGH', -1);
 
 /**
+ * Signals that the registry lookup cache should be reset. 
+ */
+define('REGISTRY_RESET_LOOKUP_CACHE', 1);
+
+/**
+ * Signals that the registry lookup cache should be written to storage. 
+ */
+define('REGISTRY_WRITE_LOOKUP_CACHE', 2);
+
+/**
  * @} End of "Title text filtering flags".
  */
 
@@ -655,7 +665,7 @@ function page_get_cache() {
  *   The name of the bootstrap hook we wish to invoke.
  */
 function bootstrap_invoke_all($hook) {
-  foreach (module_list(TRUE, TRUE) as $module) {
+  foreach (module_implements($hook) as $module) {
     module_invoke($module, $hook);
   }
 }
@@ -1412,7 +1422,6 @@ function drupal_function_exists($functio
   $checked[$function] = FALSE;
 
   if (function_exists($function)) {
-    registry_mark_code('function', $function);
     $checked[$function] = TRUE;
     return TRUE;
   }
@@ -1457,7 +1466,36 @@ function drupal_autoload_class($class) {
 /**
  * Helper to check for a resource in the registry.
  */
-function _registry_check_code($type, $name) {
+function _registry_check_code($type, $name = NULL) {
+  static $lookup_cache, $cache_update_needed;
+
+  if (!isset($lookup_cache)) {
+    $lookup_cache = _registry_get_lookup_cache();
+  }
+  
+  // When we rebuild the registry, we need to reset this cache so
+  // we don't keep lookups for resources that changed during the rebuild.
+  if ($type == REGISTRY_RESET_LOOKUP_CACHE) {
+    $cache_update_needed = TRUE;
+    $lookup_cache = NULL;
+    return;
+  }
+
+  if ($type == REGISTRY_WRITE_LOOKUP_CACHE) {
+    if ($cache_update_needed) {
+      _registry_set_lookup_cache($lookup_cache);
+    }
+    return;
+  }
+  
+  $cache_key = $type[0] . $name;
+  if (isset($lookup_cache[$cache_key])) {
+    if ($lookup_cache[$cache_key]) {
+      require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
+    }
+    return $lookup_cache[$cache_key];
+  }
+  
   // This function may get called when the default database is not active, but
   // there is no reason we'd ever want to not use the default database for
   // this query.
@@ -1466,38 +1504,20 @@ function _registry_check_code($type, $na
       ':type' => $type,
     ))
     ->fetchField();
+
+  // Flag that we've run a lookup query and need to update the cache.
+  $cache_update_needed = TRUE;
+
+  // Misses are valuable information worth caching, so cache even if
+  // $file is FALSE.
+  $lookup_cache[$cache_key] = $file;
+
   if ($file) {
     require_once DRUPAL_ROOT . '/' . $file;
-    registry_mark_code($type, $name);
     return TRUE;
   }
-  return FALSE;
-}
-
-/**
- * Collect the resources used for this request.
- *
- * @param $type
- *   The type of resource.
- * @param $name
- *   The name of the resource.
- * @param $return
- *   Boolean flag to indicate whether to return the resources.
- */
-function registry_mark_code($type, $name, $return = FALSE) {
-  static $resources = array();
-
-  if ($type && $name) {
-    if (!isset($resources[$type])) {
-      $resources[$type] = array();
-    }
-    if (!in_array($name, $resources[$type])) {
-      $resources[$type][] = $name;
-    }
-  }
-
-  if ($return) {
-    return $resources;
+  else {
+    return FALSE;
   }
 }
 
@@ -1513,60 +1533,30 @@ function registry_rebuild() {
 }
 
 /**
- * Save the files required by the registry for this path.
+ * Wrapper function to perform string to array conversion of lookup cache.
  */
-function registry_cache_path_files() {
-  if ($used_code = registry_mark_code(NULL, NULL, TRUE)) {
-    $files = array();
-    $type_sql = array();
-    $params = array();
-
-    // This function may get called when the default database is not active, but
-    // there is no reason we'd ever want to not use the default database for
-    // this query.
-    $select = Database::getConnection('default')->select('registry')->distinct();
-    $select->addField('registry', 'filename');
-
-    // This creates a series of 2-clause AND conditions that are then ORed together.
-    $ors = db_or();
-    foreach ($used_code as $type => $names) {
-      $and = db_and()
-        ->condition('name', $names, 'IN')
-        ->condition('type', $type);
-      $ors->condition($and);
-    }
-    $select->condition($ors);
-    $files = $select->execute()->fetchCol();
-
-    if ($files) {
-      sort($files);
-      // Only write this to cache if the file list we are going to cache
-      // is different to what we loaded earlier in the request.
-      if ($files != registry_load_path_files(TRUE)) {
-        $menu = menu_get_item();
-        cache_set('registry:' . $menu['path'], implode(';', $files), 'cache_registry');
-      }
+function _registry_get_lookup_cache() {
+  $lookup_cache = array();
+  if ($cache = cache_get('lookup_cache', 'cache_registry')) {
+    foreach (explode(';', $cache->data) as $lookup) {
+      list($resource, $result) = explode('|', $lookup);
+      $lookup_cache[$resource] = $result;
     }
   }
+  return $lookup_cache;
 }
 
 /**
- * registry_load_path_files
+ * Wrapper function to perform array to string conversion of lookup cache.
  */
-function registry_load_path_files($return = FALSE) {
-  static $file_cache_data = array();
-  if ($return) {
-    sort($file_cache_data);
-    return $file_cache_data;
-  }
-  $menu = menu_get_item();
-  $cache = cache_get('registry:' . $menu['path'], 'cache_registry');
-  if (!empty($cache->data)) {
-    foreach(explode(';', $cache->data) as $file) {
-      require_once DRUPAL_ROOT . '/' . $file;
-      $file_cache_data[] = $file;
-    }
+function _registry_set_lookup_cache(array $lookup_cache) {
+  // Cache a string, not an array, so we can avoid the memory usage hit 
+  // from serialize in the cache system.
+  $key_value_pairs = array();
+  foreach ($lookup_cache as $key => $value) {
+    $key_value_pairs[] =  "$key|" . ($value ? $value : '');
   }
+  return cache_set('lookup_cache', implode(';', $key_value_pairs), 'cache_registry');
 }
 
 /**
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.823
diff -u -p -r1.823 common.inc
--- includes/common.inc	10 Nov 2008 05:22:59 -0000	1.823
+++ includes/common.inc	11 Nov 2008 04:07:32 -0000
@@ -1641,7 +1641,7 @@ function drupal_page_footer() {
   module_invoke_all('exit');
 
   module_implements(MODULE_IMPLEMENTS_WRITE_CACHE);
-  registry_cache_path_files();
+  _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
 }
 
 /**
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.300
diff -u -p -r1.300 menu.inc
--- includes/menu.inc	8 Nov 2008 04:13:10 -0000	1.300
+++ includes/menu.inc	11 Nov 2008 04:07:33 -0000
@@ -393,7 +393,6 @@ function menu_execute_active_handler($pa
     menu_rebuild();
   }
   if ($router_item = menu_get_item($path)) {
-    registry_load_path_files();
     if ($router_item['access']) {
       if (drupal_function_exists($router_item['page_callback'])) {
         return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.132
diff -u -p -r1.132 module.inc
--- includes/module.inc	31 Oct 2008 02:18:22 -0000	1.132
+++ includes/module.inc	11 Nov 2008 04:07:33 -0000
@@ -90,7 +90,7 @@ function module_list($refresh = FALSE, $
  *   The array of filesystem objects used to rebuild the cache.
  */
 function module_rebuild_cache() {
-  // Get current list of modules
+  // Get current list of modules, including uninstalled modules.
   $files = drupal_system_listing('/\.module$/', 'modules', 'name', 0);
 
   // Extract current files from database.
@@ -108,7 +108,7 @@ function module_rebuild_cache() {
     'php' => DRUPAL_MINIMUM_PHP,
     'files' => array(),
   );
-
+  
   foreach ($files as $filename => $file) {
     // Look for the info file.
     $file->info = drupal_parse_info_file(dirname($file->filename) . '/' . $file->name . '.info');
@@ -128,7 +128,8 @@ function module_rebuild_cache() {
     // Log the critical hooks implemented by this module.
     $bootstrap = 0;
     foreach (bootstrap_hooks() as $hook) {
-      if (module_hook($file->name, $hook)) {
+      // Only look for hooks in installed modules.
+      if ((isset($file->status) && $file->status) && in_array($file->name, module_implements($hook))) {
         $bootstrap = 1;
         break;
       }
Index: includes/registry.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/registry.inc,v
retrieving revision 1.7
diff -u -p -r1.7 registry.inc
--- includes/registry.inc	31 Oct 2008 02:18:22 -0000	1.7
+++ includes/registry.inc	11 Nov 2008 04:07:33 -0000
@@ -69,10 +69,26 @@ function _registry_rebuild() {
         ->execute();
     }
   }
-  _registry_parse_files($files);
+  $parsed_files = _registry_parse_files($files);
+
+  $unchanged_resources = array();
+  foreach (_registry_get_lookup_cache() as $key => $file) {
+    // If the file for this cached resource is carried over unchanged from 
+    // the last registry build, then we can safely re-cache it.
+    if ($file && in_array($file, array_keys($files)) && !in_array($file, $parsed_files)) {
+      $unchanged_resources[$key] = $file;
+    }
+  }
+  _registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
 
   module_implements(MODULE_IMPLEMENTS_CLEAR_CACHE);
   cache_clear_all('*', 'cache_registry', TRUE);
+
+  // We have some unchanged resources, warm up the cache - no need to pay
+  // for looking them up again.
+  if (count($unchanged_resources) > 0) {
+    _registry_set_lookup_cache($unchanged_resources);
+  }
 }
 
 /**
@@ -92,12 +108,13 @@ function registry_get_parsed_files() {
  *  The list of files to check and parse.
  */
 function _registry_parse_files($files) {
-  $changed_files = array();
+  $parsed_files = array();
   foreach ($files as $filename => $file) {
     $contents = file_get_contents($filename);
     $md5 = md5($contents);
     $new_file = !isset($file['md5']);
     if ($new_file || $md5 != $file['md5']) {
+      $parsed_files[] = $filename;
       // We update the md5 after we've saved the files resources rather than here, so if we
       // don't make it through this rebuild, the next run will reparse the file.
       _registry_parse_file($filename, $contents, $file['module'], $file['weight']);
@@ -108,6 +125,7 @@ function _registry_parse_files($files) {
         ->execute();
     }
   }
+  return $parsed_files;
 }
 
 /**
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.313
diff -u -p -r1.313 block.module
--- modules/block/block.module	8 Nov 2008 20:43:54 -0000	1.313
+++ modules/block/block.module	11 Nov 2008 04:07:33 -0000
@@ -239,7 +239,7 @@ function _block_rehash() {
   // Valid region names for the theme.
   $regions = system_region_list($theme_key);
 
-  foreach (module_list() as $module) {
+  foreach (module_implements('block') as $module) {
     $module_blocks = module_invoke($module, 'block', 'list');
     if ($module_blocks) {
       foreach ($module_blocks as $delta => $block) {
Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.103
diff -u -p -r1.103 system.admin.inc
--- modules/system/system.admin.inc	10 Nov 2008 05:23:00 -0000	1.103
+++ modules/system/system.admin.inc	11 Nov 2008 04:07:34 -0000
@@ -657,7 +657,7 @@ function system_modules($form_state = ar
       }
     }
     // Generate link for module's help page, if there is one.
-    if ($help_arg && module_hook($filename, 'help')) {
+    if ($help_arg && $module->status && in_array($filename, module_implements('help'))) {
       if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) {
         // Module has a help page.
         $extra['help'] = theme('more_help_link', url("admin/help/$filename"));
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.635
diff -u -p -r1.635 system.module
--- modules/system/system.module	31 Oct 2008 02:18:22 -0000	1.635
+++ modules/system/system.module	11 Nov 2008 04:07:35 -0000
@@ -1380,7 +1380,7 @@ function system_admin_compact_page($mode
  */
 function system_get_module_admin_tasks($module) {
   static $items;
-
+  
   if (!isset($items)) {
     $result = db_query("
        SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, ml.*
@@ -1398,12 +1398,12 @@ function system_get_module_admin_tasks($
   $admin_tasks = array();
   $admin_task_count = 0;
   // Check for permissions.
-  if (module_hook($module, 'perm') && $admin_access) {
+  if (in_array($module, module_implements('perm')) && $admin_access) {
     $admin_tasks[-1] = l(t('Configure permissions'), 'admin/user/permissions', array('fragment' => 'module-' . $module));
   }
 
   // Check for menu items that are admin links.
-  if ($menu = module_invoke($module, 'menu')) {
+  if (in_array($module, module_implements('menu')) && $menu = module_invoke($module, 'menu')) {
     foreach (array_keys($menu) as $path) {
       if (isset($items[$path])) {
         $admin_tasks[$items[$path]['title'] . $admin_task_count ++] = l($items[$path]['title'], $path);
Index: modules/user/user.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v
retrieving revision 1.31
diff -u -p -r1.31 user.admin.inc
--- modules/user/user.admin.inc	10 Nov 2008 05:23:01 -0000	1.31
+++ modules/user/user.admin.inc	11 Nov 2008 04:07:35 -0000
@@ -510,7 +510,7 @@ function user_admin_perm($form_state, $r
   // Render role/permission overview:
   $options = array();
   $hide_descriptions = !system_admin_compact_mode();
-  foreach (module_list(FALSE, FALSE, TRUE) as $module) {
+  foreach (module_implements('perm') as $module) {
     if ($permissions = module_invoke($module, 'perm')) {
       $form['permission'][] = array(
         '#markup' => $module,
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.932
diff -u -p -r1.932 user.module
--- modules/user/user.module	10 Nov 2008 05:23:01 -0000	1.932
+++ modules/user/user.module	11 Nov 2008 04:07:35 -0000
@@ -1917,7 +1917,7 @@ function user_help($path, $arg) {
 function _user_categories($account) {
   $categories = array();
 
-  foreach (module_list() as $module) {
+  foreach (module_implements('user_categories') as $module) {
     if ($data = module_invoke($module, 'user_categories', NULL, $account, '')) {
       $categories = array_merge($data, $categories);
     }
