diff --git a/core/authorize.php b/core/authorize.php
index 7f80850..7e85e95 100644
--- a/core/authorize.php
+++ b/core/authorize.php
@@ -80,9 +80,9 @@ function authorize_access_allowed() {
 // display errors via the maintenance theme.
 $module_list['system']['filename'] = 'core/modules/system/system.module';
 $module_list['user']['filename'] = 'core/modules/user/user.module';
-module_list(NULL, $module_list);
-drupal_load('module', 'system');
-drupal_load('module', 'user');
+drupal_extension_handler()->setModuleList($module_list);
+drupal_extension_handler()->load('system');
+drupal_extension_handler()->load('user');
 
 // Initialize the language system.
 drupal_language_initialize();
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 4ebe881..08801f0 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -4,6 +4,8 @@
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\ExtensionHandler;
+use Drupal\Core\ExtensionHandlerMinimal;
 use Symfony\Component\ClassLoader\UniversalClassLoader;
 use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
 use Symfony\Component\DependencyInjection\Container;
@@ -896,6 +898,14 @@ function drupal_get_filename($type, $name, $filename = NULL) {
     // nothing
   }
   else {
+    if ($type == 'module') {
+      if (empty($files[$type])) {
+        $files[$type] = drupal_extension_handler()->getEnabledModules();
+      }
+      if (isset($files[$type][$name])) {
+        return $files[$type][$name];
+      }
+    }
     // Verify that we have an keyvalue service before using it. This is required
     // because this function is called during installation.
     // @todo Inject database connection into KeyValueStore\DatabaseStorage.
@@ -1127,8 +1137,9 @@ function drupal_page_is_cacheable($allow_caching = NULL) {
  * @see bootstrap_hooks()
  */
 function bootstrap_invoke_all($hook) {
-  foreach (module_list('bootstrap') as $module) {
-    drupal_load('module', $module);
+  $extension_handler = drupal_extension_handler();
+  foreach ($extension_handler->getBootstrapModules() as $module) {
+    $extension_handler->load($module);
     module_invoke($module, $hook);
   }
 }
@@ -1147,6 +1158,10 @@ function bootstrap_invoke_all($hook) {
  *   TRUE if the item is loaded or has already been loaded.
  */
 function drupal_load($type, $name) {
+  if ($type == 'module') {
+    return drupal_extension_handler()->load($name);
+  }
+
   // Once a file is included this can't be reversed during a request so do not
   // use drupal_static() here.
   static $files = array();
@@ -2462,6 +2477,78 @@ function drupal_container(Container $new_container = NULL) {
 }
 
 /**
+ * Retrieves the ExtensionHandler that manages the list of enabled modules.
+ *
+ * This function instantiates a new ExtensionHandlerMinimal if there is no
+ * 'extension_handler' service in the container. This is currently needed for
+ * running tests.
+ * @todo Figure out how to get tests to run without using this.
+ *
+ * @return Drupal\Core\ExtensionHandlerInterface
+ */
+function drupal_extension_handler() {
+  $extension_handler = &drupal_static(__FUNCTION__, NULL);
+  if (isset($extension_handler)) {
+    return $extension_handler;
+  }
+  if (drupal_container()->has('extension_handler')) {
+    $extension_handler = drupal_container()->get('extension_handler');
+  }
+  else {
+    $extension_handler = new ExtensionHandlerMinimal();
+  }
+  return $extension_handler;
+}
+
+/**
+ * Loads all the modules that have been enabled in the system table.
+ *
+ * @see ExtensionHandler::loadAll().
+ */
+function module_load_all($bootstrap = FALSE, $reset = FALSE, $loaded = FALSE) {
+  return drupal_extension_handler()->loadAll($bootstrap, $reset, $loaded);
+}
+
+/**
+ * Determines which modules are implementing a hook.
+ *
+ * @see ExtensionHandler::moduleImplements().
+ */
+function module_implements($hook) {
+  return drupal_extension_handler()->moduleImplements($hook);
+}
+
+/**
+ * Invokes a hook in all enabled modules that implement it.
+ *
+ * @see ExtensionHandler::moduleInvokeAll().
+ */
+function module_invoke_all($hook) {
+  $args = func_get_args();
+  // Remove $hook from the arguments.
+  unset($args[0]);
+  return drupal_extension_handler()->moduleInvokeAll($hook, $args);
+}
+
+/**
+ * Passes alterable variables to specific hook_TYPE_alter() implementations.
+ *
+ * @see ExtensionHandler::alter().
+ */
+function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
+  return drupal_extension_handler()->alter($type, $data, $context1, $context2);
+}
+
+/**
+ * Determines whether a given module exists.
+ *
+ * @see ExtensionHandler::moduleExists().
+ */
+function module_exists($module) {
+  return drupal_extension_handler()->moduleExists($module);
+}
+
+/**
  * Returns the state storage service.
  *
  * Use this to store machine-generated data, local to a specific environment
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 20d24a7..1b68880 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -6666,68 +6666,10 @@ function debug($data, $label = NULL, $print_r = FALSE) {
 }
 
 /**
- * Parses a dependency for comparison by drupal_check_incompatibility().
- *
- * @param $dependency
- *   A dependency string, for example 'foo (>=8.x-4.5-beta5, 3.x)'.
- *
- * @return
- *   An associative array with three keys:
- *   - 'name' includes the name of the thing to depend on (e.g. 'foo').
- *   - 'original_version' contains the original version string (which can be
- *     used in the UI for reporting incompatibilities).
- *   - 'versions' is a list of associative arrays, each containing the keys
- *     'op' and 'version'. 'op' can be one of: '=', '==', '!=', '<>', '<',
- *     '<=', '>', or '>='. 'version' is one piece like '4.5-beta3'.
- *   Callers should pass this structure to drupal_check_incompatibility().
- *
- * @see drupal_check_incompatibility()
- */
-function drupal_parse_dependency($dependency) {
-  // We use named subpatterns and support every op that version_compare
-  // supports. Also, op is optional and defaults to equals.
-  $p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
-  // Core version is always optional: 8.x-2.x and 2.x is treated the same.
-  $p_core = '(?:' . preg_quote(DRUPAL_CORE_COMPATIBILITY) . '-)?';
-  $p_major = '(?P<major>\d+)';
-  // By setting the minor version to x, branches can be matched.
-  $p_minor = '(?P<minor>(?:\d+|x)(?:-[A-Za-z]+\d+)?)';
-  $value = array();
-  $parts = explode('(', $dependency, 2);
-  $value['name'] = trim($parts[0]);
-  if (isset($parts[1])) {
-    $value['original_version'] = ' (' . $parts[1];
-    foreach (explode(',', $parts[1]) as $version) {
-      if (preg_match("/^\s*$p_op\s*$p_core$p_major\.$p_minor/", $version, $matches)) {
-        $op = !empty($matches['operation']) ? $matches['operation'] : '=';
-        if ($matches['minor'] == 'x') {
-          // Drupal considers "2.x" to mean any version that begins with
-          // "2" (e.g. 2.0, 2.9 are all "2.x"). PHP's version_compare(),
-          // on the other hand, treats "x" as a string; so to
-          // version_compare(), "2.x" is considered less than 2.0. This
-          // means that >=2.x and <2.x are handled by version_compare()
-          // as we need, but > and <= are not.
-          if ($op == '>' || $op == '<=') {
-            $matches['major']++;
-          }
-          // Equivalence can be checked by adding two restrictions.
-          if ($op == '=' || $op == '==') {
-            $value['versions'][] = array('op' => '<', 'version' => ($matches['major'] + 1) . '.x');
-            $op = '>=';
-          }
-        }
-        $value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']);
-      }
-    }
-  }
-  return $value;
-}
-
-/**
  * Checks whether a version is compatible with a given dependency.
  *
  * @param $v
- *   The parsed dependency structure from drupal_parse_dependency().
+ *   A parsed dependency structure e.g. from ExtensionHandler::parseDependency().
  * @param $current_version
  *   The version to check against (like 4.2).
  *
@@ -6735,7 +6677,7 @@ function drupal_parse_dependency($dependency) {
  *   NULL if compatible, otherwise the original dependency version string that
  *   caused the incompatibility.
  *
- * @see drupal_parse_dependency()
+ * @see ExtensionHandler::parseDependency()
  */
 function drupal_check_incompatibility($v, $current_version) {
   if (!empty($v['versions'])) {
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index ef50d04..614a412 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -330,6 +330,9 @@ function install_begin_request(&$install_state) {
     $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
       ->addArgument(new Reference('config.storage'))
       ->addArgument(new Reference('dispatcher'));
+    // Register an extension handler for managing enabled modules.
+    $container
+      ->register('extension_handler', 'Drupal\Core\ExtensionHandlerMinimal');
     drupal_container($container);
   }
 
@@ -337,10 +340,10 @@ function install_begin_request(&$install_state) {
   drupal_language_initialize();
 
   require_once DRUPAL_ROOT . '/core/includes/ajax.inc';
+
   // Override the module list with a minimal set of modules.
-  $module_list['system']['filename'] = 'core/modules/system/system.module';
-  module_list(NULL, $module_list);
-  drupal_load('module', 'system');
+  drupal_extension_handler()->setModuleList(array('system' => 'core/modules/system/system.module'));
+  drupal_extension_handler()->load('system');
 
   // Load the cache infrastructure using a "fake" cache implementation that
   // does not attempt to write to the database. We need this during the initial
@@ -1496,9 +1499,14 @@ function install_load_profile(&$install_state) {
  *   An array of information about the current installation state.
  */
 function install_bootstrap_full(&$install_state) {
-  // Clear the module list that was overriden earlier in the process.
-  // This will allow all freshly installed modules to be loaded.
-  module_list_reset();
+  // Clear the statically cached extension handler. This will allow all freshly
+  // installed modules to be loaded.
+  drupal_static_reset('drupal_extension_handler');
+
+  // Instantiate the kernel.
+  $kernel = new DrupalKernel('prod', FALSE, drupal_classloader(), FALSE);
+  $kernel->boot();
+
   drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 }
 
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 99cfcf6..044b94c 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -444,10 +444,8 @@ function drupal_install_system() {
     ->save();
 
   // Clear out module list and hook implementation statics.
-  system_list_reset();
-  module_list_reset();
-  module_implements_reset();
-
+  drupal_static_reset('drupal_extension_handler');
+  drupal_extension_handler()->setModuleList(array('system' => $system_path . '/system.module'));
   config_install_default_config('module', 'system');
 
   module_invoke('system', 'install');
diff --git a/core/includes/module.inc b/core/includes/module.inc
index abe03dd..f9dd1f7 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -9,114 +9,6 @@
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * Loads all enabled modules.
- *
- * @param bool $bootstrap
- *   Whether to load only the reduced set of modules loaded in "bootstrap mode"
- *   for cached pages. See bootstrap.inc. Pass NULL to only check the current
- *   status without loading of modules.
- * @param bool $reset
- *   (optional) Internal use only. Whether to reset the internal statically
- *   cached flag of whether modules have been loaded. If TRUE, all modules are
- *   (re)loaded in the same call. Used by the testing framework to override and
- *   persist a limited module list for the duration of a unit test (in which no
- *   module system exists).
- *
- * @return bool
- *   A Boolean indicating whether all modules have been loaded. This means all
- *   modules; the load status of bootstrap modules cannot be checked.
- */
-function module_load_all($bootstrap = FALSE, $reset = FALSE) {
-  static $has_run = FALSE;
-
-  if ($reset) {
-    $has_run = FALSE;
-  }
-
-  // Unless $boostrap is NULL, load the requested set of modules.
-  if (isset($bootstrap) && !$has_run) {
-    $type = $bootstrap ? 'bootstrap' : 'module_enabled';
-    foreach (module_list($type) as $module) {
-      drupal_load('module', $module);
-    }
-    // $has_run will be TRUE if $bootstrap is FALSE.
-    $has_run = !$bootstrap;
-  }
-  return $has_run;
-}
-
-/**
- * Returns a list of currently active modules.
- *
- * Acts as a wrapper around system_list(), returning either a list of all
- * enabled modules, or just modules needed for bootstrap.
- *
- * The returned module list is always based on system_list(). The only exception
- * to that is when a fixed list of modules has been passed in previously, in
- * which case system_list() is omitted and the fixed list is always returned in
- * subsequent calls until manually reverted via module_list_reset().
- *
- * @param string $type
- *   The type of list to return:
- *   - module_enabled: All enabled modules.
- *   - bootstrap: All enabled modules required for bootstrap.
- * @param array $fixed_list
- *   (optional) An array of module names to override the list of modules. This
- *   list will persist until the next call with a new $fixed_list passed in.
- *   Primarily intended for internal use (e.g., in install.php and update.php).
- *   Use module_list_reset() to undo the $fixed_list override.
- * @param bool $reset
- *   (optional) Whether to reset/remove the $fixed_list.
- *
- * @return array
- *   An associative array whose keys and values are the names of the modules in
- *   the list.
- *
- * @see module_list_reset()
- */
-function module_list($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) {
-  // This static is only used for $fixed_list. It must not be a drupal_static(),
-  // since any call to drupal_static_reset() in unit tests would cause an
-  // attempt to retrieve the list of modules from the database (which does not
-  // exist).
-  static $module_list;
-
-  if ($reset) {
-    $module_list = NULL;
-    // Do nothing if no $type and no $fixed_list have been passed.
-    if (!isset($type) && !isset($fixed_list)) {
-      return;
-    }
-  }
-
-  // The list that will be be returned. Separate from $module_list in order
-  // to not duplicate the static cache of system_list().
-  $list = $module_list;
-
-  if (isset($fixed_list)) {
-    $module_list = array();
-    foreach ($fixed_list as $name => $module) {
-      system_register('module', $name, $module['filename']);
-      $module_list[$name] = $name;
-    }
-    $list = $module_list;
-  }
-  elseif (!isset($module_list)) {
-    $list = system_list($type);
-  }
-  return $list;
-}
-
-/**
- * Reverts an enforced fixed list of module_list().
- *
- * Subsequent calls to module_list() will no longer use a fixed list.
- */
-function module_list_reset() {
-  module_list(NULL, NULL, TRUE);
-}
-
-/**
  * Builds a list of bootstrap modules and enabled modules and themes.
  *
  * @param $type
@@ -131,7 +23,6 @@ function module_list_reset() {
  *   For $type 'theme', the array values are objects representing the
  *   respective database row, with the 'info' property already unserialized.
  *
- * @see module_list()
  * @see list_themes()
  *
  * @todo There are too many layers/levels of caching involved for system_list()
@@ -141,126 +32,80 @@ function module_list_reset() {
  */
 function system_list($type) {
   $lists = &drupal_static(__FUNCTION__);
-
-  // For bootstrap modules, attempt to fetch the list from cache if possible.
-  // if not fetch only the required information to fire bootstrap hooks
-  // in case we are going to serve the page from cache.
-  if ($type == 'bootstrap') {
-    if (isset($lists['bootstrap'])) {
-      return $lists['bootstrap'];
-    }
-    if ($cached = cache('bootstrap')->get('bootstrap_modules')) {
-      $bootstrap_list = $cached->data;
-    }
-    else {
-      $bootstrap_list = state()->get('system.module.bootstrap') ?: array();
-      cache('bootstrap')->set('bootstrap_modules', $bootstrap_list);
-    }
-    // To avoid a separate database lookup for the filepath, prime the
-    // drupal_get_filename() static cache for bootstrap modules only.
-    // The rest is stored separately to keep the bootstrap module cache small.
-    foreach ($bootstrap_list as $name => $filename) {
-      system_register('module', $name, $filename);
-    }
-    // We only return the module names here since module_list() doesn't need
-    // the filename itself.
-    $lists['bootstrap'] = array_keys($bootstrap_list);
+  if ($cached = cache('bootstrap')->get('system_list')) {
+    $lists = $cached->data;
   }
-  // Otherwise build the list for enabled modules and themes.
-  elseif (!isset($lists['module_enabled'])) {
-    if ($cached = cache('bootstrap')->get('system_list')) {
-      $lists = $cached->data;
-    }
-    else {
-      $lists = array(
-        'module_enabled' => array(),
-        'theme' => array(),
-        'filepaths' => array(),
-      );
-      // The module name (rather than the filename) is used as the fallback
-      // weighting in order to guarantee consistent behavior across different
-      // Drupal installations, which might have modules installed in different
-      // locations in the file system. The ordering here must also be
-      // consistent with the one used in module_implements().
-      $enabled_modules = (array) config('system.module')->get('enabled');
-      $module_files = state()->get('system.module.files');
-      foreach ($enabled_modules as $name => $weight) {
-        // Build a list of all enabled modules.
-        $lists['module_enabled'][$name] = $name;
-        // Build a list of filenames so drupal_get_filename can use it.
+  else {
+    $lists = array(
+      'theme' => array(),
+      'filepaths' => array(),
+    );
+    // Build a list of themes.
+    $enabled_themes = (array) config('system.theme')->get('enabled');
+    // @todo Themes include all themes, including disabled/uninstalled. This
+    //   system.theme.data state will go away entirely as soon as themes have
+    //   a proper installation status.
+    // @see http://drupal.org/node/1067408
+    $theme_data = state()->get('system.theme.data');
+    if (empty($theme_data)) {
+      // @todo: system_list() may be called from _drupal_bootstrap_code() and
+      // module_load_all(), in which case system.module is not loaded yet.
+      // Prevent a filesystem scan in drupal_load() and include it directly.
+      // @see http://drupal.org/node/1067408
+      require_once DRUPAL_ROOT . '/core/modules/system/system.module';
+      $theme_data = system_rebuild_theme_data();
+    }
+    foreach ($theme_data as $name => $theme) {
+      $theme->status = (int) isset($enabled_themes[$name]);
+      $lists['theme'][$name] = $theme;
+      // Build a list of filenames so drupal_get_filename can use it.
+      if (isset($enabled_themes[$name])) {
         $lists['filepaths'][] = array(
-          'type' => 'module',
+          'type' => 'theme',
           'name' => $name,
-          'filepath' => $module_files[$name],
+          'filepath' => $theme->filename,
         );
       }
-
-      // Build a list of themes.
-      $enabled_themes = (array) config('system.theme')->get('enabled');
-      // @todo Themes include all themes, including disabled/uninstalled. This
-      //   system.theme.data state will go away entirely as soon as themes have
-      //   a proper installation status.
-      // @see http://drupal.org/node/1067408
-      $theme_data = state()->get('system.theme.data');
-      if (empty($theme_data)) {
-        // @todo: system_list() may be called from _drupal_bootstrap_code() and
-        // module_load_all(), in which case system.module is not loaded yet.
-        // Prevent a filesystem scan in drupal_load() and include it directly.
-        // @see http://drupal.org/node/1067408
-        require_once DRUPAL_ROOT . '/core/modules/system/system.module';
-        $theme_data = system_rebuild_theme_data();
-      }
-      foreach ($theme_data as $name => $theme) {
-        $theme->status = (int) isset($enabled_themes[$name]);
-        $lists['theme'][$name] = $theme;
-        // Build a list of filenames so drupal_get_filename can use it.
-        if (isset($enabled_themes[$name])) {
-          $lists['filepaths'][] = array(
-            'type' => 'theme',
-            'name' => $name,
-            'filepath' => $theme->filename,
-          );
-        }
-      }
-      // @todo Move into list_themes(). Read info for a particular requested
-      //   theme from state instead.
-      foreach ($lists['theme'] as $key => $theme) {
-        if (!empty($theme->info['base theme'])) {
-          // Make a list of the theme's base themes.
-          require_once DRUPAL_ROOT . '/core/includes/theme.inc';
-          $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key);
-          // Don't proceed if there was a problem with the root base theme.
-          if (!current($lists['theme'][$key]->base_themes)) {
-            continue;
-          }
-          // Determine the root base theme.
-          $base_key = key($lists['theme'][$key]->base_themes);
-          // Add to the list of sub-themes for each of the theme's base themes.
-          foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) {
-            $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name'];
-          }
-          // Add the base theme's theme engine info.
-          $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_key]->info['engine'];
+    }
+    // @todo Move into list_themes(). Read info for a particular requested
+    //   theme from state instead.
+    foreach ($lists['theme'] as $key => $theme) {
+      if (!empty($theme->info['base theme'])) {
+        // Make a list of the theme's base themes.
+        require_once DRUPAL_ROOT . '/core/includes/theme.inc';
+        $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key);
+        // Don't proceed if there was a problem with the root base theme.
+        if (!current($lists['theme'][$key]->base_themes)) {
+          continue;
         }
-        else {
-          // A plain theme is its own base theme.
-          $base_key = $key;
+        // Determine the root base theme.
+        $base_key = key($lists['theme'][$key]->base_themes);
+        // Add to the list of sub-themes for each of the theme's base themes.
+        foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) {
+          $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name'];
         }
-        // Set the theme engine prefix.
-        $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];
+        // Add the base theme's theme engine info.
+        $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_key]->info['engine'];
       }
-      cache('bootstrap')->set('system_list', $lists);
-    }
-    // To avoid a separate database lookup for the filepath, prime the
-    // drupal_get_filename() static cache with all enabled modules and themes.
-    foreach ($lists['filepaths'] as $item) {
-      system_register($item['type'], $item['name'], $item['filepath']);
+      else {
+        // A plain theme is its own base theme.
+        $base_key = $key;
+      }
+      // Set the theme engine prefix.
+      $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];
     }
+    cache('bootstrap')->set('system_list', $lists);
+  }
+  // To avoid a separate database lookup for the filepath, prime the
+  // drupal_get_filename() static cache with all enabled modules and themes.
+  foreach ($lists['filepaths'] as $item) {
+    system_register($item['type'], $item['name'], $item['filepath']);
   }
 
   return $lists[$type];
 }
 
+
 /**
  * Resets all system_list() caches.
  */
@@ -280,6 +125,7 @@ function system_list_reset() {
   state()->delete('system.theme.data');
 }
 
+
 /**
  * Registers an extension in runtime registries for execution.
  *
@@ -297,53 +143,6 @@ function system_register($type, $name, $uri) {
 }
 
 /**
- * Determines which modules require and are required by each module.
- *
- * @param $files
- *   The array of filesystem objects used to rebuild the cache.
- *
- * @return
- *   The same array with the new keys for each module:
- *   - requires: An array with the keys being the modules that this module
- *     requires.
- *   - required_by: An array with the keys being the modules that will not work
- *     without this module.
- */
-function _module_build_dependencies($files) {
-  foreach ($files as $filename => $file) {
-    $graph[$file->name]['edges'] = array();
-    if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
-      foreach ($file->info['dependencies'] as $dependency) {
-        $dependency_data = drupal_parse_dependency($dependency);
-        $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data;
-      }
-    }
-  }
-  $graph_object = new Graph($graph);
-  $graph = $graph_object->searchAndSort();
-  foreach ($graph as $module => $data) {
-    $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array();
-    $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array();
-    $files[$module]->sort = $data['weight'];
-  }
-  return $files;
-}
-
-/**
- * Determines whether a given module exists.
- *
- * @param $module
- *   The name of the module (without the .module extension).
- *
- * @return
- *   TRUE if the module is both installed and enabled.
- */
-function module_exists($module) {
-  $list = module_list();
-  return isset($list[$module]);
-}
-
-/**
  * Loads a module's installation hooks.
  *
  * @param $module
@@ -401,15 +200,6 @@ function module_load_include($type, $module, $name = NULL) {
   return FALSE;
 }
 
-/**
- * Loads an include file for each enabled module.
- */
-function module_load_all_includes($type, $name = NULL) {
-  $modules = module_list();
-  foreach ($modules as $module) {
-    module_load_include($type, $module, $name);
-  }
-}
 
 /**
  * Enables or installs a given list of modules.
@@ -493,9 +283,11 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
   $schema_store = drupal_container()->get('keyvalue')->get('system.schema');
   $module_config = config('system.module');
   $disabled_config = config('system.module.disabled');
+  $extension_handler = drupal_extension_handler();
   foreach ($module_list as $module) {
+    $enabled_modules = $extension_handler->getEnabledModules();
     // Only process modules that are not already enabled.
-    $enabled = $module_config->get("enabled.$module") !== NULL;
+    $enabled = isset($enabled_modules[$module]);
     if (!$enabled) {
       $weight = $disabled_config->get($module);
       if ($weight === NULL) {
@@ -508,20 +300,32 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
       $disabled_config
         ->clear($module)
         ->save();
+
+      $filename = drupal_get_filename('module', $module);
+      $enabled_modules[$module] = $filename;
+      $sorted_modules = $module_config->get('enabled');
+      $sorted_with_filenames = array();
+      foreach (array_keys($sorted_modules) as $m) {
+        $sorted_with_filenames[$m] = $enabled_modules[$m];
+      }
+      // Refresh the module list in the extension handler.
+      $extension_handler->setModuleList($sorted_with_filenames);
       // Load the module's code.
-      drupal_load('module', $module);
+      drupal_extension_handler()->load($module);
       module_load_install($module);
 
       // Refresh the module list to include it.
-      system_list_reset();
-      module_implements_reset();
+      drupal_extension_handler()->moduleImplementsReset();
+
       _system_update_bootstrap_status();
-      // Update the kernel to include it.
+
+      // Update the kernel to include the new module
       // @todo The if statement is here because install_begin_request() creates
       //   a container without a kernel. It probably shouldn't.
       if ($kernel = drupal_container()->get('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
-        $kernel->updateModules(module_list(), array($module => drupal_get_filename('module', $module)));
+        $kernel->updateModules($sorted_modules, $sorted_with_filenames);
       }
+
       // Refresh the schema to include it.
       drupal_get_schema(NULL, TRUE);
       // Update the theme registry to include it.
@@ -629,8 +433,10 @@ function module_disable($module_list, $disable_dependents = TRUE) {
 
   $module_config = config('system.module');
   $disabled_config = config('system.module.disabled');
+  $extension_handler = drupal_extension_handler();
   foreach ($module_list as $module) {
-    if (module_exists($module)) {
+    $enabled = $extension_handler->getEnabledModules();
+    if (isset($enabled[$module])) {
       module_load_install($module);
       module_invoke($module, 'disable');
       $disabled_config
@@ -639,6 +445,8 @@ function module_disable($module_list, $disable_dependents = TRUE) {
       $module_config
         ->clear("enabled.$module")
         ->save();
+      unset($enabled[$module]);
+      $extension_handler->setModuleList($enabled);
       $invoke_modules[] = $module;
       watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO);
     }
@@ -646,15 +454,14 @@ function module_disable($module_list, $disable_dependents = TRUE) {
 
   if (!empty($invoke_modules)) {
     // Refresh the module list to exclude the disabled modules.
-    system_list_reset();
-    module_implements_reset();
+    drupal_extension_handler()->moduleImplementsReset();
     entity_info_cache_clear();
     // Invoke hook_modules_disabled before disabling modules,
     // so we can still call module hooks to get information.
     module_invoke_all('modules_disabled', $invoke_modules);
     _system_update_bootstrap_status();
     // Update the kernel to exclude the disabled modules.
-    drupal_container()->get('kernel')->updateModules(module_list());
+    drupal_container()->get('kernel')->updateModules($module_config->get('enabled'), $enabled);
     // Update the theme registry to remove the newly-disabled module.
     drupal_theme_rebuild();
   }
@@ -781,7 +588,7 @@ function module_hook($module, $hook) {
   }
   // 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();
+  $hook_info = drupal_extension_handler()->moduleHookInfo();
   if (isset($hook_info[$hook]['group'])) {
     module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
     if (function_exists($function)) {
@@ -792,171 +599,6 @@ function module_hook($module, $hook) {
 }
 
 /**
- * Determines which modules are implementing a hook.
- *
- * @param $hook
- *   The name of the hook (e.g. "help" or "menu").
- *
- * @return
- *   An array with the names of the modules which are implementing this hook.
- *
- * @see module_implements_write_cache()
- */
-function module_implements($hook) {
-  // Use the advanced drupal_static() pattern, since this is called very often.
-  static $drupal_static_fast;
-  if (!isset($drupal_static_fast)) {
-    $drupal_static_fast['implementations'] = &drupal_static(__FUNCTION__);
-  }
-  $implementations = &$drupal_static_fast['implementations'];
-
-  // Fetch implementations from cache.
-  if (empty($implementations)) {
-    $implementations = cache('bootstrap')->get('module_implements');
-    if ($implementations === FALSE) {
-      $implementations = array();
-    }
-    else {
-      $implementations = $implementations->data;
-    }
-  }
-
-  if (!isset($implementations[$hook])) {
-    // The hook is not cached, so ensure that whether or not it has
-    // implementations, that the cache is updated at the end of the request.
-    $implementations['#write_cache'] = TRUE;
-    $hook_info = module_hook_info();
-    $implementations[$hook] = array();
-    foreach (module_list() as $module) {
-      $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
-      // 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;
-      }
-    }
-    // Allow modules to change the weight of specific implementations but avoid
-    // an infinite loop.
-    if ($hook != 'module_implements_alter') {
-      drupal_alter('module_implements', $implementations[$hook], $hook);
-    }
-  }
-  else {
-    foreach ($implementations[$hook] as $module => $group) {
-      // If this hook implementation is stored in a lazy-loaded file, so 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 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]);
-        $implementations['#write_cache'] = TRUE;
-      }
-    }
-  }
-
-  return array_keys($implementations[$hook]);
-}
-
-/**
- * Regenerates the stored list of hook implementations.
- */
-function module_implements_reset() {
-  // We maintain a persistent cache of hook implementations in addition to the
-  // static cache to avoid looping through every module and every hook on each
-  // request. Benchmarks show that the benefit of this caching outweighs the
-  // additional database hit even when using the default database caching
-  // backend and only a small number of modules are enabled. The cost of the
-  // cache('bootstrap')->get() is more or less constant and reduced further when
-  // non-database caching backends are used, so there will be more significant
-  // gains when a large number of modules are installed or hooks invoked, since
-  // this can quickly lead to module_hook() being called several thousand times
-  // per request.
-  drupal_static_reset('module_implements');
-  cache('bootstrap')->set('module_implements', array());
-  drupal_static_reset('module_hook_info');
-  drupal_static_reset('drupal_alter');
-  cache('bootstrap')->delete('hook_info');
-}
-
-/**
- * Retrieves a list of hooks that are declared through hook_hook_info().
- *
- * @return
- *   An associative array whose keys are hook names and whose values are an
- *   associative array containing a group name. The structure of the array
- *   is the same as the return value of hook_hook_info().
- *
- * @see hook_hook_info()
- */
-function module_hook_info() {
-  // When this function is indirectly invoked from bootstrap_invoke_all() prior
-  // to all modules being loaded, we do not want to cache an incomplete
-  // hook_hook_info() result, so instead return an empty array. This requires
-  // bootstrap hook implementations to reside in the .module file, which is
-  // optimal for performance anyway.
-  if (!module_load_all(NULL)) {
-    return array();
-  }
-  $hook_info = &drupal_static(__FUNCTION__);
-
-  if (!isset($hook_info)) {
-    $hook_info = array();
-    $cache = cache('bootstrap')->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('bootstrap')->set('hook_info', $hook_info);
-    }
-    else {
-      $hook_info = $cache->data;
-    }
-  }
-
-  return $hook_info;
-}
-
-/**
- * Writes the hook implementation cache.
- *
- * @see module_implements()
- */
-function module_implements_write_cache() {
-  $implementations = &drupal_static('module_implements');
-  // Check whether we need to write the cache. We do not want to cache hooks
-  // which are only invoked on HTTP POST requests since these do not need to be
-  // optimized as tightly, and not doing so keeps the cache entry smaller.
-  if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) {
-    unset($implementations['#write_cache']);
-    cache('bootstrap')->set('module_implements', $implementations);
-  }
-}
-
-/**
  * Invokes a hook in a particular module.
  *
  * @param $module
@@ -979,39 +621,6 @@ function module_invoke($module, $hook) {
 }
 
 /**
- * Invokes a hook in all enabled modules that implement it.
- *
- * @param $hook
- *   The name of the hook to invoke.
- * @param ...
- *   Arguments to pass to the hook.
- *
- * @return
- *   An array of return values of the hook implementations. If modules return
- *   arrays from their implementations, those are merged into one array.
- */
-function module_invoke_all($hook) {
-  $args = func_get_args();
-  // Remove $hook from the arguments.
-  unset($args[0]);
-  $return = array();
-  foreach (module_implements($hook) as $module) {
-    $function = $module . '_' . $hook;
-    if (function_exists($function)) {
-      $result = call_user_func_array($function, $args);
-      if (isset($result) && is_array($result)) {
-        $return = array_merge_recursive($return, $result);
-      }
-      elseif (isset($result)) {
-        $return[] = $result;
-      }
-    }
-  }
-
-  return $return;
-}
-
-/**
  * @} End of "defgroup hooks".
  */
 
@@ -1036,172 +645,6 @@ function drupal_required_modules() {
 }
 
 /**
- * Passes alterable variables to specific hook_TYPE_alter() implementations.
- *
- * This dispatch function hands off the passed-in variables to type-specific
- * hook_TYPE_alter() implementations in modules. It ensures a consistent
- * interface for all altering operations.
- *
- * A maximum of 2 alterable arguments is supported. In case more arguments need
- * to be passed and alterable, modules provide additional variables assigned by
- * reference in the last $context argument:
- * @code
- *   $context = array(
- *     'alterable' => &$alterable,
- *     'unalterable' => $unalterable,
- *     'foo' => 'bar',
- *   );
- *   drupal_alter('mymodule_data', $alterable1, $alterable2, $context);
- * @endcode
- *
- * Note that objects are always passed by reference in PHP5. If it is absolutely
- * required that no implementation alters a passed object in $context, then an
- * object needs to be cloned:
- * @code
- *   $context = array(
- *     'unalterable_object' => clone $object,
- *   );
- *   drupal_alter('mymodule_data', $data, $context);
- * @endcode
- *
- * @param $type
- *   A string describing the type of the alterable $data. 'form', 'links',
- *   'node_content', and so on are several examples. Alternatively can be an
- *   array, in which case hook_TYPE_alter() is invoked for each value in the
- *   array, ordered first by module, and then for each module, in the order of
- *   values in $type. For example, when Form API is using drupal_alter() to
- *   execute both hook_form_alter() and hook_form_FORM_ID_alter()
- *   implementations, it passes array('form', 'form_' . $form_id) for $type.
- * @param $data
- *   The variable that will be passed to hook_TYPE_alter() implementations to be
- *   altered. The type of this variable depends on the value of the $type
- *   argument. For example, when altering a 'form', $data will be a structured
- *   array. When altering a 'profile', $data will be an object.
- * @param $context1
- *   (optional) An additional variable that is passed by reference.
- * @param $context2
- *   (optional) An additional variable that is passed by reference. If more
- *   context needs to be provided to implementations, then this should be an
- *   associative array as described above.
- */
-function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
-  // Use the advanced drupal_static() pattern, since this is called very often.
-  static $drupal_static_fast;
-  if (!isset($drupal_static_fast)) {
-    $drupal_static_fast['functions'] = &drupal_static(__FUNCTION__);
-  }
-  $functions = &$drupal_static_fast['functions'];
-
-  // Most of the time, $type is passed as a string, so for performance,
-  // normalize it to that. When passed as an array, usually the first item in
-  // the array is a generic type, and additional items in the array are more
-  // specific variants of it, as in the case of array('form', 'form_FORM_ID').
-  if (is_array($type)) {
-    $cid = implode(',', $type);
-    $extra_types = $type;
-    $type = array_shift($extra_types);
-    // Allow if statements in this function to use the faster isset() rather
-    // than !empty() both when $type is passed as a string, or as an array with
-    // one item.
-    if (empty($extra_types)) {
-      unset($extra_types);
-    }
-  }
-  else {
-    $cid = $type;
-  }
-
-  // Some alter hooks are invoked many times per page request, so statically
-  // cache the list of functions to call, and on subsequent calls, iterate
-  // through them quickly.
-  if (!isset($functions[$cid])) {
-    $functions[$cid] = array();
-    $hook = $type . '_alter';
-    $modules = module_implements($hook);
-    if (!isset($extra_types)) {
-      // For the more common case of a single hook, we do not need to call
-      // function_exists(), since module_implements() returns only modules with
-      // implementations.
-      foreach ($modules as $module) {
-        $functions[$cid][] = $module . '_' . $hook;
-      }
-    }
-    else {
-      // For multiple hooks, we need $modules to contain every module that
-      // implements at least one of them.
-      $extra_modules = array();
-      foreach ($extra_types as $extra_type) {
-        $extra_modules = array_merge($extra_modules, module_implements($extra_type . '_alter'));
-      }
-      // If any modules implement one of the extra hooks that do not implement
-      // the primary hook, we need to add them to the $modules array in their
-      // appropriate order. module_implements() can only return ordered
-      // implementations of a single hook. To get the ordered implementations
-      // of multiple hooks, we mimic the module_implements() logic of first
-      // ordering by module_list(), and then calling
-      // drupal_alter('module_implements').
-      if (array_diff($extra_modules, $modules)) {
-        // Merge the arrays and order by module_list().
-        $modules = array_intersect(module_list(), array_merge($modules, $extra_modules));
-        // Since module_implements() already took care of loading the necessary
-        // include files, we can safely pass FALSE for the array values.
-        $implementations = array_fill_keys($modules, FALSE);
-        // Let modules adjust the order solely based on the primary hook. This
-        // ensures the same module order regardless of whether this if block
-        // runs. Calling drupal_alter() recursively in this way does not result
-        // in an infinite loop, because this call is for a single $type, so we
-        // won't end up in this code block again.
-        drupal_alter('module_implements', $implementations, $hook);
-        $modules = array_keys($implementations);
-      }
-      foreach ($modules as $module) {
-        // Since $modules is a merged array, for any given module, we do not
-        // know whether it has any particular implementation, so we need a
-        // function_exists().
-        $function = $module . '_' . $hook;
-        if (function_exists($function)) {
-          $functions[$cid][] = $function;
-        }
-        foreach ($extra_types as $extra_type) {
-          $function = $module . '_' . $extra_type . '_alter';
-          if (function_exists($function)) {
-            $functions[$cid][] = $function;
-          }
-        }
-      }
-    }
-    // Allow the theme to alter variables after the theme system has been
-    // initialized.
-    global $theme, $base_theme_info;
-    if (isset($theme)) {
-      $theme_keys = array();
-      foreach ($base_theme_info as $base) {
-        $theme_keys[] = $base->name;
-      }
-      $theme_keys[] = $theme;
-      foreach ($theme_keys as $theme_key) {
-        $function = $theme_key . '_' . $hook;
-        if (function_exists($function)) {
-          $functions[$cid][] = $function;
-        }
-        if (isset($extra_types)) {
-          foreach ($extra_types as $extra_type) {
-            $function = $theme_key . '_' . $extra_type . '_alter';
-            if (function_exists($function)) {
-              $functions[$cid][] = $function;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  foreach ($functions[$cid] as $function) {
-    $function($data, $context1, $context2);
-  }
-}
-
-/**
  * Sets weight of a particular module.
  *
  * The weight of uninstalled modules cannot be changed.
@@ -1214,11 +657,20 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
 function module_set_weight($module, $weight) {
   // Update the module weight in the config file that contains it.
   $module_config = config('system.module');
+  $extension_handler = drupal_extension_handler();
+  $enabled_modules = $extension_handler->getEnabledModules();
   if ($module_config->get("enabled.$module") !== NULL) {
+    $sorted_modules = module_config_sort($module_config->get('enabled'));
     $module_config
       ->set("enabled.$module", $weight)
-      ->set('enabled', module_config_sort($module_config->get('enabled')))
+      ->set('enabled', $sorted_modules)
       ->save();
+    $sorted_with_filenames = array();
+    foreach (array_keys($sorted_modules) as $m) {
+      $sorted_with_filenames[$m] = $enabled_modules[$m];
+    }
+    // Refresh the module list in the extension handler.
+    $extension_handler->setModuleList($sorted_with_filenames);
     return;
   }
   $disabled_config = config('system.module.disabled');
diff --git a/core/includes/schema.inc b/core/includes/schema.inc
index 7661dcb..1d04270 100644
--- a/core/includes/schema.inc
+++ b/core/includes/schema.inc
@@ -79,13 +79,13 @@ function drupal_get_complete_schema($rebuild = FALSE) {
       // Load the .install files to get hook_schema.
       // On some databases this function may be called before bootstrap has
       // been completed, so we force the functions we need to load just in case.
-      if (function_exists('module_load_all_includes')) {
+      if (function_exists('system_list_reset')) {
         // This function can be called very early in the bootstrap process, so
         // we force the system_list() static cache to be refreshed to ensure
         // that it contains the complete list of modules before we go on to call
         // module_load_all_includes().
         system_list_reset();
-        module_load_all_includes('install');
+        drupal_extension_handler()->loadAllIncludes('install');
       }
 
       require_once DRUPAL_ROOT . '/core/includes/common.inc';
@@ -130,8 +130,7 @@ function drupal_get_schema_versions($module) {
   $updates = &drupal_static(__FUNCTION__, NULL);
   if (!isset($updates[$module])) {
     $updates = array();
-
-    foreach (module_list() as $loaded_module) {
+    foreach (drupal_extension_handler()->getEnabledModules() as $loaded_module => $filename) {
       $updates[$loaded_module] = array();
     }
 
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 8a35b6e..8d1c970 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -511,7 +511,7 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
             // Add all modules so they can intervene with their own variable
             // processors. This allows them to provide variable processors even
             // if they are not the owner of the current hook.
-            $prefixes += module_list();
+            $prefixes += array_keys(drupal_extension_handler()->getEnabledModules());
           }
           elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
             // Theme engines get an extra set that come before the normally
diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc
index 4b3e80c..88c893b 100644
--- a/core/includes/theme.maintenance.inc
+++ b/core/includes/theme.maintenance.inc
@@ -56,8 +56,8 @@ function _drupal_maintenance_theme() {
   // Ensure that system.module is loaded.
   if (!function_exists('_system_rebuild_theme_data')) {
     $module_list['system']['filename'] = 'core/modules/system/system.module';
-    module_list(NULL, $module_list);
-    drupal_load('module', 'system');
+    drupal_extension_handler()->setModuleList($module_list);
+    drupal_extension_handler()->load('system');
   }
 
   $themes = list_themes();
diff --git a/core/includes/update.inc b/core/includes/update.inc
index 299a81c..d6b6a3e 100644
--- a/core/includes/update.inc
+++ b/core/includes/update.inc
@@ -9,6 +9,7 @@
  */
 
 use Drupal\Component\Graph\Graph;
+use Drupal\Core\ExtensionHandler;
 use Drupal\Core\Config\FileStorage;
 use Drupal\Core\Config\ConfigException;
 use Drupal\Component\Uuid\Uuid;
@@ -125,9 +126,8 @@ function update_prepare_d8_bootstrap() {
 
     include_once DRUPAL_ROOT . '/core/includes/module.inc';
     include_once DRUPAL_ROOT . '/core/includes/cache.inc';
-    $module_list['system']['filename'] = 'core/modules/system/system.module';
-    module_list(NULL, $module_list);
-    require_once DRUPAL_ROOT . '/' . $module_list['system']['filename'];
+    drupal_extension_handler(array('system' => 'core/modules/system/system.module'));
+    drupal_extension_handler()->load('system');
     // Ensure the configuration directories exist and are writable, or create
     // them. If the directories have not been specified in settings.php and
     // created manually already, and either directory cannot be created by the
@@ -241,7 +241,7 @@ function update_prepare_d8_bootstrap() {
       // Populate a fixed module list (again, why did it get lost?) to avoid
       // errors due to the drupal_alter() in _system_rebuild_module_data().
       $module_list['system']['filename'] = 'core/modules/system/system.module';
-      module_list(NULL, $module_list);
+      drupal_extension_handler()->setModuleList($module_list);
       $module_data = _system_rebuild_module_data();
 
       // Migrate each extension into configuration, varying by the extension's
@@ -275,8 +275,6 @@ function update_prepare_d8_bootstrap() {
       update_prepare_stored_includes();
       // Update the environment for the language bootstrap if needed.
       update_prepare_d8_language();
-      // Prime the classloader.
-      system_list('module_enabled');
 
       // Change language column to langcode in url_alias.
       if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) {
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index e1147ac..4127764 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -99,6 +99,25 @@ public function build(ContainerBuilder $container) {
     $container->register('request', 'Symfony\Component\HttpFoundation\Request')
       ->setSynthetic(TRUE);
 
+    $container
+      ->register('cache.cache', 'Drupal\Core\Cache\CacheBackendInterface')
+      ->setFactoryClass('Drupal\Core\Cache\CacheFactory')
+      ->setFactoryMethod('get')
+      ->addArgument('cache');
+    $container
+      ->register('cache.bootstrap', 'Drupal\Core\Cache\CacheBackendInterface')
+      ->setFactoryClass('Drupal\Core\Cache\CacheFactory')
+      ->setFactoryMethod('get')
+      ->addArgument('bootstrap');
+
+    // The ExtensionHandler manages enabled modules and provides the ability to
+    // invoke hooks in all enabled modules.
+    $container->register('extension_handler', 'Drupal\Core\ExtensionHandler')
+      ->addArgument('%container.modules%')
+      ->addArgument(new Reference('keyvalue'))
+      ->addArgument(new Reference('cache.cache'))
+      ->addArgument(new Reference('cache.bootstrap'));
+
     $container->register('dispatcher', 'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher')
       ->addArgument(new Reference('service_container'));
     $container->register('resolver', 'Drupal\Core\ControllerResolver')
@@ -133,7 +152,8 @@ public function build(ContainerBuilder $container) {
     $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder')
       ->addArgument(new Reference('router.dumper'))
       ->addArgument(new Reference('lock'))
-      ->addArgument(new Reference('dispatcher'));
+      ->addArgument(new Reference('dispatcher'))
+      ->addArgument(new Reference('extension_handler'));
 
 
     $container->register('matcher', 'Drupal\Core\Routing\ChainMatcher');
@@ -216,6 +236,7 @@ public function build(ContainerBuilder $container) {
       ->setScope('request')
       ->addTag('event_subscriber');
     $container->register('request_close_subscriber', 'Drupal\Core\EventSubscriber\RequestCloseSubscriber')
+      ->addArgument(new Reference('extension_handler'))
       ->addTag('event_subscriber');
     $container->register('config_global_override_subscriber', 'Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber')
       ->addTag('event_subscriber');
diff --git a/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php
index 8d5f87a..04250ab 100644
--- a/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\EventSubscriber;
 
+use Drupal\Core\ExtensionHandlerInterface;
 use Symfony\Component\HttpKernel\KernelEvents;
 use Symfony\Component\HttpKernel\Event\PostResponseEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -16,6 +17,18 @@
  */
 class RequestCloseSubscriber implements EventSubscriberInterface {
 
+   /**
+   * @var ExtensionHandlerInterface
+   */
+  protected $extensionHandler;
+
+  /**
+   * Constructor.
+   */
+  function __construct(ExtensionHandlerInterface $extension_handler) {
+    $this->extensionHandler = $extension_handler;
+  }
+
   /**
    * Performs end of request tasks.
    *
@@ -29,7 +42,14 @@ class RequestCloseSubscriber implements EventSubscriberInterface {
    */
   public function onTerminate(PostResponseEvent $event) {
     module_invoke_all('exit');
-    module_implements_write_cache();
+    $request_method = $event->getRequest()->getMethod();
+    // Check whether we need to write the module implementations cache. We do
+    // not want to cache hooks which are only invoked on HTTP POST requests
+    // since these do not need to be optimized as tightly, and not doing so
+    // keeps the cache entry smaller.
+    if ($request_method == 'GET' || $request_method == 'HEAD') {
+      $this->extensionHandler->writeModuleImplementationsCache();
+    }
     system_run_automated_cron();
   }
 
diff --git a/core/lib/Drupal/Core/ExtensionHandler.php b/core/lib/Drupal/Core/ExtensionHandler.php
new file mode 100644
index 0000000..9170824
--- /dev/null
+++ b/core/lib/Drupal/Core/ExtensionHandler.php
@@ -0,0 +1,523 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\ExtensionHandler.
+ */
+
+namespace Drupal\Core;
+
+use Drupal\Component\Graph\Graph;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\KeyValueStore\KeyValueFactory;
+use Drupal\Core\ExtensionHandlerInterface;
+
+class ExtensionHandler implements ExtensionHandlerInterface {
+
+  /**
+   * Database connection object.
+   */
+  protected $connection;
+
+  /**
+   * Cache backend that stores system info.
+   */
+  protected $cache;
+
+  /**
+   * Cache backend for storing enabled modules.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $bootstrapCache;
+
+  /**
+   * Keeps track internally of loaded files.
+   */
+  protected $loadedFiles;
+
+  /**
+   * Keeps track internally of enabled modules and themes.
+   */
+  protected $bootstrapModules;
+
+  /**
+   * Array of enabled modules.
+   */
+  protected $moduleList;
+
+  /**
+   * Boolean indicating whether modules have been loaded.
+   */
+  protected $loaded = FALSE;
+
+  /**
+   * Keeps track internally of hook implementations.
+   */
+  protected $implementations;
+
+  /**
+   * Keeps track internally of hook info.
+   */
+  protected $hookInfo;
+
+  /**
+   * Keeps track internally of alter functions.
+   */
+  protected $alterFunctions;
+
+  /**
+   * Constructor.
+   */
+  public function __construct(array $module_list, KeyValueFactory $key_value, CacheBackendInterface $cache, CacheBackendInterface $bootstrapCache) {
+    $this->moduleList = $module_list;
+    $this->state = $key_value->get('state');
+    $this->cache = $cache;
+    $this->bootstrapCache = $bootstrapCache;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::load().
+   */
+  public function load($name) {
+    if (isset($this->loadedFiles[$name])) {
+      return TRUE;
+    }
+
+    if (isset($this->moduleList[$name])) {
+      $filename = $this->moduleList[$name];
+      include_once DRUPAL_ROOT . '/' . $filename;
+      $this->loadedFiles[$name] = TRUE;
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::loadAll().
+   */
+  public function loadAll($bootstrap = FALSE, $reset = FALSE, $loaded = FALSE) {
+    if ($reset) {
+      $this->loaded = $loaded;
+    }
+    // Unless $bootstrap is NULL, load the requested set of modules.
+    if (isset($bootstrap) && !$this->loaded) {
+      $to_load = $bootstrap ? $this->getBootstrapModules() : array_keys($this->moduleList);
+      foreach ($to_load as $module) {
+        $this->load($module);
+      }
+      // $this->loaded will be TRUE if $bootstrap is FALSE.
+      $this->loaded = !$bootstrap;
+    }
+    return $this->loaded;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduelList().
+   */
+  public function getEnabledModules() {
+    return $this->moduleList;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::setModuleList().
+   */
+  public function setModuleList(array $module_list = array()) {
+    $this->moduleList = $module_list;
+  }
+
+  public function getBootstrapModules() {
+    if (isset($this->bootstrapModules)) {
+      return $this->bootstrapModules;
+    }
+    if ($cached = $this->bootstrapCache->get('bootstrap_modules')) {
+      $bootstrap_list = $cached->data;
+    }
+    else {
+      $bootstrap_list = $this->state->get('system.module.bootstrap') ?: array();
+      $this->bootstrapCache->set('bootstrap_modules', $bootstrap_list);
+    }
+    // We only return the module names here since getEnabledModules() doesn't need
+    // the filename itself.
+    $this->bootstrapModules = array_keys($bootstrap_list);
+    return $this->bootstrapModules;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::buildModuleDependencies().
+   */
+  public function buildModuleDependencies($files) {
+    foreach ($files as $filename => $file) {
+      $graph[$file->name]['edges'] = array();
+      if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
+        foreach ($file->info['dependencies'] as $dependency) {
+          $dependency_data = $this->parseDependency($dependency);
+          $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data;
+        }
+      }
+    }
+    $graph_object = new Graph($graph);
+    $graph = $graph_object->searchAndSort();
+    foreach ($graph as $module => $data) {
+      $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array();
+      $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array();
+      $files[$module]->sort = $data['weight'];
+    }
+    return $files;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduleExists().
+   */
+  public function moduleExists($module) {
+    return isset($this->moduleList[$module]);
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::loadAllIncludes().
+   */
+  public function loadAllIncludes($type, $name = NULL) {
+    foreach ($this->moduleList as $module => $filename) {
+      module_load_include($type, $module, $name);
+    }
+  }
+
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduleImplements().
+   */
+  public function moduleImplements($hook) {
+    // Fetch implementations from cache.
+    if (empty($this->implementations)) {
+      $implementations = $this->bootstrapCache->get('module_implements');
+      if ($implementations === FALSE) {
+        $this->implementations = array();
+      }
+      else {
+        $this->implementations = $implementations->data;
+      }
+    }
+
+    if (!isset($this->implementations[$hook])) {
+      // The hook is not cached, so ensure that whether or not it has
+      // implementations, that the cache is updated at the end of the request.
+      $this->implementations['#write_cache'] = TRUE;
+      $hookInfo = $this->moduleHookInfo();
+      $this->implementations[$hook] = array();
+      foreach ($this->moduleList as $module => $filename) {
+        $include_file = isset($hookInfo[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hookInfo[$hook]['group']);
+        // Since module_hook() may needlessly try to load the include file again,
+        // function_exists() is used directly here.
+        if (function_exists($module . '_' . $hook)) {
+          $this->implementations[$hook][$module] = $include_file ? $hookInfo[$hook]['group'] : FALSE;
+        }
+      }
+      // Allow modules to change the weight of specific implementations but avoid
+      // an infinite loop.
+      if ($hook != 'module_implements_alter') {
+        $this->alter('module_implements', $this->implementations[$hook], $hook);
+      }
+    }
+    else {
+      foreach ($this->implementations[$hook] as $module => $group) {
+        // If this hook implementation is stored in a lazy-loaded file, so 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 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($this->implementations[$hook][$module]);
+          $this->implementations['#write_cache'] = TRUE;
+        }
+      }
+    }
+
+    return array_keys($this->implementations[$hook]);
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::cachedHookImplementations().
+   */
+  public function cachedHookImplementations() {
+    if (empty($this->implementations)) {
+      return array();
+    }
+    return $this->implementations;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduleImplementsReset().
+   */
+  public function moduleImplementsReset() {
+    // We maintain a persistent cache of hook implementations in addition to the
+    // static cache to avoid looping through every module and every hook on each
+    // request. Benchmarks show that the benefit of this caching outweighs the
+    // additional database hit even when using the default database caching
+    // backend and only a small number of modules are enabled. The cost of the
+    // $this->bootstrapCache->get() is more or less constant and reduced further when
+    // non-database caching backends are used, so there will be more significant
+    // gains when a large number of modules are installed or hooks invoked, since
+    // this can quickly lead to module_hook() being called several thousand times
+    // per request.
+    $this->implementations = NULL;
+    $this->bootstrapCache->set('module_implements', array());
+    $this->hookInfo = NULL;
+    $this->alterFunctions = NULL;
+    $this->bootstrapCache->delete('hook_info');
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduleHookInfo().
+   */
+  public function moduleHookInfo() {
+    // When this function is indirectly invoked from bootstrap_invoke_all() prior
+    // to all modules being loaded, we do not want to cache an incomplete
+    // hook_hookInfo() result, so instead return an empty array. This requires
+    // bootstrap hook implementations to reside in the .module file, which is
+    // optimal for performance anyway.
+    if (!$this->loadAll(NULL)) {
+      return array();
+    }
+
+    if (!isset($this->hookInfo)) {
+      $this->hookInfo = array();
+      $cache = $this->bootstrapCache->get('hook_info');
+      if ($cache === FALSE) {
+        // Rebuild the cache and save it.
+        // We can't use $this->moduleInvokeAll() here or it would cause an infinite
+        // loop.
+        foreach ($this->moduleList as $module => $filename) {
+          $function = $module . '_hook_info';
+          if (function_exists($function)) {
+            $result = $function();
+            if (isset($result) && is_array($result)) {
+              $this->hookInfo = array_merge_recursive($this->hookInfo, $result);
+            }
+          }
+        }
+        // We can't use $this->alter() for the same reason as above.
+        foreach ($this->moduleList as $module => $filename) {
+          $function = $module . '_hook_info_alter';
+          if (function_exists($function)) {
+            $function($this->hookInfo);
+          }
+        }
+        $this->bootstrapCache->set('hook_info', $this->hookInfo);
+      }
+      else {
+        $this->hookInfo = $cache->data;
+      }
+    }
+
+    return $this->hookInfo;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduleImplementsWriteCache().
+   */
+  public function writeModuleImplementationsCache() {
+    if (isset($this->implementations['#write_cache'])) {
+      unset($this->implementations['#write_cache']);
+      $this->bootstrapCache->set('module_implements', $this->implementations);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduleInvokeAll().
+   */
+  public function moduleInvokeAll($hook, $args) {
+    $return = array();
+    foreach ($this->moduleImplements($hook) as $module) {
+      $function = $module . '_' . $hook;
+      if (function_exists($function)) {
+        $result = call_user_func_array($function, $args);
+        if (isset($result) && is_array($result)) {
+          $return = array_merge_recursive($return, $result);
+        }
+        elseif (isset($result)) {
+          $return[] = $result;
+        }
+      }
+    }
+
+    return $return;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::alter().
+   */
+  public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
+    // Most of the time, $type is passed as a string, so for performance,
+    // normalize it to that. When passed as an array, usually the first item in
+    // the array is a generic type, and additional items in the array are more
+    // specific variants of it, as in the case of array('form', 'form_FORM_ID').
+    if (is_array($type)) {
+      $cid = implode(',', $type);
+      $extra_types = $type;
+      $type = array_shift($extra_types);
+      // Allow if statements in this function to use the faster isset() rather
+      // than !empty() both when $type is passed as a string, or as an array with
+      // one item.
+      if (empty($extra_types)) {
+        unset($extra_types);
+      }
+    }
+    else {
+      $cid = $type;
+    }
+
+    // Some alter hooks are invoked many times per page request, so store the
+    // list of functions to call, and on subsequent calls, iterate through them
+    // quickly.
+    if (!isset($this->alterFunctions[$cid])) {
+      $this->alterFunctions[$cid] = array();
+      $hook = $type . '_alter';
+      $modules = $this->moduleImplements($hook);
+      if (!isset($extra_types)) {
+        // For the more common case of a single hook, we do not need to call
+        // function_exists(), since $this->moduleImplements() returns only modules with
+        // implementations.
+        foreach ($modules as $module) {
+          $this->alterFunctions[$cid][] = $module . '_' . $hook;
+        }
+      }
+      else {
+        // For multiple hooks, we need $modules to contain every module that
+        // implements at least one of them.
+        $extra_modules = array();
+        foreach ($extra_types as $extra_type) {
+          $extra_modules = array_merge($extra_modules, $this->moduleImplements($extra_type . '_alter'));
+        }
+        // If any modules implement one of the extra hooks that do not implement
+        // the primary hook, we need to add them to the $modules array in their
+        // appropriate order. $this->moduleImplements() can only return ordered
+        // implementations of a single hook. To get the ordered implementations
+        // of multiple hooks, we mimic the $this->moduleImplements() logic of first
+        // ordering by $this->getEnabledModules(), and then calling
+        // $this->alter('module_implements').
+        if (array_diff($extra_modules, $modules)) {
+          // Merge the arrays and order by getEnabledModules().
+          $modules = array_intersect(array_keys($this->moduleList), array_merge($modules, $extra_modules));
+          // Since $this->moduleImplements() already took care of loading the necessary
+          // include files, we can safely pass FALSE for the array values.
+          $implementations = array_fill_keys($modules, FALSE);
+          // Let modules adjust the order solely based on the primary hook. This
+          // ensures the same module order regardless of whether this if block
+          // runs. Calling $this->alter() recursively in this way does not result
+          // in an infinite loop, because this call is for a single $type, so we
+          // won't end up in this code block again.
+          $this->alter('module_implements', $implementations, $hook);
+          $modules = array_keys($implementations);
+        }
+        foreach ($modules as $module) {
+          // Since $modules is a merged array, for any given module, we do not
+          // know whether it has any particular implementation, so we need a
+          // function_exists().
+          $function = $module . '_' . $hook;
+          if (function_exists($function)) {
+            $this->alterFunctions[$cid][] = $function;
+          }
+          foreach ($extra_types as $extra_type) {
+            $function = $module . '_' . $extra_type . '_alter';
+            if (function_exists($function)) {
+              $this->alterFunctions[$cid][] = $function;
+            }
+          }
+        }
+      }
+      // Allow the theme to alter variables after the theme system has been
+      // initialized.
+      global $theme, $base_theme_info;
+      if (isset($theme)) {
+        $theme_keys = array();
+        foreach ($base_theme_info as $base) {
+          $theme_keys[] = $base->name;
+        }
+        $theme_keys[] = $theme;
+        foreach ($theme_keys as $theme_key) {
+          $function = $theme_key . '_' . $hook;
+          if (function_exists($function)) {
+            $this->alterFunctions[$cid][] = $function;
+          }
+          if (isset($extra_types)) {
+            foreach ($extra_types as $extra_type) {
+              $function = $theme_key . '_' . $extra_type . '_alter';
+              if (function_exists($function)) {
+                $this->alterFunctions[$cid][] = $function;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    foreach ($this->alterFunctions[$cid] as $function) {
+      $function($data, $context1, $context2);
+    }
+  }
+
+  /**
+   * Parses a dependency for comparison by drupal_check_incompatibility().
+   *
+   * @param $dependency
+   *   A dependency string, for example 'foo (>=8.x-4.5-beta5, 3.x)'.
+   *
+   * @return
+   *   An associative array with three keys:
+   *   - 'name' includes the name of the thing to depend on (e.g. 'foo').
+   *   - 'original_version' contains the original version string (which can be
+   *     used in the UI for reporting incompatibilities).
+   *   - 'versions' is a list of associative arrays, each containing the keys
+   *     'op' and 'version'. 'op' can be one of: '=', '==', '!=', '<>', '<',
+   *     '<=', '>', or '>='. 'version' is one piece like '4.5-beta3'.
+   *   Callers should pass this structure to drupal_check_incompatibility().
+   *
+   * @see drupal_check_incompatibility()
+   */
+  protected function parseDependency($dependency) {
+    // We use named subpatterns and support every op that version_compare
+    // supports. Also, op is optional and defaults to equals.
+    $p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
+    // Core version is always optional: 8.x-2.x and 2.x is treated the same.
+    $p_core = '(?:' . preg_quote(DRUPAL_CORE_COMPATIBILITY) . '-)?';
+    $p_major = '(?P<major>\d+)';
+    // By setting the minor version to x, branches can be matched.
+    $p_minor = '(?P<minor>(?:\d+|x)(?:-[A-Za-z]+\d+)?)';
+    $value = array();
+    $parts = explode('(', $dependency, 2);
+    $value['name'] = trim($parts[0]);
+    if (isset($parts[1])) {
+      $value['original_version'] = ' (' . $parts[1];
+      foreach (explode(',', $parts[1]) as $version) {
+        if (preg_match("/^\s*$p_op\s*$p_core$p_major\.$p_minor/", $version, $matches)) {
+          $op = !empty($matches['operation']) ? $matches['operation'] : '=';
+          if ($matches['minor'] == 'x') {
+            // Drupal considers "2.x" to mean any version that begins with
+            // "2" (e.g. 2.0, 2.9 are all "2.x"). PHP's version_compare(),
+            // on the other hand, treats "x" as a string; so to
+            // version_compare(), "2.x" is considered less than 2.0. This
+            // means that >=2.x and <2.x are handled by version_compare()
+            // as we need, but > and <= are not.
+            if ($op == '>' || $op == '<=') {
+              $matches['major']++;
+            }
+            // Equivalence can be checked by adding two restrictions.
+            if ($op == '=' || $op == '==') {
+              $value['versions'][] = array('op' => '<', 'version' => ($matches['major'] + 1) . '.x');
+              $op = '>=';
+            }
+          }
+          $value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']);
+        }
+      }
+    }
+    return $value;
+  }
+}
diff --git a/core/lib/Drupal/Core/ExtensionHandlerInterface.php b/core/lib/Drupal/Core/ExtensionHandlerInterface.php
new file mode 100644
index 0000000..c9f1391
--- /dev/null
+++ b/core/lib/Drupal/Core/ExtensionHandlerInterface.php
@@ -0,0 +1,218 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\ExtensionHandlerInterface.
+ */
+
+namespace Drupal\Core;
+
+interface ExtensionHandlerInterface {
+
+  /**
+   * Includes a file with the provided type and name.
+   *
+   * This prevents including a theme, engine, module, etc., more than once.
+   *
+   * @param $name
+   *   The name of the module to load.
+   *
+   * @return
+   *   TRUE if the item is loaded or has already been loaded.
+   */
+  public function load($name);
+
+  /**
+   * Loads all the modules that have been enabled.
+   *
+   * @param bool $bootstrap
+   *   Whether to load only the reduced set of modules loaded in "bootstrap mode"
+   *   for cached pages. See bootstrap.inc.
+   *
+   * @param bool $reset
+   *   (optional) Internal use only. Whether to reset the internal flag of
+   *   whether modules have been loaded. If TRUE, all modules are (re)loaded in
+   *   the same call. Used by the testing framework to override and persist a
+   *   limited module list for the duration of a unit test (in which no module
+   *   system exists).
+   *
+   * @return bool
+   *   A Boolean indicating whether all modules have been loaded. This means all
+   *   modules; the load status of bootstrap modules cannot be checked.
+   */
+  public function loadAll($bootstrap = FALSE, $reset = FALSE);
+
+  /**
+   * Returns a list of currently active modules.
+   *
+   * Acts as a wrapper around systemList(), returning either a list of all
+   * enabled modules, or just modules needed for bootstrap.
+   *
+   * The returned module list is always based on systemList(). The only exception
+   * to that is when a fixed list of modules has been passed in previously, in
+   * which case systemList() is omitted and the fixed list is always returned in
+   * subsequent calls until manually reverted via moduleListReset().
+   *
+   * @param string $type
+   *   The type of list to return:
+   *   - module_enabled: All enabled modules.
+   *   - bootstrap: All enabled modules required for bootstrap.
+   * @param array $fixed_list
+   *   (optional) An array of module names to override the list of modules. This
+   *   list will persist until the next call with a new $fixed_list passed in.
+   *   Primarily intended for internal use (e.g., in install.php and update.php).
+   *   Use moduleListReset() to undo the $fixed_list override.
+   * @param bool $reset
+   *   (optional) Whether to reset/remove the $fixed_list.
+   *
+   * @return array
+   *   An associative array whose keys and values are the names of the modules in
+   *   the list.
+   *
+   * @see self::moduleListReset()
+   */
+  public function getEnabledModules();
+
+  /**
+   * Explicitly sets the moduleList property to the passed in array of modules.
+   */
+  public function setModuleList(array $module_list = array());
+
+  /**
+   * Determines which modules require and are required by each module.
+   *
+   * @param $files
+   *   The array of filesystem objects used to rebuild the cache.
+   *
+   * @return
+   *   The same array with the new keys for each module:
+   *   - requires: An array with the keys being the modules that this module
+   *     requires.
+   *   - required_by: An array with the keys being the modules that will not work
+   *     without this module.
+   */
+  public function buildModuleDependencies($files);
+
+  /**
+   * Determines whether a given module exists.
+   *
+   * @param $module
+   *   The name of the module (without the .module extension).
+   *
+   * @return
+   *   TRUE if the module is both installed and enabled.
+   */
+  public function moduleExists($module);
+
+  /**
+   * Loads an include file for each module enabled in the {system} table.
+   */
+  public function loadAllIncludes($type, $name = NULL);
+
+  /**
+   * Determines which modules are implementing a hook.
+   *
+   * @param $hook
+   *   The name of the hook (e.g. "help" or "menu").
+   *
+   * @return
+   *   An array with the names of the modules which are implementing this hook.
+   *
+   * @see module_implements_write_cache()
+   */
+  public function moduleImplements($hook);
+
+  /**
+   * Regenerates the stored list of hook implementations.
+   */
+  public function moduleImplementsReset();
+
+  /**
+   * Returns the hook implementation cache.
+   */
+  public function cachedHookImplementations();
+
+  /**
+   * Retrieves a list of hooks that are declared through hook_hook_info().
+   *
+   * @return
+   *   An associative array whose keys are hook names and whose values are an
+   *   associative array containing a group name. The structure of the array
+   *   is the same as the return value of hook_hook_info().
+   *
+   * @see hook_hook_info()
+   */
+  public function moduleHookInfo();
+
+  /**
+   * Writes the hook implementation cache.
+   *
+   * @see $this->moduleImplements()
+   */
+  public function writeModuleImplementationsCache();
+
+  /**
+   * Invokes a hook in all enabled modules that implement it.
+   *
+   * @param $hook
+   *   The name of the hook to invoke.
+   * @param ...
+   *   Arguments to pass to the hook.
+   *
+   * @return
+   *   An array of return values of the hook implementations. If modules return
+   *   arrays from their implementations, those are merged into one array.
+   */
+  public function moduleInvokeAll($hook, $args);
+
+  /**
+   * Passes alterable variables to specific hook_TYPE_alter() implementations.
+   *
+   * This dispatch function hands off the passed-in variables to type-specific
+   * hook_TYPE_alter() implementations in modules. It ensures a consistent
+   * interface for all altering operations.
+   *
+   * A maximum of 2 alterable arguments is supported. In case more arguments need
+   * to be passed and alterable, modules provide additional variables assigned by
+   * reference in the last $context argument:
+   * @code
+   *   $context = array(
+   *     'alterable' => &$alterable,
+   *     'unalterable' => $unalterable,
+   *     'foo' => 'bar',
+   *   );
+   *   $this->alter('mymodule_data', $alterable1, $alterable2, $context);
+   * @endcode
+   *
+   * Note that objects are always passed by reference in PHP5. If it is absolutely
+   * required that no implementation alters a passed object in $context, then an
+   * object needs to be cloned:
+   * @code
+   *   $context = array(
+   *     'unalterable_object' => clone $object,
+   *   );
+   *   $this->alter('mymodule_data', $data, $context);
+   * @endcode
+   *
+   * @param $type
+   *   A string describing the type of the alterable $data. 'form', 'links',
+   *   'node_content', and so on are several examples. Alternatively can be an
+   *   array, in which case hook_TYPE_alter() is invoked for each value in the
+   *   array, ordered first by module, and then for each module, in the order of
+   *   values in $type. For example, when Form API is using $this->alter() to
+   *   execute both hook_form_alter() and hook_form_FORM_ID_alter()
+   *   implementations, it passes array('form', 'form_' . $form_id) for $type.
+   * @param $data
+   *   The variable that will be passed to hook_TYPE_alter() implementations to be
+   *   altered. The type of this variable depends on the value of the $type
+   *   argument. For example, when altering a 'form', $data will be a structured
+   *   array. When altering a 'profile', $data will be an object.
+   * @param $context1
+   *   (optional) An additional variable that is passed by reference.
+   * @param $context2
+   *   (optional) An additional variable that is passed by reference. If more
+   *   context needs to be provided to implementations, then this should be an
+   *   associative array as described above.
+   */
+  public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL);
+}
diff --git a/core/lib/Drupal/Core/ExtensionHandlerMinimal.php b/core/lib/Drupal/Core/ExtensionHandlerMinimal.php
new file mode 100644
index 0000000..4718f46
--- /dev/null
+++ b/core/lib/Drupal/Core/ExtensionHandlerMinimal.php
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\ExtensionHandlerMinimal.
+ */
+
+namespace Drupal\Core;
+
+use Drupal\Core\ExtensionHandler;
+use Symfony\Component\ClassLoader\UniversalClassLoader;
+
+/**
+ * Class representing an ExtensionHandler that tests can use.
+ *
+ * Overrides all methods in the ExtensionHandler class that attempt to talk to
+ * a database.
+ */
+class ExtensionHandlerMinimal extends ExtensionHandler {
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::construct().
+   */
+  public function __construct(array $module_list = array()) {
+    $this->moduleList = $module_list;
+  }
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::moduleImplements().
+   */
+  public function moduleImplements($hook) {
+    // Fetch implementations from cache.
+    if (empty($this->implementations)) {
+      $this->implementations = array();
+    }
+    if (!isset($this->implementations[$hook])) {
+      // The hook is not cached, so ensure that whether or not it has
+      // implementations, that the cache is updated at the end of the request.
+      $this->implementations['#write_cache'] = TRUE;
+      $hook_info = $this->moduleHookInfo();
+      $this->implementations[$hook] = array();
+      foreach ($this->moduleList as $module => $filename) {
+        $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
+        // Since module_hook() may needlessly try to load the include file again,
+        // function_exists() is used directly here.
+        if (function_exists($module . '_' . $hook)) {
+          $this->implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
+        }
+      }
+      // Allow modules to change the weight of specific implementations but avoid
+      // an infinite loop.
+      if ($hook != 'module_implements_alter') {
+        $this->alter('module_implements', $this->implementations[$hook], $hook);
+      }
+    }
+    else {
+      foreach ($this->implementations[$hook] as $module => $group) {
+        // If this hook implementation is stored in a lazy-loaded file, so 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 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($this->implementations[$hook][$module]);
+          $this->implementations['#write_cache'] = TRUE;
+        }
+      }
+    }
+
+    return array_keys($this->implementations[$hook]);
+  }
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::moduleImplementsReset().
+   */
+  public function moduleImplementsReset() {
+    // We maintain a persistent cache of hook implementations in addition to the
+    // static cache to avoid looping through every module and every hook on each
+    // request. Benchmarks show that the benefit of this caching outweighs the
+    // additional database hit even when using the default database caching
+    // backend and only a small number of modules are enabled. The cost of the
+    // cache('bootstrap')->get() is more or less constant and reduced further when
+    // non-database caching backends are used, so there will be more significant
+    // gains when a large number of modules are installed or hooks invoked, since
+    // this can quickly lead to module_hook() being called several thousand times
+    // per request.
+    $this->implementations = NULL;
+    $this->hookInfo = NULL;
+    $this->alterFunctions = NULL;
+  }
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::moduleHookInfo().
+   */
+  public function moduleHookInfo() {
+    // When this function is indirectly invoked from bootstrap_invoke_all() prior
+    // to all modules being loaded, we do not want to cache an incomplete
+    // hook_hook_info() result, so instead return an empty array. This requires
+    // bootstrap hook implementations to reside in the .module file, which is
+    // optimal for performance anyway.
+    if (!$this->loadAll(NULL)) {
+      return array();
+    }
+
+    if (!isset($this->hookInfo)) {
+      $this->hookInfo = array();
+      // We can't use $this->moduleInvokeAll() here or it would cause an infinite
+      // loop.
+      foreach ($this->moduleList as $module => $filename) {
+        $function = $module . '_hook_info';
+        if (function_exists($function)) {
+          $result = $function();
+          if (isset($result) && is_array($result)) {
+            $this->hookInfo = array_merge_recursive($this->hookInfo, $result);
+          }
+        }
+      }
+      // We can't use $this->alter() for the same reason as above.
+      foreach ($this->moduleList as $module => $filename) {
+        $function = $module . '_hook_info_alter';
+        if (function_exists($function)) {
+          $function($this->hookInfo);
+        }
+      }
+    }
+    return $this->hookInfo;
+  }
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::moduleImplementsWriteCache().
+   */
+  public function writeModuleImplementationsCache() {
+  }
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php
index 6f7a86e..0e8d5b0 100644
--- a/core/lib/Drupal/Core/Routing/RouteBuilder.php
+++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php
@@ -13,6 +13,7 @@
 use Symfony\Component\Routing\RouteCollection;
 use Symfony\Component\Routing\Route;
 
+use Drupal\Core\ExtensionHandlerInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 
 /**
@@ -45,6 +46,13 @@ class RouteBuilder {
   protected $dispatcher;
 
   /**
+   * The extension handler for retieving the list of enabled modules.
+   *
+   * @var \Drupal\Core\ExtensionHandlerInterface
+   */
+  protected $extensionHandler;
+
+  /**
    * Construcs the RouteBuilder using the passed MatcherDumperInterface.
    *
    * @param \Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface $dumper
@@ -54,10 +62,11 @@ class RouteBuilder {
    * @param \Symfony\Component\EventDispatcherEventDispatcherInterface
    *   The event dispatcher to notify of routes.
    */
-  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher) {
+  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher, ExtensionHandlerInterface $extension_handler) {
     $this->dumper = $dumper;
     $this->lock = $lock;
     $this->dispatcher = $dispatcher;
+    $this->extensionHandler = $extension_handler;
   }
 
   /**
@@ -76,11 +85,9 @@ public function rebuild() {
 
     // We need to manually call each module so that we can know which module
     // a given item came from.
-    // @todo Use an injected Extension service rather than module_list():
-    //   http://drupal.org/node/1331486.
-    foreach (module_list() as $module) {
+    foreach ($this->extensionHandler->getEnabledModules() as $module => $filename) {
       $collection = new RouteCollection();
-      $routing_file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . '/' . $module . '.routing.yml';
+      $routing_file = DRUPAL_ROOT . '/' . dirname($filename) . '/' . $module . '.routing.yml';
       if (file_exists($routing_file)) {
         $routes = $parser->parse(file_get_contents($routing_file));
         if (!empty($routes)) {
diff --git a/core/modules/breakpoint/breakpoint.install b/core/modules/breakpoint/breakpoint.install
index 0e6bc30..60ba9cf 100644
--- a/core/modules/breakpoint/breakpoint.install
+++ b/core/modules/breakpoint/breakpoint.install
@@ -18,6 +18,5 @@ function breakpoint_enable() {
   _breakpoint_theme_enabled(array_keys($themes));
 
   // Import breakpoints from modules.
-  $modules = module_list();
-  _breakpoint_modules_enabled(array_keys($modules));
+  _breakpoint_modules_enabled(array_keys(drupal_extension_handler()->getEnabledModules()));
 }
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 686e3f5..b3ba60d 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -504,7 +504,7 @@ function field_modules_disabled($modules) {
 function field_sync_field_status() {
   // Refresh the 'active' and 'storage_active' columns according to the current
   // set of enabled modules.
-  $modules = module_list();
+  $modules = array_keys(drupal_extension_handler()->getEnabledModules());
   foreach ($modules as $module_name) {
     field_associate_fields($module_name);
   }
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php
index a6c4ea9..9f803de 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php
@@ -10,6 +10,7 @@
 use DirectoryIterator;
 use Drupal\Component\Plugin\Derivative\DerivativeInterface;
 use Drupal\Core\Config\FileStorage;
+use Drupal\Core\ExtensionHandlerInterface;
 
 /**
  * Layout plugin derivative definition.
@@ -58,11 +59,13 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
     $available_layout_providers = array();
 
     // Add all modules as possible layout providers.
-    foreach (module_list() as $module) {
+    // @todo Change this class so that it gets the extension handler injected
+    //   into it.
+    foreach (drupal_extension_handler()->getEnabledModules() as $module => $filename) {
       $available_layout_providers[$module] = array(
         'type' => 'module',
         'provider' => $module,
-        'dir' => drupal_get_path('module', $module),
+        'dir' => dirname($filename),
       );
     }
 
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
index 793c8c0..430dbe0 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
@@ -53,8 +53,8 @@
    * Fixed module list being used by this test.
    *
    * @var array
-   *   An associative array containing the required data for the $fixed_list
-   *   argument of module_list().
+   *   An associative array containing the required data to set the moduleList
+   *   property of the ExtensionHandler.
    *
    * @see UnitTestBase::setUp()
    * @see UnitTestBase::enableModules()
@@ -145,6 +145,9 @@ public function containerBuild($container) {
         ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory')
         ->addArgument(new Reference('service_container'));
     }
+    // Register an extension handler for managing enabled modules.
+      $container
+        ->register('extension_handler', 'Drupal\Core\ExtensionHandlerMinimal');
   }
 
   /**
@@ -202,12 +205,12 @@ protected function installSchema($module, $table) {
    *   implementation that is able to manage a fixed module list.
    */
   protected function enableModules(array $modules, $install = TRUE) {
-    // Set the modules in the fixed module_list().
+    // Explicitly set the list of modules in the extension handler.
     $new_enabled = array();
+    $extension_handler = $this->container->get('extension_handler');
     foreach ($modules as $module) {
-      $this->moduleList[$module]['filename'] = drupal_get_filename('module', $module);
-      $new_enabled[$module] = dirname($this->moduleList[$module]['filename']);
-      module_list(NULL, $this->moduleList);
+      $this->moduleList[$module] = drupal_get_filename('module', $module);
+      $this->container->get('extension_handler')->setModuleList($this->moduleList);
 
       // Call module_enable() to enable (install) the new module.
       if ($install) {
@@ -217,7 +220,7 @@ protected function enableModules(array $modules, $install = TRUE) {
     // Otherwise, only ensure that the new modules are loaded.
     if (!$install) {
       module_load_all(FALSE, TRUE);
-      module_implements_reset();
+      $extension_handler->moduleImplementsReset();
     }
   }
 
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index 93cdbf2..95d4db6 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -1015,10 +1015,6 @@ protected function tearDown() {
       drupal_valid_test_ua($this->originalPrefix);
     }
 
-    // Reset module list and module load status.
-    module_list_reset();
-    module_load_all(FALSE, TRUE);
-
     // Restore original shutdown callbacks.
     $callbacks = &drupal_register_shutdown_function();
     $callbacks = $this->originalShutdownCallbacks;
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
index 5813ffd..5ca7c71 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
@@ -39,7 +39,7 @@ function testSetUp() {
     // Verify that specified $modules have been loaded.
     $this->assertTrue(function_exists('entity_test_permission'), "$module.module was loaded.");
     // Verify that there is a fixed module list.
-    $this->assertIdentical(module_list(), array($module => $module));
+    $this->assertIdentical(array_keys(drupal_extension_handler()->getEnabledModules()), array($module));
     $this->assertIdentical(module_implements('permission'), array($module));
 
     // Verify that no modules have been installed.
@@ -54,9 +54,9 @@ function testEnableModulesLoad() {
 
     // Verify that the module does not exist yet.
     $this->assertFalse(module_exists($module), "$module module not found.");
-    $list = module_list();
-    $this->assertFalse(in_array($module, $list), "$module module in module_list() not found.");
-    $list = module_list('permission');
+    $list = array_keys(drupal_extension_handler()->getEnabledModules());
+    $this->assertFalse(in_array($module, $list), "$module module not found in the extension handler's module list.");
+    $list = module_implements('permission');
     $this->assertFalse(in_array($module, $list), "{$module}_permission() in module_implements() not found.");
 
     // Enable the module.
@@ -64,9 +64,9 @@ function testEnableModulesLoad() {
 
     // Verify that the module exists.
     $this->assertTrue(module_exists($module), "$module module found.");
-    $list = module_list();
-    $this->assertTrue(in_array($module, $list), "$module module in module_list() found.");
-    $list = module_list('permission');
+    $list = array_keys(drupal_extension_handler()->getEnabledModules());
+    $this->assertTrue(in_array($module, $list), "$module module found in the extension handler's module list.");
+    $list = module_implements('permission');
     $this->assertTrue(in_array($module, $list), "{$module}_permission() in module_implements() found.");
   }
 
@@ -83,9 +83,9 @@ function testEnableModulesInstall() {
 
     // Verify that the module does not exist yet.
     $this->assertFalse(module_exists($module), "$module module not found.");
-    $list = module_list();
-    $this->assertFalse(in_array($module, $list), "$module module in module_list() not found.");
-    $list = module_list('permission');
+    $list = array_keys(drupal_extension_handler()->getEnabledModules());
+    $this->assertFalse(in_array($module, $list), "$module module not found in the extension handler's module list.");
+    $list = module_implements('permission');
     $this->assertFalse(in_array($module, $list), "{$module}_permission() in module_implements() not found.");
 
     $this->assertFalse(db_table_exists($table), "'$table' database table not found.");
@@ -97,9 +97,9 @@ function testEnableModulesInstall() {
 
     // Verify that the enabled module exists.
     $this->assertTrue(module_exists($module), "$module module found.");
-    $list = module_list();
-    $this->assertTrue(in_array($module, $list), "$module module in module_list() found.");
-    $list = module_list('permission');
+    $list = array_keys(drupal_extension_handler()->getEnabledModules());
+    $this->assertTrue(in_array($module, $list), "$module module found in the extension handler's module list.");
+    $list = module_implements('permission');
     $this->assertTrue(in_array($module, $list), "{$module}_permission() in module_implements() found.");
 
     $this->assertTrue(db_table_exists($table), "'$table' database table found.");
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
index 648aa42..d7bd225 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
@@ -56,9 +56,6 @@ protected function setUp() {
     $conf = array();
     drupal_static_reset();
 
-    // Enforce an empty module list.
-    module_list(NULL, array());
-
     $conf['file_public_path'] = $this->public_files_directory;
 
     // Change the database prefix.
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index a936558..14a1169 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -828,8 +828,6 @@ protected function tearDown() {
     // Reload module list and implementations to ensure that test module hooks
     // aren't called after tests.
     system_list_reset();
-    module_list_reset();
-    module_implements_reset();
 
     // Reset the Field API.
     field_cache_clear();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php
index e73ace9..5e28d84 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php
@@ -23,6 +23,7 @@ public static function getInfo() {
    * Tests that all core modules can be enabled, disabled and uninstalled.
    */
   function testEnableDisable() {
+
     // Try to enable, disable and uninstall all core modules, unless they are
     // hidden or required.
     $modules = system_rebuild_module_data();
@@ -39,7 +40,7 @@ function testEnableDisable() {
 
     // Remove already enabled modules (via installation profile).
     // @todo Remove this after removing all dependencies from Testing profile.
-    foreach (module_list() as $dependency) {
+    foreach ($this->container->get('extension_handler')->getEnabledModules() as $dependency => $filename) {
       // Exclude required modules. Only installation profile "suggestions" can
       // be disabled and uninstalled.
       if (isset($modules[$dependency])) {
@@ -90,10 +91,12 @@ function testEnableDisable() {
         $edit = array();
         $edit['modules[Core][' . $name . '][enable]'] = $name;
         $this->drupalPost('admin/modules', $edit, t('Save configuration'));
+        $this->rebuildContainer();
         // Handle the case where modules were installed along with this one and
         // where we therefore hit a confirmation screen.
         if (count($modules_to_enable) > 1) {
           $this->drupalPost(NULL, array(), t('Continue'));
+          $this->rebuildContainer();
         }
         $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
 
@@ -162,6 +165,7 @@ function testEnableDisable() {
       $edit['modules[Core][' . $name . '][enable]'] = $name;
     }
     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
+    $this->rebuildContainer();
     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
   }
 
@@ -176,6 +180,7 @@ function assertSuccessfulDisableAndUninstall($module) {
     $edit = array();
     $edit['modules[Core][' . $module . '][enable]'] = FALSE;
     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
+    $this->rebuildContainer();
     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
     $this->assertModules(array($module), FALSE);
 
@@ -194,6 +199,7 @@ function assertSuccessfulDisableAndUninstall($module) {
     $edit['uninstall[' . $module . ']'] = $module;
     $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
     $this->drupalPost(NULL, NULL, t('Uninstall'));
+    $this->rebuildContainer();
     $this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
     $this->assertModules(array($module), FALSE);
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php
index 42924bf..a16c49f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php
@@ -25,7 +25,7 @@ public static function getInfo() {
   }
 
   /**
-   * The basic functionality of module_list().
+   * The basic functionality of retrieving enabled modules.
    */
   function testModuleList() {
     // Build a list of modules, sorted alphabetically.
@@ -36,8 +36,8 @@ function testModuleList() {
     $module_list[] = 'standard';
 
     sort($module_list);
-    // Compare this list to the one returned by module_list(). We expect them
-    // to match, since all default profile modules have a weight equal to 0
+    // Compare this list to the one returned by the extension handler. We expect
+    // them to match, since all default profile modules have a weight equal to 0
     // (except for block.module, which has a lower weight but comes first in
     // the alphabet anyway).
     $this->assertModuleList($module_list, t('Standard profile'));
@@ -50,8 +50,7 @@ function testModuleList() {
 
     // Try to mess with the module weights.
     module_set_weight('contact', 20);
-    // Reset the module list.
-    system_list_reset();
+
     // Move contact to the end of the array.
     unset($module_list[array_search('contact', $module_list)]);
     $module_list[] = 'contact';
@@ -59,27 +58,25 @@ function testModuleList() {
 
     // Test the fixed list feature.
     $fixed_list = array(
-      'system' => array('filename' => drupal_get_path('module', 'system')),
-      'menu' => array('filename' => drupal_get_path('module', 'menu')),
+      'system' => 'core/modules/system/system.module',
+      'menu' => 'core/modules/menu/menu.module',
     );
-    module_list(NULL, $fixed_list);
+    $this->container->get('extension_handler')->setModuleList($fixed_list);
     $new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list));
     $this->assertModuleList($new_module_list, t('When using a fixed list'));
 
-    // Reset the module list.
-    module_list_reset();
-    $this->assertModuleList($module_list, t('After reset'));
   }
 
   /**
-   * Assert that module_list() return the expected values.
+   * Assert that the extension handler returns the expected values.
    *
    * @param $expected_values
    *   The expected values, sorted by weight and module name.
    */
   protected function assertModuleList(Array $expected_values, $condition) {
-    $expected_values = array_combine($expected_values, $expected_values);
-    $this->assertEqual($expected_values, module_list(), format_string('@condition: module_list() returns correct results', array('@condition' => $condition)));
+    $expected_values = array_values(array_unique($expected_values));
+    $enabled_modules = array_keys($this->container->get('extension_handler')->getEnabledModules());
+    $this->assertEqual($expected_values, $enabled_modules, format_string('@condition: extension handler returns correct results', array('@condition' => $condition)));
   }
 
   /**
@@ -106,9 +103,9 @@ function testModuleImplements() {
 
     module_load_include('inc', 'module_test', 'module_test.file');
     $modules = module_implements('test_hook');
-    $static = drupal_static('module_implements');
     $this->assertTrue(in_array('module_test', $modules), 'Hook found.');
-    $this->assertEqual($static['test_hook']['module_test'], 'file', 'Include file detected.');
+    $module_implementations = drupal_extension_handler()->cachedHookImplementations();
+    $this->assertEqual($module_implementations['test_hook']['module_test'], 'file', 'Include file detected.');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php
index b7fd25c..1fc541f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php
@@ -139,7 +139,6 @@ function assertNoModuleConfig($module) {
    *   Expected module state.
    */
   function assertModules(array $modules, $enabled) {
-    system_list_reset();
     foreach ($modules as $module) {
       if ($enabled) {
         $message = 'Module "@module" is enabled.';
@@ -147,7 +146,7 @@ function assertModules(array $modules, $enabled) {
       else {
         $message = 'Module "@module" is not enabled.';
       }
-      $this->assertEqual(module_exists($module), $enabled, format_string($message, array('@module' => $module)));
+      $this->assertEqual($this->container->get('extension_handler')->moduleExists($module), $enabled, format_string($message, array('@module' => $module)));
     }
   }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/ModulesDisabledUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/ModulesDisabledUpgradePathTest.php
index c4c6f75..24531de 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/ModulesDisabledUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/ModulesDisabledUpgradePathTest.php
@@ -37,7 +37,7 @@ public function testDisabledUpgrade() {
     $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.');
 
     // Get enabled modules.
-    $enabled = module_list();
+    $enabled = drupal_extension_handler()->getEnabledModules();
     // Get all available modules.
     $available = system_rebuild_module_data();
     // Filter out hidden test modules.
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index a5194f7..6697c03 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1204,7 +1204,7 @@ function system_modules_submit($form, &$form_state) {
 
   // Gets list of modules prior to install process, unsets $form_state['storage']
   // so we don't get redirected back to the confirmation form.
-  $pre_install_list = module_list();
+  $pre_install_list = drupal_extension_handler()->getEnabledModules();
   unset($form_state['storage']);
 
   // Reverse the 'enable' list, to order dependencies before dependents.
@@ -1216,7 +1216,7 @@ function system_modules_submit($form, &$form_state) {
 
   // Gets module list after install process, flushes caches and displays a
   // message if there are changes.
-  $post_install_list = module_list();
+  $post_install_list = drupal_extension_handler()->getEnabledModules();
   if ($pre_install_list != $post_install_list) {
     drupal_flush_all_caches();
     drupal_set_message(t('The configuration options have been saved.'));
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 06935d1..44bcf92 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -400,7 +400,7 @@ function system_requirements($phase) {
     );
 
     // Check installed modules.
-    foreach (module_list() as $module) {
+    foreach (drupal_extension_handler()->getEnabledModules() as $module => $filename) {
       $updates = drupal_get_schema_versions($module);
       if ($updates !== FALSE) {
         $default = drupal_get_installed_schema_version($module);
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 139fb0f..c47618d 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2686,7 +2686,7 @@ function system_get_info($type, $name = NULL) {
   $info = array();
   if ($type == 'module') {
     $data = system_rebuild_module_data();
-    foreach (module_list() as $module) {
+    foreach (drupal_extension_handler()->getEnabledModules() as $module => $filename) {
       $info[$module] = $data[$module]->info;
     }
   }
@@ -2838,7 +2838,7 @@ function system_rebuild_module_data() {
       $record->schema_version = SCHEMA_UNINSTALLED;
       $files[$module] = $record->filename;
     }
-    $modules = _module_build_dependencies($modules);
+    $modules = drupal_extension_handler()->buildModuleDependencies($modules);
     $modules_cache = $modules;
 
     // Store filenames to allow system_list() and drupal_get_filename() to
diff --git a/core/scripts/dump-database-d6.sh b/core/scripts/dump-database-d6.sh
index d39bb43..0b0a12b 100644
--- a/core/scripts/dump-database-d6.sh
+++ b/core/scripts/dump-database-d6.sh
@@ -44,7 +44,7 @@
 
 ENDOFHEADER;
 
-foreach (module_list() as $module) {
+foreach (drupal_extension_handler()->getEnabledModules() as $module => $filename) {
   $output .= " *  - $module\n";
 }
 $output .= " */\n\n";
diff --git a/core/scripts/dump-database-d7.sh b/core/scripts/dump-database-d7.sh
index 3440fa2..77edc0d 100644
--- a/core/scripts/dump-database-d7.sh
+++ b/core/scripts/dump-database-d7.sh
@@ -45,7 +45,7 @@
 
 ENDOFHEADER;
 
-foreach (module_list() as $module) {
+foreach (drupal_extension_handler()->getEnabledModules() as $module => $filename) {
   $output .= " *  - $module\n";
 }
 $output .= " */\n\n";
diff --git a/core/update.php b/core/update.php
index ff65c6e..aa25a8e 100644
--- a/core/update.php
+++ b/core/update.php
@@ -436,12 +436,13 @@ function update_check_requirements($skip_warnings = FALSE) {
   // Load module basics.
   include_once DRUPAL_ROOT . '/core/includes/module.inc';
   $module_list['system']['filename'] = 'core/modules/system/system.module';
-  module_list(NULL, $module_list);
-  drupal_load('module', 'system');
+  $extension_handler = drupal_extension_handler();
+  $extension_handler->setModuleList($module_list);
+  $extension_handler->load('system');
 
-  // Reset the module_implements() cache so that any new hook implementations
+  // Reset the module implementations cache so that any new hook implementations
   // in updated code are picked up.
-  module_implements_reset();
+  $extension_handler->moduleImplementsReset();
 
   // Set up $language, since the installer components require it.
   drupal_language_initialize();
