diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index fdcb01b..53c7ed4 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -1,7 +1,11 @@
 <?php
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\CacheFactory;
 use Drupal\Core\Database\Database;
+use Drupal\Core\ExtensionHandler;
+use Drupal\Core\InstallerExtensionHandler;
+use Drupal\Core\KeyValueStore\KeyValueFactory;
 use Symfony\Component\ClassLoader\UniversalClassLoader;
 use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
 use Symfony\Component\DependencyInjection\Container;
@@ -841,117 +845,6 @@ function drupal_settings_initialize() {
 }
 
 /**
- * Returns and optionally sets the filename for a system resource.
- *
- * The filename, whether provided, cached, or retrieved from the database, is
- * only returned if the file exists.
- *
- * This function plays a key role in allowing Drupal's resources (modules
- * and themes) to be located in different places depending on a site's
- * configuration. For example, a module 'foo' may legally be be located
- * in any of these three places:
- *
- * core/modules/foo/foo.module
- * modules/foo/foo.module
- * sites/example.com/modules/foo/foo.module
- *
- * Calling drupal_get_filename('module', 'foo') will give you one of
- * the above, depending on where the module is located.
- *
- * @param $type
- *   The type of the item (i.e. theme, theme_engine, module, profile).
- * @param $name
- *   The name of the item for which the filename is requested.
- * @param $filename
- *   The filename of the item if it is to be set explicitly rather
- *   than by consulting the database.
- *
- * @return
- *   The filename of the requested item.
- */
-function drupal_get_filename($type, $name, $filename = NULL) {
-  // The location of files will not change during the request, so do not use
-  // drupal_static().
-  static $files = array(), $dirs = array();
-
-  // Profiles are converted into modules in system_rebuild_module_data().
-  // @todo Remove false-exposure of profiles as modules.
-  $original_type = $type;
-  if ($type == 'profile') {
-    $type = 'module';
-  }
-  if (!isset($files[$type])) {
-    $files[$type] = array();
-  }
-
-  if (!empty($filename) && file_exists($filename)) {
-    $files[$type][$name] = $filename;
-  }
-  elseif (isset($files[$type][$name])) {
-    // nothing
-  }
-  else {
-    // 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.
-    if (drupal_container()->hasDefinition('keyvalue') && function_exists('db_query')) {
-      try {
-        $file_list = state()->get('system.' . $type . '.files');
-        if ($file_list && isset($file_list[$name]) && file_exists(DRUPAL_ROOT . '/' . $file_list[$name])) {
-          $files[$type][$name] = $file_list[$name];
-        }
-      }
-      catch (Exception $e) {
-        // The keyvalue service raised an exception because the backend might
-        // be down. We have a fallback for this case so we hide the error
-        // completely.
-      }
-    }
-    // Fallback to searching the filesystem if the database could not find the
-    // file or the file returned by the database is not found.
-    if (!isset($files[$type][$name])) {
-      // We have consistent directory naming: modules, themes...
-      $dir = $type . 's';
-      if ($type == 'theme_engine') {
-        $dir = 'themes/engines';
-        $extension = 'engine';
-      }
-      elseif ($type == 'theme') {
-        $extension = 'info';
-      }
-      // Profiles are converted into modules in system_rebuild_module_data().
-      // @todo Remove false-exposure of profiles as modules.
-      elseif ($original_type == 'profile') {
-        $dir = 'profiles';
-        $extension = 'profile';
-      }
-      else {
-        $extension = $type;
-      }
-
-      if (!isset($dirs[$dir][$extension])) {
-        $dirs[$dir][$extension] = TRUE;
-        if (!function_exists('drupal_system_listing')) {
-          require_once DRUPAL_ROOT . '/core/includes/common.inc';
-        }
-        // Scan the appropriate directories for all files with the requested
-        // extension, not just the file we are currently looking for. This
-        // prevents unnecessary scans from being repeated when this function is
-        // called more than once in the same page request.
-        $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
-        foreach ($matches as $matched_name => $file) {
-          $files[$type][$matched_name] = $file->uri;
-        }
-      }
-    }
-  }
-
-  if (isset($files[$type][$name])) {
-    return $files[$type][$name];
-  }
-}
-
-/**
  * Loads the persistent variable table.
  *
  * The variable table is composed of values that have been saved in the table
@@ -1129,40 +1022,6 @@ function bootstrap_invoke_all($hook) {
 }
 
 /**
- * Includes a file with the provided type and name.
- *
- * This prevents including a theme, engine, module, etc., more than once.
- *
- * @param $type
- *   The type of item to load (i.e. theme, theme_engine, module).
- * @param $name
- *   The name of the item to load.
- *
- * @return
- *   TRUE if the item is loaded or has already been loaded.
- */
-function drupal_load($type, $name) {
-  // Once a file is included this can't be reversed during a request so do not
-  // use drupal_static() here.
-  static $files = array();
-
-  if (isset($files[$type][$name])) {
-    return TRUE;
-  }
-
-  $filename = drupal_get_filename($type, $name);
-
-  if ($filename) {
-    include_once DRUPAL_ROOT . '/' . $filename;
-    $files[$type][$name] = TRUE;
-
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
-/**
  * Sets an HTTP response header for the current page.
  *
  * Note: When sending a Content-Type header, always include a 'charset' type,
@@ -2430,10 +2289,11 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) {
     $container = $new_container;
   }
   if (!isset($container)) {
-    // Return a ContainerBuilder instance with the bare essentials needed for any
-    // full bootstrap regardless of whether there will be a DrupalKernel involved.
-    // This will get merged with the full Kernel-built Container on normal page
-    // requests.
+   // This will never happen on a normal page request. It is currently used only
+   // by the installer and in situations where a full bootstrap is done outside
+   // the context of a DrupalKernel, e.g. run-tests.sh, or when an exception is
+   // thrown while the Kernel is booting.
+   // @todo Use a precompiled container for this.
     $container = new ContainerBuilder();
 
     // Register active configuration storage.
@@ -2473,6 +2333,117 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) {
 }
 
 /**
+ * Retrieves the ExtensionHandler that manages the list of enabled modules.
+ *
+ * This is a temporary function that will instantiate a new ExtensionHandler if
+ * there isn't one present in the container. This will only happen if the
+ * boostrap container is being used, e.g. during installation.
+ * @todo Remove this and convert all calls to it to
+ *  drupal_container()->get('extension_handler') once the installer is using a
+ *  Kernel.
+ *
+ * @return Drupal\Core\ExtensionHandlerInterface
+ */
+function drupal_extension_handler($installer = FALSE) {
+  $extension_handler = &drupal_static(__FUNCTION__, NULL);
+  if (isset($extension_handler)) {
+    return $extension_handler;
+  }
+  if ($installer) {
+    $extension_handler = new InstallerExtensionHandler(drupal_classloader());
+  }
+  else {
+    if (drupal_container()->has('extension_handler')) {
+      $extension_handler = drupal_container()->get('extension_handler');
+    }
+    else {
+      $key_value = new KeyValueFactory();
+      $cache = CacheFactory::get('cache');
+      $bootstrap_cache = CacheFactory::get('bootstrap');
+      $extension_handler = new ExtensionHandler($key_value, $cache, $bootstrap_cache, drupal_classloader());
+      drupal_container()->set('keyvalue', $key_value);
+    }
+  }
+
+  return $extension_handler;
+}
+
+/**
+ * Returns and optionally sets the filename for a system resource.
+ *
+ * @see ExtensionHandler::getFilename().
+ */
+function drupal_get_filename($type, $name, $filename = NULL) {
+  return drupal_extension_handler()->getFilename($type, $name, $filename);
+}
+
+/**
+ * Includes a file with the provided type and name.
+ *
+ * @see ExtensionHandler::load().
+ */
+function drupal_load($type, $name) {
+  return drupal_extension_handler()->load($type, $name);
+}
+
+/**
+ * Returns a list of currently active modules.
+ *
+ * @see ExtensionHandler::moduleList().
+ */
+function module_list($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) {
+  return drupal_extension_handler()->getEnabledModules($type, $fixed_list, $reset);
+}
+
+/**
+ * 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
@@ -2709,14 +2680,8 @@ function language($type, $reset = FALSE) {
   // When the language_manager service exists (is both defined and the 'request'
   // scope is active in the container), use it to get the language. Otherwise
   // return the default language.
-  try {
-    $language_manager = drupal_container()->get('language_manager', Container::NULL_ON_INVALID_REFERENCE);
-  }
-  catch (DependencyInjectionRuntimeException $e) {
-  }
-
-  if (isset($language_manager)) {
-    return $language_manager->getLanguage($type);
+  if (drupal_container()->isScopeActive('request')) {
+    return drupal_container()->get('language_manager')->getLanguage($type);
   }
   else {
     if (!isset($languages[$type])) {
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 0f252ca..3c9595f 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -6899,68 +6899,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).
  *
@@ -6968,7 +6910,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 da39097..2e61751 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -267,7 +267,8 @@ function install_begin_request(&$install_state) {
   conf_path(FALSE);
 
   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
-
+  // Ensure that an InstallerExtensionHandler is used during installation.
+  drupal_extension_handler(TRUE);
   // A request object from the HTTPFoundation to tell us about the request.
   $request = Request::createFromGlobals();
 
@@ -1461,9 +1462,9 @@ 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');
   // @todo The constructor parameters for the Kernel class are for environment,
   // e.g. 'prod', 'dev', and a boolean indicating whether it is in debug mode.
   // Drupal does not currently make use of either of these, though that may
diff --git a/core/includes/install.inc b/core/includes/install.inc
index db92987..648bfa6 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -424,9 +424,10 @@ function drupal_install_system() {
     ->save();
 
   // Clear out module list and hook implementation statics.
-  system_list_reset();
-  module_list_reset();
-  module_implements_reset();
+  $extension_handler = drupal_extension_handler();
+  $extension_handler->systemListReset();
+  $extension_handler->moduleListReset();
+  $extension_handler->moduleImplementsReset();
 
   config_install_default_config('module', 'system');
 
diff --git a/core/includes/module.inc b/core/includes/module.inc
index c1d99f2..587eec4 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -8,327 +8,6 @@
 use Drupal\Component\Graph\Graph;
 
 /**
- * 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) {
-      drupal_get_filename('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
- *   The type of list to return:
- *   - module_enabled: All enabled modules.
- *   - bootstrap: All enabled modules required for bootstrap.
- *   - theme: All themes.
- *
- * @return
- *   An associative array of modules or themes, keyed by name. For $type
- *   'bootstrap' and 'module_enabled', the array values equal the keys.
- *   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()
- *   data. Consider to add a config($name, $cache = TRUE) argument to allow
- *   callers like system_list() to force-disable a possible configuration
- *   storage controller cache or some other way to circumvent it/take it over.
- */
-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) {
-      drupal_classloader_register($name, dirname($filename));
-      drupal_get_filename('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);
-  }
-  // 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 = 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.
-        $lists['filepaths'][] = array(
-          'type' => 'module',
-          'name' => $name,
-          'filepath' => $module_files[$name],
-        );
-      }
-
-      // Build a list of themes.
-      $enabled_themes = 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'];
-        }
-        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) {
-      drupal_get_filename($item['type'], $item['name'], $item['filepath']);
-      drupal_classloader_register($item['name'], dirname($item['filepath']));
-    }
-  }
-
-  return $lists[$type];
-}
-
-/**
- * Resets all system_list() caches.
- */
-function system_list_reset() {
-  drupal_static_reset('system_list');
-  drupal_static_reset('system_rebuild_module_data');
-  drupal_static_reset('list_themes');
-  cache('bootstrap')->deleteMultiple(array('bootstrap_modules', 'system_list'));
-  cache()->delete('system_info');
-  // Remove last known theme data state.
-  // This causes system_list() to call system_rebuild_theme_data() on its next
-  // invocation. When enabling a module that implements hook_system_info_alter()
-  // to inject a new (testing) theme or manipulate an existing theme, then that
-  // will cause system_list_reset() to be called, but theme data is not
-  // necessarily rebuilt afterwards.
-  // @todo Obsolete with proper installation status for themes.
-  state()->delete('system.theme.data');
-}
-
-/**
- * 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
@@ -387,16 +66,6 @@ function module_load_include($type, $module, $name = NULL) {
 }
 
 /**
- * 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.
  *
  * Definitions:
@@ -498,8 +167,8 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
       module_load_install($module);
 
       // Refresh the module list to include it.
-      system_list_reset();
-      module_implements_reset();
+      drupal_extension_handler()->systemListReset();
+      drupal_extension_handler()->moduleImplementsReset();
       _system_update_bootstrap_status();
       // Refresh the schema to include it.
       drupal_get_schema(NULL, TRUE);
@@ -625,8 +294,8 @@ 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()->systemListReset();
+    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.
@@ -761,7 +430,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)) {
@@ -772,171 +441,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
@@ -959,39 +463,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".
  */
 
@@ -1016,172 +487,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.
diff --git a/core/includes/schema.inc b/core/includes/schema.inc
index f46739c..8aa3899 100644
--- a/core/includes/schema.inc
+++ b/core/includes/schema.inc
@@ -79,14 +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')) {
-        // 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');
-      }
+      $extension_handler = drupal_extension_handler();
+      // This function can be called very early in the bootstrap process, so
+      // we force the ExtensionHandler's system list to be refreshed to ensure
+      // that it contains the complete list of modules before we go on to call
+      // module_load_all_includes().
+      $extension_handler->systemListReset();
+      $extension_handler->loadAllIncludes('install');
 
       require_once DRUPAL_ROOT . '/core/includes/common.inc';
       // Invoke hook_schema for all modules.
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index cc29a98..902592e 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -665,7 +665,7 @@ function list_themes($refresh = FALSE) {
 
   if ($refresh) {
     $list = array();
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
   }
 
   if (empty($list)) {
@@ -675,7 +675,7 @@ function list_themes($refresh = FALSE) {
     // Also check that the site is not in the middle of an install or update.
     if (!defined('MAINTENANCE_MODE')) {
       try {
-        $themes = system_list('theme');
+        $themes = drupal_extension_handler()->systemList('theme');
       }
       catch (Exception $e) {
         // If the database is not available, rebuild the theme data.
diff --git a/core/includes/update.inc b/core/includes/update.inc
index bd13dc7..7d141c0 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;
@@ -226,12 +227,15 @@ function update_prepare_d8_bootstrap() {
         ':schema_uninstalled' => SCHEMA_UNINSTALLED,
       ));
       $module_data = _system_rebuild_module_data();
+      $theme_data = _system_rebuild_theme_data();
+      $list = array();
 
       // Migrate each extension into configuration, varying by the extension's
       // status, and record its schema version.
       foreach ($result as $record) {
         if ($record->type == 'module') {
           if ($record->status && isset($module_data[$record->name])) {
+            $list[$record->name] = $module_data[$record->name]->filename;
             $module_config->set('enabled.' . $record->name, $record->weight);
           }
           else {
@@ -240,6 +244,7 @@ function update_prepare_d8_bootstrap() {
         }
         elseif ($record->type == 'theme') {
           if ($record->status) {
+            $list[$record->name] = $theme_data[$record->name]->filename;
             $theme_config->set('enabled.' . $record->name, 0);
           }
           else {
@@ -259,7 +264,7 @@ function update_prepare_d8_bootstrap() {
       // Update the environment for the language bootstrap if needed.
       update_prepare_d8_language();
       // Prime the classloader.
-      system_list('module_enabled');
+      drupal_extension_handler()->systemListWarm($list);
 
       // Change language column to langcode in url_alias.
       if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) {
@@ -464,10 +469,7 @@ function update_module_enable(array $modules) {
     // updates since the module's inception are executed in a core upgrade.
     $schema_store->set($module, 0);
 
-    // system_list_reset() is in module.inc but that would only be available
-    // once the variable bootstrap is done.
-    require_once DRUPAL_ROOT . '/core/includes/module.inc';
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
     //  @todo: figure out what to do about hook_install() and hook_enable().
   }
 }
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index f1e43d6..c4358da 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -35,6 +35,39 @@ public function build(ContainerBuilder $container) {
     $container->register('request', 'Symfony\Component\HttpFoundation\Request')
       ->setSynthetic(TRUE);
 
+    // Register the config services
+    // Register active configuration storage.
+    $container
+      ->register('config.cachedstorage.storage', 'Drupal\Core\Config\FileStorage')
+      ->addArgument(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY));
+    // @todo Replace this with a cache.factory service plus 'config' argument.
+    $container
+      ->register('cache.config')
+      ->setFactoryClass('Drupal\Core\Cache\CacheFactory')
+      ->setFactoryMethod('get')
+      ->addArgument('config');
+
+    $container
+      ->register('config.storage', 'Drupal\Core\Config\CachedStorage')
+      ->addArgument(new Reference('config.cachedstorage.storage'))
+      ->addArgument(new Reference('cache.config'));
+
+    // Register configuration object factory.
+    $container->register('config.subscriber.globalconf', 'Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber');
+    $container->register('dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher')
+      ->addMethodCall('addSubscriber', array(new Reference('config.subscriber.globalconf')));
+    $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
+      ->addArgument(new Reference('config.storage'))
+      ->addArgument(new Reference('dispatcher'));
+
+    // Register staging configuration storage.
+    $container
+      ->register('config.storage.staging', 'Drupal\Core\Config\FileStorage')
+      ->addArgument(config_get_config_directory(CONFIG_STAGING_DIRECTORY));
+    $container
+      ->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage')
+      ->addArgument('state');
+
     $container->register('dispatcher', 'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher')
       ->addArgument(new Reference('service_container'));
     $container->register('resolver', 'Drupal\Core\ControllerResolver')
@@ -79,6 +112,7 @@ public function build(ContainerBuilder $container) {
     $matcher->add($nested, 5);
 
     $content_negotation = new \Drupal\Core\ContentNegotiation();
+    $extension_handler = $container->get('extension_handler');
     $dispatcher->addSubscriber(new \Symfony\Component\HttpKernel\EventListener\RouterListener($matcher));
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\ViewSubscriber($content_negotation));
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\AccessSubscriber());
@@ -87,7 +121,7 @@ public function build(ContainerBuilder $container) {
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\LegacyRequestSubscriber());
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\LegacyControllerSubscriber());
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\FinishResponseSubscriber());
-    $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RequestCloseSubscriber());
+    $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RequestCloseSubscriber($extension_handler));
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber());
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RouteProcessorSubscriber());
     $container->set('content_negotiation', $content_negotation);
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 869d4ee..b4d41dd 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -7,11 +7,14 @@
 
 namespace Drupal\Core;
 
+use Drupal\Core\Cache\CacheFactory;
 use Drupal\Core\CoreBundle;
-use Symfony\Component\HttpKernel\Kernel;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\ExtensionHandler;
+use Drupal\Core\KeyValueStore\KeyValueFactory;
 use Symfony\Component\Config\Loader\LoaderInterface;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use Symfony\Component\HttpKernel\Kernel;
 
 /**
  * The DrupalKernel class is the core of Drupal itself.
@@ -26,6 +29,16 @@
 class DrupalKernel extends Kernel {
 
   /**
+   * ExtensionHandler instance holding the list of enabled modules.
+   */
+  protected $extensionHandler;
+
+  /**
+   * The database connection used by the ExtensionHandler.
+   */
+  protected $connection;
+
+  /**
    * Overrides Kernel::init().
    */
   public function init() {
@@ -36,6 +49,22 @@ public function init() {
   }
 
   /**
+   * Overrides Kernel::boot().
+   */
+  public function boot() {
+    // Instantiate an ExtensionHandler which the Kernel itself needs in order to
+    // find out which modules are enabled. In the buildContainer() method we
+    // register this and the database connection we pass to it as synthetic
+    // services to the container so that they do not need to be instantiated
+    // over again.
+    $this->keyValue = new KeyValueFactory();
+    $class_loader = drupal_classloader();
+    $this->extensionHandler = new ExtensionHandler($this->keyValue, CacheFactory::get('cache'), CacheFactory::get('bootstrap'), $class_loader);
+    parent::boot();
+    drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE);
+  }
+
+  /**
    * Returns an array of available bundles.
    */
   public function registerBundles() {
@@ -43,9 +72,7 @@ public function registerBundles() {
       new CoreBundle(),
     );
 
-    // @todo Remove the necessity of calling system_list() to find out which
-    // bundles exist. See http://drupal.org/node/1331486
-    $modules = array_keys(system_list('module_enabled'));
+    $modules = array_keys($this->extensionHandler->systemList('module_enabled'));
     foreach ($modules as $module) {
       $camelized = ContainerBuilder::camelize($module);
       $class = "Drupal\\{$module}\\{$camelized}Bundle";
@@ -77,10 +104,14 @@ protected function initializeContainer() {
   protected function buildContainer() {
     $container = $this->getContainerBuilder();
 
-    // Merge in the minimal bootstrap container.
-    if ($bootstrap_container = drupal_container()) {
-      $container->merge($bootstrap_container);
-    }
+    // Add our ExtensionHandler and KeyValueFactory as synthetic services.
+    $container->register('extension_handler', 'Drupal\Core\ExtensionHandler')
+      ->setSynthetic(TRUE);
+    $container->set('extension_handler', $this->extensionHandler);
+    $container->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory')
+      ->setSynthetic(TRUE);
+    $container->set('keyvalue', $this->keyValue);
+
     foreach ($this->bundles as $bundle) {
       $bundle->build($container);
     }
diff --git a/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php
index 175ce79..c487d1d 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;
@@ -17,6 +18,18 @@
 class RequestCloseSubscriber implements EventSubscriberInterface {
 
   /**
+   * @var ExtensionHandlerInterface
+   */
+  protected $extensionHandler;
+
+  /**
+   * Constructor.
+   */
+  function __construct(ExtensionHandlerInterface $extension_handler) {
+    $this->extensionHandler = $extension_handler;
+  }
+
+  /**
    * Performs end of request tasks.
    *
    * @todo The body of this function has just been copied almost verbatim from
@@ -30,7 +43,7 @@ class RequestCloseSubscriber implements EventSubscriberInterface {
   public function onTerminate(PostResponseEvent $event) {
     module_invoke_all('exit');
     drupal_cache_system_paths();
-    module_implements_write_cache();
+    $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..745ed5a
--- /dev/null
+++ b/core/lib/Drupal/Core/ExtensionHandler.php
@@ -0,0 +1,776 @@
+<?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;
+use Symfony\Component\ClassLoader\UniversalClassLoader;
+
+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;
+
+  /**
+   * @var \Symfony\Component\ClassLoader\UniversalClassLoader
+   */
+  protected $classLoader;
+
+  /**
+   * Keeps track internally of loaded files.
+   */
+  protected $loadedFiles;
+
+  /**
+   * Keeps track internally of extension filenames.
+   */
+  protected $filenames = array();
+
+  /**
+   * Keeps track internall of extension directories.
+   */
+  protected $directories = array();
+
+  /**
+   * Keeps track internally of enabled modules and themes.
+   */
+  protected $lists;
+
+  /**
+   * 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(KeyValueFactory $key_value, CacheBackendInterface $cache, CacheBackendInterface $bootstrapCache, UniversalClassLoader $loader) {
+    $this->keyValue = $key_value;
+    $this->cache = $cache;
+    $this->bootstrapCache = $bootstrapCache;
+    $this->classLoader = $loader;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::getFilename().
+   */
+  public function getFilename($type, $name, $filename = NULL) {
+
+    // Profiles are converted into modules in system_rebuild_module_data().
+    // @todo Remove false-exposure of profiles as modules.
+    $original_type = $type;
+    if ($type == 'profile') {
+      $type = 'module';
+    }
+    if (!isset($this->filenames[$type])) {
+      $this->filenames[$type] = array();
+    }
+
+    if (!empty($filename) && file_exists($filename)) {
+      $this->filenames[$type][$name] = $filename;
+    }
+    elseif (!isset($this->filenames[$type][$name])) {
+      $file_list = $this->getFileListFromState($type);
+      if ($file_list && isset($file_list[$name]) && file_exists(DRUPAL_ROOT . '/' . $file_list[$name])) {
+        $this->filenames[$type][$name] = $file_list[$name];
+      }
+      // Fallback to searching the filesystem if the database could not find the
+      // file or the file returned by the database is not found.
+      if (!isset($this->filenames[$type][$name])) {
+        // We have consistent directory naming: modules, themes...
+        $dir = $type . 's';
+        if ($type == 'theme_engine') {
+          $dir = 'themes/engines';
+          $extension = 'engine';
+        }
+        elseif ($type == 'theme') {
+          $extension = 'info';
+        }
+        // Profiles are converted into modules in system_rebuild_module_data().
+        // @todo Remove false-exposure of profiles as modules.
+        elseif ($original_type == 'profile') {
+          $dir = 'profiles';
+          $extension = 'profile';
+        }
+        else {
+          $extension = $type;
+        }
+
+        if (!isset($this->directories[$dir][$extension])) {
+          $this->directories[$dir][$extension] = TRUE;
+          if (!function_exists('drupal_system_listing')) {
+            require_once DRUPAL_ROOT . '/core/includes/common.inc';
+          }
+          // Scan the appropriate directories for all files with the requested
+          // extension, not just the file we are currently looking for. This
+          // prevents unnecessary scans from being repeated when this function is
+          // called more than once in the same page request.
+          $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
+          foreach ($matches as $matched_name => $file) {
+            $this->filenames[$type][$matched_name] = $file->uri;
+          }
+        }
+      }
+    }
+
+    if (isset($this->filenames[$type][$name])) {
+      return $this->filenames[$type][$name];
+    }
+  }
+
+  /**
+   * Retrieves the file list from the key/value store's 'state' collection.
+   */
+  protected function getFileListFromState($type) {
+    $file_list = array();
+    try {
+      $file_list = $this->keyValue->get('state')->get('system.' . $type . '.files');
+    }
+    catch (Exception $e) {
+      // The keyvalue service raised an exception because the backend might
+      // be down. We have a fallback for this case so we hide the error
+      // completely.
+    }
+    return $file_list;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::load().
+   */
+  public function load($type, $name) {
+
+    if (isset($this->loadedFiles[$type][$name])) {
+      return TRUE;
+    }
+
+    $filename = $this->getFilename($type, $name);
+
+    if ($filename) {
+      include_once DRUPAL_ROOT . '/' . $filename;
+      $this->loadedFiles[$type][$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;
+    }
+    if (isset($bootstrap) && !$this->loaded) {
+      $type = $bootstrap ? 'bootstrap' : 'module_enabled';
+      foreach ($this->getEnabledModules($type) as $module) {
+        $this->load('module', $module);
+      }
+      // $has_run will be TRUE if $bootstrap is FALSE.
+      $this->loaded = !$bootstrap;
+    }
+    return $this->loaded;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduelList().
+   */
+  public function getEnabledModules($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) {
+    if ($reset) {
+      $this->moduleList = 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 $moduleList in order
+    // to not duplicate the static cache of drupal_extension_handler()->systemList().
+    $list = $this->moduleList;
+
+    if (isset($fixed_list)) {
+      $this->moduleList = array();
+      foreach ($fixed_list as $name => $module) {
+        $this->getFilename('module', $name, $module['filename']);
+        $this->moduleList[$name] = $name;
+      }
+      $list = $this->moduleList;
+    }
+    elseif (!isset($this->moduleList)) {
+      $list = $this->systemList($type);
+    }
+    return $list;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::moduleListReset().
+   */
+  public function moduleListReset() {
+    $this->moduleList = NULL;
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::systemList().
+   */
+  public function systemList($type) {
+    // 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($this->lists['bootstrap'])) {
+        return $lists['bootstrap'];
+      }
+      if ($cached = $this->bootstrapCache->get('bootstrap_modules')) {
+        $bootstrap_list = $cached->data;
+      }
+      else {
+        $bootstrap_list = $this->keyValue->get('state')->get('system.module.bootstrap') ?: array();
+        $this->bootstrapCache->set('bootstrap_modules', $bootstrap_list);
+      }
+      // To avoid a separate database lookup for the filepath, prime the
+      // $filenames internal cache for bootstrap modules only.
+      // The rest is stored separately to keep the bootstrap module cache small.
+      $this->systemListWarm($bootstrap_list);
+      // We only return the module names here since getEnabledModules() doesn't need
+      // the filename itself.
+      $lists['bootstrap'] = array_keys($bootstrap_list);
+    }
+    // Otherwise build the list for enabled modules and themes.
+    elseif (!isset($lists['module_enabled'])) {
+      if ($cached = $this->bootstrapCache->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 = config('system.module')->get('enabled');
+        $module_files = $this->keyValue->get('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 getFilename can use it.
+          $lists['filepaths'][$name] = $module_files[$name];
+        }
+
+        // Build a list of themes.
+        $enabled_themes = 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 = $this->keyValue->get('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 self::load() and include it directly.
+          // @see http://drupal.org/node/1067408
+          if (!function_exists('drupal_system_listing')) {
+            require_once DRUPAL_ROOT . '/core/includes/common.inc';
+          }
+          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 getFilename can use it.
+          if (isset($enabled_themes[$name])) {
+            $lists['filepaths'][$name] = $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'];
+          }
+          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'];
+        }
+        $this->bootstrapCache->set('system_list', $lists);
+      }
+      // To avoid a separate database lookup for the filepath, prime the
+      // $filenames internal cache with all enabled modules and themes.
+      $this->systemListWarm($lists['filepaths']);
+    }
+
+    return $lists[$type];
+  }
+
+  public function systemListWarm($list) {
+    foreach ($list as $name => $filename) {
+      $this->classLoader->registerNamespace('Drupal\\' . $name, DRUPAL_ROOT . '/' . dirname($filename) . '/lib');
+      $this->getFilename('module', $name, $filename);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::systemListReset().
+   */
+  public function systemListReset() {
+    $this->lists = NULL;
+    drupal_static_reset('system_rebuild_module_data');
+    drupal_static_reset('list_themes');
+    $this->bootstrapCache->deleteMultiple(array('bootstrap_modules', 'system_list'));
+    $this->cache->delete('system_info');
+    // Remove last known theme data state.
+    // This causes system_list() to call system_rebuild_theme_data() on its next
+    // invocation. When enabling a module that implements hook_system_info_alter()
+    // to inject a new (testing) theme or manipulate an existing theme, then that
+    // will cause system_list_reset() to be called, but theme data is not
+    // necessarily rebuilt afterwards.
+    // @todo Obsolete with proper installation status for themes.
+    $this->keyValue->get('state')->delete('system.theme.data');
+  }
+
+  /**
+   * 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) {
+    $list = $this->getEnabledModules();
+    return isset($list[$module]);
+  }
+
+  /**
+   * Implements Drupal\Core\ExtensionHandlerInterface::loadAllIncludes().
+   */
+  public function loadAllIncludes($type, $name = NULL) {
+    $modules = $this->getEnabledModules();
+    foreach ($modules as $module) {
+      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->getEnabledModules() as $module) {
+        $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->getEnabledModules() as $module) {
+          $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->getEnabledModules() as $module) {
+          $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() {
+    // 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($this->implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) {
+      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 statically
+    // cache 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($this->getEnabledModules(), 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..6ffc7cc
--- /dev/null
+++ b/core/lib/Drupal/Core/ExtensionHandlerInterface.php
@@ -0,0 +1,278 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\ExtensionHandlerInterface.
+ */
+
+namespace Drupal\Core;
+
+interface ExtensionHandlerInterface {
+
+  /**
+   * Returns and optionally sets the filename for a system resource.
+   *
+   * The filename, whether provided, cached, or retrieved from the database, is
+   * only returned if the file exists.
+   *
+   * This function plays a key role in allowing Drupal's resources (modules
+   * and themes) to be located in different places depending on a site's
+   * configuration. For example, a module 'foo' may legally be be located
+   * in any of these three places:
+   *
+   * core/modules/foo/foo.module
+   * modules/foo/foo.module
+   * sites/example.com/modules/foo/foo.module
+   *
+   * Calling getFilename('module', 'foo') will give you one of the above,
+   * depending on where the module is located.
+   *
+   * @param $type
+   *   The type of the item (i.e. theme, theme_engine, module, profile).
+   * @param $name
+   *   The name of the item for which the filename is requested.
+   * @param $filename
+   *   The filename of the item if it is to be set explicitly rather
+   *   than by consulting the database.
+   *
+   * @return
+   *   The filename of the requested item.
+   */
+  public function getFilename($type, $name, $filename = NULL);
+
+  /**
+   * Includes a file with the provided type and name.
+   *
+   * This prevents including a theme, engine, module, etc., more than once.
+   *
+   * @param $type
+   *   The type of item to load (i.e. theme, theme_engine, module).
+   * @param $name
+   *   The name of the item to load.
+   *
+   * @return
+   *   TRUE if the item is loaded or has already been loaded.
+   */
+  public function load($type, $name);
+
+  /**
+   * Loads all the modules that have been enabled.
+   *
+   * @param $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
+   *   If $bootstrap is NULL, return a boolean indicating whether all modules
+   *   have been loaded.
+   */
+  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($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE);
+
+  /**
+   * Reverts an enforced fixed list of self::getEnabledModules().
+   *
+   * Subsequent calls to getEnabledModules() will no longer use a fixed list.
+   */
+  public function moduleListReset();
+
+  /**
+   * Builds a list of bootstrap modules and enabled modules and themes.
+   *
+   * @param $type
+   *   The type of list to return:
+   *   - module_enabled: All enabled modules.
+   *   - bootstrap: All enabled modules required for bootstrap.
+   *   - theme: All themes.
+   *
+   * @return
+   *   An associative array of modules or themes, keyed by name. For $type
+   *   'bootstrap' and 'module_enabled', the array values equal the keys.
+   *   For $type 'theme', the array values are objects representing the
+   *   respective database row, with the 'info' property already unserialized.
+   *
+   * @see self::getEnabledModules()
+   * @see list_themes()
+   */
+  public function systemList($type);
+
+  /**
+   * Resets all systemList() caches.
+   */
+  public function systemListReset();
+
+  /**
+   * 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/InstallerExtensionHandler.php b/core/lib/Drupal/Core/InstallerExtensionHandler.php
new file mode 100644
index 0000000..702dd10
--- /dev/null
+++ b/core/lib/Drupal/Core/InstallerExtensionHandler.php
@@ -0,0 +1,162 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\ExtensionHandler.
+ */
+
+namespace Drupal\Core;
+
+use Drupal\Core\ExtensionHandler;
+use Symfony\Component\ClassLoader\UniversalClassLoader;
+
+/**
+ * Class representing and ExtensionHandler that the installer can use.
+ *
+ * Overrides all methods in the ExtensionHandler class that attempt to talk to
+ * a database.
+ */
+class InstallerExtensionHandler extends ExtensionHandler {
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::construct().
+   */
+  public function __construct(UniversalClassLoader $loader) {
+    $this->classLoader = $loader;
+  }
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::getFileListFromState().
+   */
+  protected function getFileListFromState($type) {
+    return array();
+  }
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::systemList().
+   */
+  public function systemList($type) {
+    return array();
+  }
+
+  /**
+   * Overrides Drupal\Core\ExtensionHandler::systemListReset().
+   */
+  public function systemListReset() {
+  }
+
+  /**
+   * 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();
+      $enabled = $this->getEnabledModules();
+      foreach ($enabled 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)) {
+          $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->getEnabledModules() as $module) {
+        $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->getEnabledModules() as $module) {
+        $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/KeyValueStore/DatabaseStorage.php b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php
index c837796..20e0c92 100644
--- a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php
+++ b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\KeyValueStore;
 
+use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Query\Merge;
 
 /**
@@ -29,6 +30,11 @@ class DatabaseStorage extends StorageBase {
   protected $table;
 
   /**
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
    * Overrides Drupal\Core\KeyValueStore\StorageBase::__construct().
    *
    * @param string $collection
@@ -39,6 +45,8 @@ class DatabaseStorage extends StorageBase {
   public function __construct($collection, $table = 'key_value') {
     parent::__construct($collection);
     $this->table = $table;
+    // Eventually, inject this.
+    $this->connection = Database::getConnection();
   }
 
   /**
@@ -47,7 +55,7 @@ public function __construct($collection, $table = 'key_value') {
   public function getMultiple(array $keys) {
     $values = array();
     try {
-      $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE name IN (:keys) AND collection = :collection', array(':keys' => $keys, ':collection' => $this->collection))->fetchAllAssoc('name');
+      $result = $this->connection->query('SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN (:keys) AND collection = :collection', array(':keys' => $keys, ':collection' => $this->collection))->fetchAllAssoc('name');
       foreach ($keys as $key) {
         if (isset($result[$key])) {
           $values[$key] = unserialize($result[$key]->value);
@@ -66,7 +74,7 @@ public function getMultiple(array $keys) {
    * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getAll().
    */
   public function getAll() {
-    $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE collection = :collection', array(':collection' => $this->collection));
+    $result = $this->connection->query('SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection', array(':collection' => $this->collection));
     $values = array();
 
     foreach ($result as $item) {
@@ -81,7 +89,7 @@ public function getAll() {
    * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::set().
    */
   public function set($key, $value) {
-    db_merge($this->table)
+    $this->connection->merge($this->table)
       ->key(array(
         'name' => $key,
         'collection' => $this->collection,
@@ -94,7 +102,7 @@ public function set($key, $value) {
    * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::setIfNotExists().
    */
   public function setIfNotExists($key, $value) {
-    $result = db_merge($this->table)
+    $result = $this->connection->merge($this->table)
       ->insertFields(array(
         'collection' => $this->collection,
         'name' => $key,
@@ -112,7 +120,7 @@ public function setIfNotExists($key, $value) {
   public function deleteMultiple(array $keys) {
     // Delete in chunks when a large array is passed.
     do {
-      db_delete($this->table)
+      $this->connection->delete($this->table)
         ->condition('name', array_splice($keys, 0, 1000))
         ->condition('collection', $this->collection)
         ->execute();
diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php
index 7edb754..0a729d0 100644
--- a/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php
+++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\Core\KeyValueStore;
+use Drupal\Core\Database\Connection;
 
 /**
  * Defines the key/value store factory.
diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
index b747a68..ff3847c 100644
--- a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
+++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
@@ -108,7 +108,7 @@ function testEnableForumField() {
     $edit['modules[Core][forum][enable]'] = FALSE;
     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
     $this->assertFalse(module_exists('forum'), 'Forum module is not enabled.');
 
     // Attempt to re-enable the Forum module and ensure it does not try to
@@ -117,7 +117,7 @@ function testEnableForumField() {
     $edit['modules[Core][forum][enable]'] = 'forum';
     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
     $this->assertTrue(module_exists('forum'), 'Forum module is enabled.');
   }
 
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 71135a4..1d25e51 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -487,6 +487,7 @@ function language_save($language) {
 
   // Update URL Prefixes for all languages after the new default language is
   // propagated and the language_list() cache is flushed.
+  language_negotiation_include();
   language_negotiation_url_prefixes_update();
 
   return $language;
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
index 961a556..ee2999d 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
@@ -139,7 +139,7 @@ protected function languageNegotiationUpdate($op = 'enable') {
       $function = "module_{$op}";
       $function($modules);
       // Reset hook implementation cache.
-      module_implements_reset();
+      drupal_extension_handler()->moduleImplementsReset();
     }
 
     drupal_static_reset('language_types_info');
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php
index 017e2c5..9893643 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php
@@ -124,7 +124,7 @@ function testLocaleCompare() {
 
     // Reset static system list caches to reflect info changes.
     drupal_static_reset('locale_translation_project_list');
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
 
     // Check if interface translation data is collected from hook_info.
     $projects = locale_translation_project_list();
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index dc6dae4..b39ff43 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -1008,8 +1008,8 @@ protected function tearDown() {
     }
 
     // Reset module list and module load status.
-    module_list_reset();
-    module_load_all(FALSE, TRUE);
+    drupal_extension_handler()->moduleListReset();
+    module_load_all(FALSE, TRUE, TRUE);
 
     // Restore original shutdown callbacks.
     $callbacks = &drupal_register_shutdown_function();
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index c5dbd0b..64c2086 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -155,6 +155,11 @@
   protected $kernel;
 
   /**
+   * The original extension handler for the parent site.
+   */
+  protected $originalExtensionHandler;
+
+  /**
    * Constructor for Drupal\simpletest\WebTestBase.
    */
   function __construct($test_id = NULL) {
@@ -603,6 +608,10 @@ protected function setUp() {
     // Backup the currently running Simpletest batch.
     $this->originalBatch = batch_get();
 
+    // Store the original ExtensionHandler object so that it can be restored at
+    // tearDown.
+    $this->originalExtensionHandler = drupal_extension_handler();
+
     // Create the database prefix for this test.
     $this->prepareDatabasePrefix();
 
@@ -763,6 +772,10 @@ protected function refreshVariables() {
    * and reset the database prefix.
    */
   protected function tearDown() {
+
+    // Restore the original ExtensionHandler.
+    drupal_container()->set('extension_handler', $this->originalExtensionHandler);
+
     // Destroy the testing kernel.
     if (isset($this->kernel)) {
       $this->kernel->shutdown();
@@ -775,9 +788,10 @@ 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();
+    $extension_handler = drupal_extension_handler();
+    $extension_handler->systemListReset();
+    $extension_handler->moduleListReset();
+    $extension_handler->moduleImplementsReset();
 
     // Reset the Field API.
     field_cache_clear();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php
index df59ea0..6db9d43 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php
@@ -29,7 +29,7 @@ function testClassLoading() {
 
     module_enable(array('module_test', 'module_autoload_test'), FALSE);
     $this->resetAll();
-    // Check twice to test an unprimed and primed system_list() cache.
+    // Check twice to test an unprimed and primed drupal_extension_handler()->systemList() cache.
     for ($i=0; $i<2; $i++) {
       $this->drupalGet('module-test/class-loading');
       $this->assertText($expected, 'Autoloader loads classes from an enabled module.');
@@ -37,7 +37,7 @@ function testClassLoading() {
 
     module_disable(array('module_autoload_test'), FALSE);
     $this->resetAll();
-    // Check twice to test an unprimed and primed system_list() cache.
+    // Check twice to test an unprimed and primed drupal_extension_handler()->systemList() cache.
     for ($i=0; $i<2; $i++) {
       $this->drupalGet('module-test/class-loading');
       $this->assertNoText($expected, 'Autoloader does not load classes from a disabled module.');
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 f6e6929..2989644 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php
@@ -51,7 +51,7 @@ function testModuleList() {
     // Try to mess with the module weights.
     module_set_weight('contact', 20);
     // Reset the module list.
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
     // Move contact to the end of the array.
     unset($module_list[array_search('contact', $module_list)]);
     $module_list[] = 'contact';
@@ -67,7 +67,7 @@ function testModuleList() {
     $this->assertModuleList($new_module_list, t('When using a fixed list'));
 
     // Reset the module list.
-    module_list_reset();
+    drupal_extension_handler()->moduleListReset();
     $this->assertModuleList($module_list, t('After reset'));
   }
 
@@ -106,9 +106,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 f23204b..0168350 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,7 @@ function assertNoModuleConfig($module) {
    *   Expected module state.
    */
   function assertModules(array $modules, $enabled) {
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
     foreach ($modules as $module) {
       if ($enabled) {
         $message = 'Module "@module" is enabled.';
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php
index 8d004ab..f8c2684 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php
@@ -39,9 +39,9 @@ function testSystemInfoAlter() {
     $this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was returned by system_get_info().'));
     $seven_regions = system_region_list('seven');
     $this->assertTrue(isset($seven_regions['test_region']), t('Altered theme info was returned by system_region_list().'));
-    $system_list_themes = system_list('theme');
+    $system_list_themes = drupal_extension_handler()->systemList('theme');
     $info = $system_list_themes['seven']->info;
-    $this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was returned by system_list().'));
+    $this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was returned by systemList().'));
     $list_themes = list_themes();
     $this->assertTrue(isset($list_themes['seven']->info['regions']['test_region']), t('Altered theme info was returned by list_themes().'));
 
@@ -53,9 +53,9 @@ function testSystemInfoAlter() {
     $this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was not returned by system_get_info().'));
     $seven_regions = system_region_list('seven');
     $this->assertFalse(isset($seven_regions['test_region']), t('Altered theme info was not returned by system_region_list().'));
-    $system_list_themes = system_list('theme');
+    $system_list_themes = drupal_extension_handler()->systemList('theme');
     $info = $system_list_themes['seven']->info;
-    $this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was not returned by system_list().'));
+    $this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was not returned by systemList().'));
     $list_themes = list_themes();
     $this->assertFalse(isset($list_themes['seven']->info['regions']['test_region']), t('Altered theme info was not returned by list_themes().'));
   }
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php b/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php
index e1fd6f6..7f2ebd6 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php
@@ -56,7 +56,7 @@ function testMainContentFallback() {
     $edit['modules[Core][block][enable]'] = FALSE;
     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
     $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.'));
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
     $this->assertFalse(module_exists('block'), t('Block module disabled.'));
 
     // At this point, no region is filled and fallback should be triggered.
@@ -90,7 +90,7 @@ function testMainContentFallback() {
     $edit['modules[Core][block][enable]'] = 'block';
     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
     $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.'));
-    system_list_reset();
+    drupal_extension_handler()->systemListReset();
     $this->assertTrue(module_exists('block'), t('Block module re-enabled.'));
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
index f63e978..a940de7 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
@@ -241,9 +241,9 @@ protected function performUpgrade($register_errors = TRUE) {
 
     // Reload module list for modules that are enabled in the test database
     // but not on the test client.
-    system_list_reset();
-    module_implements_reset();
-    module_load_all(FALSE, TRUE);
+    drupal_extension_handler()->systemListReset();
+    drupal_extension_handler()->moduleImplementsReset();
+    module_load_all(FALSE, TRUE, TRUE);
 
     // Rebuild caches.
     // @todo Remove the try/catch when UpgradePathTestBase::setup() is fixed to
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index ea3dadc..8e681f7 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2707,7 +2707,7 @@ function system_get_info($type, $name = NULL) {
     }
   }
   else {
-    $list = system_list($type);
+    $list = drupal_extension_handler()->systemList($type);
     foreach ($list as $shortname => $item) {
       if (!empty($item->status)) {
         $info[$shortname] = $item->info;
@@ -2854,7 +2854,8 @@ 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/update.php b/core/update.php
index 2273b40..145a39d 100644
--- a/core/update.php
+++ b/core/update.php
@@ -423,7 +423,7 @@ function update_check_requirements($skip_warnings = FALSE) {
 
   // Reset the module_implements() cache so that any new hook implementations
   // in updated code are picked up.
-  module_implements_reset();
+  drupal_extension_handler()->moduleImplementsReset();
 
   // Set up $language, since the installer components require it.
   drupal_language_initialize();
diff --git a/index.php b/index.php
index 38177ab..03724d2 100644
--- a/index.php
+++ b/index.php
@@ -25,7 +25,7 @@
 // @see Drupal\Core\EventSubscriber\PathSubscriber;
 // @see Drupal\Core\EventSubscriber\LegacyRequestSubscriber;
 require_once DRUPAL_ROOT . '/core/includes/bootstrap.inc';
-drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE);
+drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
 
 // @todo Figure out how best to handle the Kernel constructor parameters.
 $kernel = new DrupalKernel('prod', FALSE);
