=== modified file 'includes/module.inc'
--- includes/module.inc	2009-10-10 12:12:15 +0000
+++ includes/module.inc	2009-10-11 22:22:47 +0000
@@ -376,18 +376,25 @@ function module_implements($hook, $sort 
   }
 
   if (!isset($implementations[$hook])) {
+    $hook_info = module_hook_info();
     $implementations[$hook] = array();
     $list = module_list(FALSE, FALSE, $sort);
     foreach ($list as $module) {
-      if (module_hook($module, $hook)) {
-        $implementations[$hook][$module] = $module;
+      $include_file = FALSE;
+      if (module_hook($module, $hook) || (isset($hook_info[$hook]['group']) && $include_file = module_load_include('inc', $module, "$module.{$hook_info[$hook]['group']}") && module_hook($module, $hook))) {
+        $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
         // We added something to the cache, so write it when we are done.
         $implementations['#write_cache'] = TRUE;
       }
     }
   }
   else {
-    foreach ($implementations[$hook] as $module) {
+    foreach ($implementations[$hook] as $module => $group) {
+      // If this hook implementation is stored in a lazy-loaded file,
+      // include that file first.
+      if ($group) {
+        module_load_include('inc', $module, "$module.$group");
+      }
       // It is possible that a module removed a hook implementation without the
       // implementations cache being rebuilt yet, so we check module_hook() on
       // each request to avoid undefined function errors.
@@ -400,13 +407,46 @@ function module_implements($hook, $sort 
     }
   }
 
-  // The explicit cast forces a copy to be made. This is needed because
-  // $implementations[$hook] is only a reference to an element of
-  // $implementations and if there are nested foreaches (due to nested node
-  // API calls, for example), they would both manipulate the same array's
-  // references, which causes some modules' hooks not to be called.
-  // See also http://www.zend.com/zend/art/ref-count.php.
-  return (array)$implementations[$hook];
+  return array_keys($implementations[$hook]);
+}
+
+/**
+ * Retrieve a list of what hooks are explicitly declared.
+ */
+function module_hook_info() {
+  $hook_info = &drupal_static(__FUNCTION__, array());
+
+  if (empty($hook_info)) {
+    $cache = cache_get('hook_info');
+    if ($cache === FALSE) {
+      // rebuild the cache and save it.
+
+      // We can't use module_invoke_all() here or it would cause
+      // an infinite loop.
+      foreach (module_list() as $module) {
+        $function = $module . '_hook_info';
+        if (function_exists($function)) {
+          $result = $function();
+          if (isset($result) && is_array($result)) {
+            $hook_info = array_merge_recursive($hook_info, $result);
+          }
+        }
+      }
+      // We can't use drupal_alter() for the same reason as above.
+      foreach (module_list() as $module) {
+        $function = $module . '_hook_info_alter';
+        if (function_exists($function)) {
+          $function($hook_info);
+        }
+      }
+      cache_set('hook_info', $hook_info);
+    }
+    else {
+      $hook_info = $cache->data;
+    }
+  }
+
+  return $hook_info;
 }
 
 /**

=== modified file 'modules/node/node.module'
--- modules/node/node.module	2009-10-11 03:07:16 +0000
+++ modules/node/node.module	2009-10-11 22:17:42 +0000
@@ -117,50 +117,6 @@ function node_help($path, $arg) {
 }
 
 /**
- * Implement hook_theme().
- */
-function node_theme() {
-  return array(
-    'node' => array(
-      'arguments' => array('elements' => NULL),
-      'template' => 'node',
-    ),
-    'node_list' => array(
-      'arguments' => array('items' => NULL, 'title' => NULL),
-    ),
-    'node_search_admin' => array(
-      'arguments' => array('form' => NULL),
-    ),
-    'node_filter_form' => array(
-      'arguments' => array('form' => NULL),
-      'file' => 'node.admin.inc',
-    ),
-    'node_filters' => array(
-      'arguments' => array('form' => NULL),
-      'file' => 'node.admin.inc',
-    ),
-    'node_add_list' => array(
-      'arguments' => array('content' => NULL),
-      'file' => 'node.pages.inc',
-    ),
-    'node_form' => array(
-      'arguments' => array('form' => NULL),
-      'file' => 'node.pages.inc',
-    ),
-    'node_preview' => array(
-      'arguments' => array('node' => NULL),
-      'file' => 'node.pages.inc',
-    ),
-    'node_log_message' => array(
-      'arguments' => array('log' => NULL),
-    ),
-    'node_admin_overview' => array(
-      'arguments' => array('name' => NULL, 'type' => NULL),
-    ),
-  );
-}
-
-/**
  * Implement hook_cron().
  */
 function node_cron() {
@@ -1102,7 +1058,7 @@ function node_build($node, $build_mode =
   $build = $node->content;
   // We don't need duplicate rendering info in node->content.
   unset($node->content);
-  
+
   $build += array(
     '#theme' => 'node',
     '#node' => $node,
@@ -1274,7 +1230,7 @@ function template_preprocess_node(&$vari
 
   // Flatten the node object's member fields.
   $variables = array_merge((array)$node, $variables);
-  
+
   // Helpful $content variable for templates.
   foreach (element_children($variables['elements']) as $key) {
     $variables['content'][$key] = $variables['elements'][$key];
@@ -1448,7 +1404,7 @@ function node_search_admin() {
   $form['content_ranking']['info'] = array(
     '#value' => '<em>' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '</em>'
   );
-  
+
   // Note: reversed to reflect that higher number = higher ranking.
   $options = drupal_map_assoc(range(0, 10));
   foreach (module_invoke_all('ranking') as $var => $values) {
@@ -1473,7 +1429,7 @@ function node_search_execute($keys = NUL
     ->condition('n.status', 1)
     ->addTag('node_access')
     ->searchExpression($keys, 'node');
-  
+
   // Insert special keywords.
   $query->setOption('type', 'n.type');
   $query->setOption('language', 'n.language');
@@ -1484,10 +1440,10 @@ function node_search_execute($keys = NUL
   if (!$query->executeFirstPass()) {
     return array();
   }
-  
+
   // Add the ranking expressions.
   _node_rankings($query);
-  
+
   // Add a count query.
   $inner_query = clone $query;
   $count_query = db_select($inner_query->fields('i', array('sid')));
@@ -1496,7 +1452,7 @@ function node_search_execute($keys = NUL
   $find = $query
     ->limit(10)
     ->execute();
-  
+
   // Load results.
   $results = array();
   foreach ($find as $item) {
@@ -1504,14 +1460,14 @@ function node_search_execute($keys = NUL
     $node = node_load($item->sid);
     node_build_content($node, 'search_result');
     $node->rendered = drupal_render($node->content);
-  
+
     // Fetch comments for snippet.
     $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
     // Fetch terms for snippet.
     $node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node);
-  
+
     $extra = module_invoke_all('node_search_result', $node);
-  
+
     $results[] = array(
       'link' => url('node/' . $item->sid, array('absolute' => TRUE)),
       'type' => check_plain(node_type_get_name($node)),

=== added file 'modules/node/node.registry.inc'
--- modules/node/node.registry.inc	1970-01-01 00:00:00 +0000
+++ modules/node/node.registry.inc	2009-10-11 22:17:42 +0000
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Implement hook_theme().
+ */
+function node_theme() {
+  return array(
+    'node' => array(
+      'arguments' => array('elements' => NULL),
+      'template' => 'node',
+    ),
+    'node_list' => array(
+      'arguments' => array('items' => NULL, 'title' => NULL),
+    ),
+    'node_search_admin' => array(
+      'arguments' => array('form' => NULL),
+    ),
+    'node_filter_form' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'node.admin.inc',
+    ),
+    'node_filters' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'node.admin.inc',
+    ),
+    'node_add_list' => array(
+      'arguments' => array('content' => NULL),
+      'file' => 'node.pages.inc',
+    ),
+    'node_form' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'node.pages.inc',
+    ),
+    'node_preview' => array(
+      'arguments' => array('node' => NULL),
+      'file' => 'node.pages.inc',
+    ),
+    'node_log_message' => array(
+      'arguments' => array('log' => NULL),
+    ),
+    'node_admin_overview' => array(
+      'arguments' => array('name' => NULL, 'type' => NULL),
+    ),
+  );
+}

=== modified file 'modules/system/system.api.php'
--- modules/system/system.api.php	2009-10-11 03:07:16 +0000
+++ modules/system/system.api.php	2009-10-11 22:17:42 +0000
@@ -2362,5 +2362,30 @@ function hook_action_info_alter(&$action
 }
 
 /**
+ * Defines one or more hooks that are exposed by a module.
+ *
+ * Normally hooks do not need to be explicitly defined.  However,
+ * by declaring a hook explicitly a module may define a
+ * "hook group" for it.  Modules that implement a hook may
+ * then place their implementation in either $module.module or
+ * in $module.$group.inc.  If the hook is in the second file,
+ * that file will be lazy-loaded as needed.  In general, hooks
+ * that are rarely invoked and/or are very large should be placed
+ * in a separate include file while hooks that are very short
+ * or are very frequently called should be left in the main
+ * module file so that they are always available.
+ */
+function hook_hook_info() {
+  $hooks['menu'] = array(
+    'group' => 'registry',
+  );
+    $hooks['theme'] = array(
+    'group' => 'registry',
+  );
+
+  return $hooks;
+}
+
+/**
  * @} End of "addtogroup hooks".
  */

=== modified file 'modules/system/system.module'
--- modules/system/system.module	2009-10-10 16:48:37 +0000
+++ modules/system/system.module	2009-10-11 22:17:42 +0000
@@ -2386,6 +2386,20 @@ function system_get_module_admin_tasks($
 }
 
 /**
+ * Implement hook_hook_info();
+ */
+function system_hook_info() {
+  $hooks['menu'] = array(
+    'group' => 'registry',
+  );
+    $hooks['theme'] = array(
+    'group' => 'registry',
+  );
+
+  return $hooks;
+}
+
+/**
  * Implement hook_cron().
  *
  * Remove older rows from flood and batch table. Remove old temporary files.

