Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.207
diff -u -p -r1.207 module.inc
--- includes/module.inc	24 Nov 2010 16:47:44 -0000	1.207
+++ includes/module.inc	26 Nov 2010 02:23:19 -0000
@@ -592,7 +592,20 @@ function module_disable($module_list, $d
  *   implemented in that module.
  */
 function module_hook($module, $hook) {
-  return function_exists($module . '_' . $hook);
+  $function = $module . '_' . $hook;
+  if (function_exists($function)) {
+    return TRUE;
+  }
+  // If the hook implementation does not exist, check whether it may live in an
+  // optional include file registered via hook_hook_info().
+  $hook_info = module_hook_info();
+  if (isset($hook_info[$hook]['group'])) {
+    module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
+    if (function_exists($function)) {
+      return TRUE;
+    }
+  }
+  return FALSE;
 }
 
 /**
@@ -660,7 +673,9 @@ function module_implements($hook, $sort 
     $list = module_list(FALSE, FALSE, $sort);
     foreach ($list as $module) {
       $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
-      if (module_hook($module, $hook)) {
+      // Since module_hook() may needlessly try to load the include file again,
+      // function_exists() is used directly here.
+      if (function_exists($module . '_' . $hook)) {
         $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
       }
     }
@@ -678,9 +693,11 @@ function module_implements($hook, $sort 
         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.
-      if (!module_hook($module, $hook)) {
+      // implementations cache being rebuilt yet, so we check whether the
+      // function exists on each request to avoid undefined function errors.
+      // Since module_hook() may needlessly try to load the include file again,
+      // function_exists() is used directly here.
+      if (!function_exists($module . '_' . $hook)) {
         // Clear out the stale implementation from the cache and force a cache
         // refresh to forget about no longer existing hook implementations.
         unset($implementations[$hook][$module]);
@@ -696,9 +713,17 @@ function module_implements($hook, $sort 
  * Retrieve a list of what hooks are explicitly declared.
  */
 function module_hook_info() {
-  $hook_info = &drupal_static(__FUNCTION__, array());
+  // This function is indirectly invoked from bootstrap_invoke_all(), in which
+  // case common.inc, subsystems, and modules are not loaded yet, so it does not
+  // make sense to support hook groups resp. lazy-loaded include files prior to
+  // full bootstrap.
+  if (drupal_bootstrap(NULL, FALSE) != DRUPAL_BOOTSTRAP_FULL) {
+    return array();
+  }
+  $hook_info = &drupal_static(__FUNCTION__);
 
-  if (empty($hook_info)) {
+  if (!isset($hook_info)) {
+    $hook_info = array();
     $cache = cache_get('hook_info', 'cache_bootstrap');
     if ($cache === FALSE) {
       // Rebuild the cache and save it.
@@ -768,6 +793,7 @@ function module_invoke() {
     return call_user_func_array($module . '_' . $hook, $args);
   }
 }
+
 /**
  * Invoke a hook in all enabled modules that implement it.
  *
Index: modules/field/field.attach.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v
retrieving revision 1.97
diff -u -p -r1.97 field.attach.inc
--- modules/field/field.attach.inc	20 Nov 2010 19:57:01 -0000	1.97
+++ modules/field/field.attach.inc	26 Nov 2010 02:23:19 -0000
@@ -188,8 +188,14 @@ function _field_invoke($op, $entity_type
   foreach ($instances as $instance) {
     $field_name = $instance['field_name'];
     $field = field_info_field($field_name);
-    $function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
-    if (function_exists($function)) {
+    $function = FALSE;
+    if ($options['default']) {
+      $function = 'field_default_' . $op;
+    }
+    elseif (module_hook($field['module'], 'field_' . $op)) {
+      $function = $field['module'] . '_field_' . $op;
+    }
+    if ($function) {
       // Determine the list of languages to iterate on.
       $available_languages = field_available_languages($entity_type, $field);
       $languages = _field_language_suggestion($available_languages, $options['language'], $field_name);
@@ -298,8 +304,14 @@ function _field_invoke_multiple($op, $en
       $field_id = $instance['field_id'];
       $field_name = $instance['field_name'];
       $field = $field_info[$field_id];
-      $function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
-      if (function_exists($function)) {
+      $function = FALSE;
+      if ($options['default']) {
+        $function = 'field_default_' . $op;
+      }
+      elseif (module_hook($field['module'], 'field_' . $op)) {
+        $function = $field['module'] . '_field_' . $op;
+      }
+      if ($function) {
         // Add the field to the list of fields to invoke the hook on.
         if (!isset($fields[$field_id])) {
           $fields[$field_id] = $field;
@@ -325,7 +337,13 @@ function _field_invoke_multiple($op, $en
   // For each field, invoke the field hook and collect results.
   foreach ($fields as $field_id => $field) {
     $field_name = $field['field_name'];
-    $function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
+    $function = FALSE;
+    if ($options['default']) {
+      $function = 'field_default_' . $op;
+    }
+    elseif (module_hook($field['module'], 'field_' . $op)) {
+      $function = $field['module'] . '_field_' . $op;
+    }
     // Iterate over all the field translations.
     foreach ($grouped_items[$field_id] as $langcode => $items) {
       $results = $function($entity_type, $grouped_entities[$field_id], $field, $grouped_instances[$field_id], $langcode, $grouped_items[$field_id][$langcode], $a, $b);
Index: modules/field/field.default.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.default.inc,v
retrieving revision 1.41
diff -u -p -r1.41 field.default.inc
--- modules/field/field.default.inc	21 Nov 2010 19:09:18 -0000	1.41
+++ modules/field/field.default.inc	26 Nov 2010 02:23:19 -0000
@@ -157,8 +157,8 @@ function field_default_prepare_view($ent
 
   foreach ($modules as $module) {
     // Invoke hook_field_formatter_prepare_view().
-    $function = $module . '_field_formatter_prepare_view';
-    if (function_exists($function)) {
+    if (module_hook($module, 'field_formatter_prepare_view')) {
+      $function = $module . '_field_formatter_prepare_view';
       $function($entity_type, $grouped_entities[$module], $field, $grouped_instances[$module], $langcode, $grouped_items[$module], $grouped_displays[$module]);
     }
   }
@@ -203,8 +203,8 @@ function field_default_view($entity_type
   if ($display['type'] !== 'hidden') {
     // Calling the formatter function through module_invoke() can have a
     // performance impact on pages with many fields and values.
-    $function = $display['module'] . '_field_formatter_view';
-    if (function_exists($function)) {
+    if (module_hook($display['module'], 'field_formatter_view')) {
+      $function = $display['module'] . '_field_formatter_view';
       $elements = $function($entity_type, $entity, $field, $instance, $langcode, $items, $display);
 
       if ($elements) {
Index: modules/field/field.form.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.form.inc,v
retrieving revision 1.54
diff -u -p -r1.54 field.form.inc
--- modules/field/field.form.inc	20 Nov 2010 19:57:01 -0000	1.54
+++ modules/field/field.form.inc	26 Nov 2010 02:23:19 -0000
@@ -61,8 +61,8 @@ function field_default_form($entity_type
     // make it the $delta value.
     else {
       $delta = isset($get_delta) ? $get_delta : 0;
-      $function = $instance['widget']['module'] . '_field_widget_form';
-      if (function_exists($function)) {
+      if (module_hook($instance['widget']['module'], 'field_widget_form')) {
+        $function = $instance['widget']['module'] . '_field_widget_form';
         $element = array(
           '#entity_type' => $instance['entity_type'],
           '#bundle' => $instance['bundle'],
@@ -160,8 +160,8 @@ function field_multiple_value_form($fiel
 
   $field_elements = array();
 
-  $function = $instance['widget']['module'] . '_field_widget_form';
-  if (function_exists($function)) {
+  if (module_hook($instance['widget']['module'], 'field_widget_form')) {
+    $function = $instance['widget']['module'] . '_field_widget_form';
     for ($delta = 0; $delta <= $max; $delta++) {
       $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
       $element = array(
@@ -337,7 +337,7 @@ function field_default_form_errors($enti
 
   if (!empty($field_state['errors'])) {
     $function = $instance['widget']['module'] . '_field_widget_error';
-    $function_exists = function_exists($function);
+    $function_exists = module_hook($instance['widget']['module'], 'field_widget_error');
 
     // Locate the correct element in the the form.
     $element = drupal_array_get_nested_value($form_state['complete form'], $field_state['array_parents']);
Index: modules/field/field.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.module,v
retrieving revision 1.90
diff -u -p -r1.90 field.module
--- modules/field/field.module	21 Nov 2010 19:09:18 -0000	1.90
+++ modules/field/field.module	26 Nov 2010 02:24:16 -0000
@@ -110,6 +110,102 @@ define('FIELD_LOAD_REVISION', 'FIELD_LOA
 class FieldUpdateForbiddenException extends FieldException {}
 
 /**
+ * Implements hook_hook_info().
+ */
+function field_hook_info() {
+  $field_hooks = array(
+    'field_access',
+    'field_attach_create_bundle',
+    'field_attach_delete',
+    'field_attach_delete_bundle',
+    'field_attach_delete_revision',
+    'field_attach_form',
+    'field_attach_insert',
+    'field_attach_load',
+    'field_attach_prepare_translation_alter',
+    'field_attach_preprocess_alter',
+    'field_attach_presave',
+    'field_attach_purge',
+    'field_attach_rename_bundle',
+    'field_attach_submit',
+    'field_attach_update',
+    'field_attach_validate',
+    'field_attach_view_alter',
+    'field_available_languages_alter',
+    'field_create_field',
+    'field_create_instance',
+    'field_delete',
+    'field_delete_field',
+    'field_delete_instance',
+    'field_delete_revision',
+    'field_display_alter',
+    // @todo http://drupal.org/node/968264
+    'field_display_ENTITY_TYPE_alter',
+    'field_extra_fields',
+    'field_extra_fields_alter',
+    'field_extra_fields_display_alter',
+    'field_formatter_info',
+    'field_formatter_info_alter',
+    'field_formatter_prepare_view',
+    'field_formatter_settings_form',
+    'field_formatter_settings_summary',
+    'field_formatter_view',
+    'field_info',
+    'field_info_alter',
+    'field_info_max_weight',
+    'field_insert',
+    'field_instance_settings_form',
+    'field_is_empty',
+    'field_language_alter',
+    'field_load',
+    'field_prepare_translation',
+    'field_prepare_view',
+    'field_presave',
+    'field_purge_field',
+    'field_purge_field_instance',
+    'field_read_field',
+    'field_read_instance',
+    'field_settings_form',
+    'field_storage_create_field',
+    'field_storage_delete',
+    'field_storage_delete_field',
+    'field_storage_delete_instance',
+    'field_storage_delete_revision',
+    'field_storage_details',
+    'field_storage_details_alter',
+    'field_storage_info',
+    'field_storage_info_alter',
+    'field_storage_load',
+    'field_storage_pre_insert',
+    'field_storage_pre_load',
+    'field_storage_pre_update',
+    'field_storage_purge',
+    'field_storage_purge_field',
+    'field_storage_purge_field_instance',
+    'field_storage_query',
+    'field_storage_update_field',
+    'field_storage_write',
+    'field_update',
+    'field_update_field',
+    'field_update_forbid',
+    'field_update_instance',
+    'field_validate',
+    'field_widget_error',
+    'field_widget_form',
+    'field_widget_info',
+    'field_widget_info_alter',
+    'field_widget_properties_alter',
+    // @todo http://drupal.org/node/968264
+    'field_widget_properties_ENTITY_TYPE_alter',
+    'field_widget_settings_form',
+  );
+  $hooks = array_fill_keys($field_hooks, array(
+    'group' => 'field',
+  ));
+  return $hooks;
+}
+
+/**
  * Implements hook_flush_caches().
  */
 function field_flush_caches() {
@@ -286,8 +382,8 @@ function field_get_default_value($entity
  *   the array keys to ensure sequential deltas.
  */
 function _field_filter_items($field, $items) {
+  module_hook($field['module'], 'field_is_empty');
   $function = $field['module'] . '_field_is_empty';
-  function_exists($function);
   foreach ((array) $items as $delta => $item) {
     // Explicitly break if the function is undefined.
     if ($function($item, $field)) {
Index: modules/file/file.field.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/file.field.inc,v
retrieving revision 1.41
diff -u -p -r1.41 file.field.inc
--- modules/file/file.field.inc	22 Nov 2010 04:42:09 -0000	1.41
+++ modules/file/file.field.inc	26 Nov 2010 02:23:19 -0000
@@ -42,15 +42,18 @@ function file_field_settings_form($field
 
   $form['display_field'] = array(
     '#type' => 'checkbox',
-    '#title' => t('Enable <em>Display</em> field'),
+    '#title' => t('Allow users to choose if files should be shown when viewing content'),
     '#default_value' => $settings['display_field'],
-    '#description' => t('The display option allows users to choose if a file should be shown when viewing the content.'),
   );
   $form['display_default'] = array(
     '#type' => 'checkbox',
-    '#title' => t('Files displayed by default'),
+    '#title' => t('Display files by default'),
     '#default_value' => $settings['display_default'],
-    '#description' => t('This setting only has an effect if the display option is enabled.'),
+    '#states' => array(
+      'visible' => array(
+        ':input[name*="display_field"]' => array('checked' => TRUE),
+      ),
+    ),
   );
 
   $scheme_options = array();
@@ -486,6 +489,12 @@ function file_field_widget_form(&$form, 
     // Allows this field to return an array instead of a single value.
     '#extended' => TRUE,
   );
+  // file_field_widget_process() is defined in this file, so ensure that it is
+  // loaded when the form is processed.
+  $form_state['build_info']['files']['file'] = array(
+    'module' => 'file',
+    'name' => 'file.field',
+  );
 
   if ($field['cardinality'] == 1) {
     // If there's only one field, return it as delta 0.
Index: modules/file/file.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/file.module,v
retrieving revision 1.47
diff -u -p -r1.47 file.module
--- modules/file/file.module	23 Nov 2010 05:51:16 -0000	1.47
+++ modules/file/file.module	26 Nov 2010 02:23:19 -0000
@@ -6,9 +6,6 @@
  * Defines a "managed_file" Form API field and a "file" field for Field module.
  */
 
-// Load all Field module hooks for File.
-require_once DRUPAL_ROOT . '/modules/file/file.field.inc';
-
 /**
  * Implements hook_help().
  */
Index: modules/image/image.field.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/image/image.field.inc,v
retrieving revision 1.34
diff -u -p -r1.34 image.field.inc
--- modules/image/image.field.inc	31 Oct 2010 12:12:00 -0000	1.34
+++ modules/image/image.field.inc	26 Nov 2010 02:23:19 -0000
@@ -6,6 +6,10 @@
  * Implement an image field, based on the file module's file field.
  */
 
+// Image field depends on file field, so all field hook implementations of File
+// module need to be loaded.
+require_once DRUPAL_ROOT . '/modules/file/file.field.inc';
+
 /**
  * Implements hook_field_info().
  */
@@ -313,6 +317,12 @@ function image_field_widget_form(&$form,
     // Add all extra functionality provided by the image widget.
     $elements[$delta]['#process'][] = 'image_field_widget_process';
   }
+  // image_field_widget_process() is defined in this file, so ensure that it is
+  // loaded when the form is processed.
+  $form_state['build_info']['files']['image'] = array(
+    'module' => 'image',
+    'name' => 'image.field',
+  );
 
   if ($field['cardinality'] == 1) {
     // If there's only one field, return it as delta 0.
Index: modules/image/image.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/image/image.module,v
retrieving revision 1.54
diff -u -p -r1.54 image.module
--- modules/image/image.module	18 Nov 2010 05:36:27 -0000	1.54
+++ modules/image/image.module	26 Nov 2010 02:23:19 -0000
@@ -31,9 +31,6 @@ define('IMAGE_STORAGE_EDITABLE', IMAGE_S
  */
 define('IMAGE_STORAGE_MODULE', IMAGE_STORAGE_OVERRIDE | IMAGE_STORAGE_DEFAULT);
 
-// Load all Field module hooks for Image.
-require_once DRUPAL_ROOT . '/modules/image/image.field.inc';
-
 /**
  * Implement of hook_help().
  */
