diff --git a/core/includes/common.inc b/core/includes/common.inc
index 8d9de117006c914cd4737d52f14339831816937c..7fa79d1d28f5b4a666406530c372de4758e5cae5 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -7,6 +7,7 @@
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Database\Database;
+use Drupal\Core\SystemListingInfo;
 use Drupal\Core\Template\Attribute;
 
 /**
@@ -5099,140 +5100,16 @@ function drupal_cron_cleanup() {
 }
 
 /**
- * Returns information about system object files (modules, themes, etc.).
+ * This function is kept only for backward compatibility.
  *
- * This function is used to find all or some system object files (module files,
- * theme files, etc.) that exist on the site. It searches in several locations,
- * depending on what type of object you are looking for. For instance, if you
- * are looking for modules and call:
- * @code
- * drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules');
- * @endcode
- * this function will search:
- * - the core modules directory; i.e., /core/modules
- * - the testing profile directory; e.g., /profiles/testing/modules
- * - your installation profile's directory; e.g., /profiles/standard/modules
- * - the site-wide modules directory; i.e., /modules
- * - the all-sites directory; i.e., /sites/all/modules
- * - the site-specific directory; i.e., /sites/example.com/modules
- * in that order, and return information about all of the files ending in
- * .module in those directories.
- *
- * The information is returned in an associative array, which can be keyed on
- * the file name ($key = 'filename'), the file name without the extension ($key
- * = 'name'), or the full file stream URI ($key = 'uri'). If you use a key of
- * 'filename' or 'name', files found later in the search will take precedence
- * over files found earlier (unless they belong to a module or theme not
- * compatible with Drupal core); if you choose a key of 'uri', you will get all
- * files found.
- *
- * @param string $mask
- *   The preg_match() regular expression for the files to find. The expression
- *   must be anchored and use DRUPAL_PHP_FUNCTION_PATTERN for the file name part
- *   before the extension, since the results could contain matches that do not
- *   present valid Drupal extensions otherwise.
- * @param string $directory
- *   The subdirectory name in which the files are found. For example,
- *   'modules' will search all 'modules' directories and their sub-directories
- *   as explained above.
- * @param string $key
- *   (optional) The key to be used for the associative array returned. Possible
- *   values are:
- *   - 'uri' for the file's URI.
- *   - 'filename' for the basename of the file.
- *   - 'name' for the name of the file without the extension.
- *   For 'name' and 'filename' only the highest-precedence file is returned.
- *   Defaults to 'name'.
- * @param int $min_depth
- *   (optional) Minimum depth of directories to return files from, relative to
- *   each directory searched. For instance, a directory of 'modules' and a
- *   minimum depth of 2 would find modules inside /modules/node/tests, but not
- *   modules directly in /modules/node. Defaults to 1. The default is sufficient
- *   and suitable for all extension types known by Drupal core.
- *
- * @return array
- *   An associative array of file objects, keyed on the chosen key. Each element
- *   in the array is an object containing file information, with properties:
- *   - 'uri': Full URI of the file.
- *   - 'filename': File name.
- *   - 'name': Name of file without the extension.
+ * @see \Drupal\Core\SystemListing::scan().
  */
 function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) {
-  $config = conf_path();
-  $files = array();
-
-  // Search for the directory in core.
-  $searchdir = array('core/' . $directory);
-
-  // The 'core/profiles' directory contains pristine collections of modules and
-  // themes as provided by a distribution. It is pristine in the same way that
-  // the 'core/modules' directory is pristine for core; users should avoid
-  // any modification by using the top-level or sites/<domain> directories.
-  $profile = drupal_get_profile();
-  // For SimpleTest to be able to test modules packaged together with a
-  // distribution we need to include the profile of the parent site (in which
-  // test runs are triggered).
-  if (drupal_valid_test_ua()) {
-    $testing_profile = config('simpletest.settings')->get('parent_profile');
-    if ($testing_profile && $testing_profile != $profile) {
-      $searchdir[] = drupal_get_path('profile', $testing_profile) . '/' . $directory;
-    }
-  }
-  // In case both profile directories contain the same extension, the actual
-  // profile always has precedence.
-  $searchdir[] = drupal_get_path('profile', $profile) . '/' . $directory;
-
-  // Always search for contributed and custom extensions in top-level
-  // directories as well as sites/all/* directories. If the same extension is
-  // located in both directories, then the latter wins for legacy/historical
-  // reasons.
-  $searchdir[] = $directory;
-  $searchdir[] = 'sites/all/' . $directory;
-
-  if (file_exists("$config/$directory")) {
-    $searchdir[] = "$config/$directory";
-  }
-
-  // Get current list of items.
-  if (!function_exists('file_scan_directory')) {
-    require_once DRUPAL_ROOT . '/core/includes/file.inc';
-  }
-  foreach ($searchdir as $dir) {
-    $files_to_add = file_scan_directory($dir, $mask, array(
-      'key' => $key,
-      'min_depth' => $min_depth,
-      // Do not recurse into ./lib directories; they cannot contain extensions.
-      // We also skip templates, css, and js directories.
-      // @todo Find a way to skip ./config directories (but not modules/config).
-      'nomask' => '/^(CVS|lib|templates|css|js)$/',
-    ));
-
-    // Duplicate files found in later search directories take precedence over
-    // earlier ones, so we want them to overwrite keys in our resulting
-    // $files array.
-    // The exception to this is if the later file is from a module or theme not
-    // compatible with Drupal core. This may occur during upgrades of Drupal
-    // core when new modules exist in core while older contrib modules with the
-    // same name exist in a directory such as /modules.
-    foreach (array_intersect_key($files_to_add, $files) as $file_key => $file) {
-      // If it has no info file, then we just behave liberally and accept the
-      // new resource on the list for merging.
-      if (file_exists($info_file = DRUPAL_ROOT . '/' . dirname($file->uri) . '/' . $file->name . '.info')) {
-        // Get the .info file for the module or theme this file belongs to.
-        $info = drupal_parse_info_file($info_file);
-
-        // If the module or theme is incompatible with Drupal core, remove it
-        // from the array for the current search directory, so it is not
-        // overwritten when merged with the $files array.
-        if (isset($info['core']) && $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
-          unset($files_to_add[$file_key]);
-        }
-      }
-    }
-    $files = array_merge($files, $files_to_add);
-  }
-
-  return $files;
+  // As SystemListing is required to build a dependency injection container
+  // from scratch and SystemListingInfo only extends SystemLising, this
+  // class needs to be hardwired.
+  $listing = new SystemListingInfo();
+  return $listing->scan($mask, $directory, $key, $min_depth);
 }
 
 /**
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index b778162776f63ed7c37969800c583fc1e888ef20..d5983b6d0faead9c7c0198094b4c65bbc4896f0e 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1502,7 +1502,7 @@ function install_bootstrap_full(&$install_state) {
   module_list_reset();
 
   // Instantiate the kernel.
-  $kernel = new DrupalKernel('prod', FALSE, NULL);
+  $kernel = new DrupalKernel('prod', FALSE, drupal_classloader());
   $kernel->boot();
   drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 }
diff --git a/core/includes/module.inc b/core/includes/module.inc
index 0c1a30b4890c074a4bb95c5f2272644394352549..a711cf1c421ea739f4c6048286b3966762bc36b5 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -507,7 +507,7 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
       // @todo The if statement is here because install_begin_request() creates
       //   a container without a kernel. It probably shouldn't.
       if ($kernel = drupal_container()->get('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
-        $kernel->updateModules(module_list());
+        $kernel->updateModules(module_list(), array($module => drupal_get_path('module', $module)));
       }
       // Refresh the schema to include it.
       drupal_get_schema(NULL, TRUE);
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 892571fc130faec19dd39cfa9232581d1de820ad..072e1096e3a7f80ffc62913ffa65ae97fd1ed3d3 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -8,6 +8,8 @@
 namespace Drupal\Core;
 
 use Drupal\Core\Cache\CacheBackendInterface;
+use Symfony\Component\ClassLoader\UniversalClassLoader;
+use Drupal\Core\Config\FileStorage;
 use Drupal\Core\CoreBundle;
 use Drupal\Component\PhpStorage\PhpStorageInterface;
 use Symfony\Component\HttpKernel\Kernel;
@@ -32,15 +34,29 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface {
    * Holds the list of enabled modules.
    *
    * @var array
+   *   An associative array whose keys are module names and whose values are
+   *   ignored.
    */
   protected $moduleList;
 
   /**
-   * Cache object for getting or setting the compiled container's class name.
+   * Holds an updated list of enabled modules.
    *
-   * @var \Drupal\Core\Cache\CacheBackendInterface
+   * @var array
+   *   An associative array whose keys are module names and whose values are
+   *   ignored.
+   */
+  protected $newModuleList;
+
+  /**
+   * An array of module data objects.
+   *
+   * The data objects have the same data structure as returned by
+   * file_scan_directory() but only the uri property is used.
+   *
+   * @var array
    */
-  protected $compilationIndexCache;
+  protected $moduleData = array();
 
   /**
    * PHP code storage object to use for the compiled container.
@@ -49,6 +65,20 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface {
    */
   protected $storage;
 
+  /**
+   * The classloader object.
+   *
+   * @var \Symfony\Component\ClassLoader\UniversalClassLoader
+   */
+  protected $classLoader;
+
+  /**
+   * The list of the classnames of the bundles in this kernel.
+   *
+   * @var array
+   */
+  protected $bundleClasses;
+
   /**
    * Constructs a DrupalKernel object.
    *
@@ -60,26 +90,20 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface {
    *   Boolean indicating whether we are in debug mode. Used by
    *   Symfony\Component\HttpKernel\Kernel::__construct(). Drupal does not use
    *   this value currently. Pass TRUE.
-   * @param array $module_list
-   *   (optional) The array of enabled modules as returned by module_list().
-   * @param Drupal\Core\Cache\CacheBackendInterface $compilation_index_cache
-   *   (optional) If wanting to dump a compiled container to disk or use a
-   *   previously compiled container, the cache object for the bin that stores
-   *   the class name of the compiled container.
-   */
-  public function __construct($environment, $debug, array $module_list = NULL, CacheBackendInterface $compilation_index_cache = NULL) {
+   * @param \Symfony\Component\ClassLoader\UniversalClassLoader $class_loader
+   *   (optional) The classloader is only used if $storage is not given or
+   *   the load from storage fails and a container rebuild is required. In
+   *   this case, the loaded modules will be registered with this loader in
+   *   order to be able to find the module bundles.
+   * @param \Drupal\Component\PhpStorage\PhpStorageInterface $storage
+   *   (optional) An object handling the load and save of the compiled
+   *   container. If not specified, the container will neither be stored to
+   *   disk nor read from there.
+   */
+  public function __construct($environment, $debug, UniversalClassLoader $class_loader, PhpStorageInterface $storage = NULL) {
     parent::__construct($environment, $debug);
-    $this->compilationIndexCache = $compilation_index_cache;
-    $this->storage = drupal_php_storage('service_container');
-    if (isset($module_list)) {
-      $this->moduleList = $module_list;
-    }
-    else {
-      // @todo This is a temporary measure which will no longer be necessary
-      //   once we have an ExtensionHandler for managing this list. See
-      //   http://drupal.org/node/1331486.
-      $this->moduleList = module_list();
-    }
+    $this->storage = $storage;
+    $this->classLoader = $class_loader;
   }
 
   /**
@@ -92,6 +116,17 @@ public function init() {
     //   handling to the one of Kernel without losing functionality.
   }
 
+  /**
+   * Overrides Kernel::boot().
+   */
+  public function boot() {
+    if ($this->booted) {
+      return;
+    }
+    $this->initializeContainer();
+    $this->booted = TRUE;
+  }
+
   /**
    * Returns an array of available bundles.
    */
@@ -99,22 +134,72 @@ public function registerBundles() {
     $bundles = array(
       new CoreBundle(),
     );
+    if (!isset($this->moduleList)) {
+      $storage = new FileStorage(config_get_config_directory());
+      $module_list = $storage->read('system.module');
+      $this->moduleList = isset($module_list['enabled']) ? $module_list['enabled'] : array();
+    }
 
-    foreach ($this->moduleList as $module) {
+    $namespaces = $this->classLoader->getNamespaces();
+    foreach ($this->moduleList as $module => $weight) {
+      // When installing new modules, the modules in the list passed to
+      // updateModules() do not yet have their namespace registered.
+      $namespace = 'Drupal\\' . $module;
+      if (!isset($namespaces[$namespace]) && $this->moduleData($module)) {
+        $this->classLoader->registerNamespace($namespace, dirname(DRUPAL_ROOT . '/' . $this->moduleData($module)->uri) . '/lib');
+      }
       $camelized = ContainerBuilder::camelize($module);
       $class = "Drupal\\{$module}\\{$camelized}Bundle";
       if (class_exists($class)) {
         $bundles[] = new $class();
+        $this->bundleClasses[] = $class;
       }
     }
     return $bundles;
   }
 
+  /**
+   * Returns module data on the filesystem.
+   *
+   * @param $module
+   *   The name of the module.
+   *
+   * @return \stdClass|bool
+   *   Returns a stdClass object if the module data is found containing at
+   *   least an uri property with the module path, for example
+   *   core/modules/user/user.module.
+   */
+  protected function moduleData($module) {
+    if (!$this->moduleData) {
+      // Find filenames to prime the classloader. First, find profiles.
+      // Profiles might want to add a bundle too and they also can contain
+      // modules.
+      $profiles_scanner = new SystemListing();
+      $all_profiles = $profiles_scanner->scan('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles');
+      $profiles = array_keys(array_intersect_key($this->moduleList, $all_profiles));
+      $storage = new FileStorage(config_get_config_directory());
+      // If a module is within a profile directory but specifies another
+      // profile for testing, it needs to be found in the parent profile.
+      if (($parent_profile_config = $storage->read('simpletest.settings')) && isset($parent_profile_config['parent_profile']) && $parent_profile_config['parent_profile'] != $profiles[0]) {
+        // In case both profile directories contain the same extension, the
+        // actual profile always has precedence.
+        array_unshift($profiles, $parent_profile_config['parent_profile']);
+      }
+      // Now find modules.
+      $modules_scanner = new SystemListing($profiles);
+      $this->moduleData = $all_profiles + $modules_scanner->scan('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules');
+    }
+    return isset($this->moduleData[$module]) ? $this->moduleData[$module] : FALSE;
+  }
+
   /**
    * Implements Drupal\Core\DrupalKernelInterface::updateModules().
    */
-  public function updateModules($module_list) {
-    $this->moduleList = $module_list;
+  public function updateModules(array $module_list, array $module_paths = array()) {
+    $this->newModuleList = $module_list;
+    foreach ($module_paths as $module => $path) {
+      $this->moduleData[$module] = (object) array('uri' => $path);
+    }
     // If we haven't yet booted, we don't need to do anything: the new module
     // list will take effect when boot() is called. If we have already booted,
     // then reboot in order to refresh the bundle list and container.
@@ -125,34 +210,64 @@ public function updateModules($module_list) {
     }
   }
 
+  /**
+   * Returns the classname based on environment, debug and testing prefix.
+   *
+   * @return string
+   *   The class name.
+   */
+  protected function getClassName() {
+    $parts = array('service_container', $this->environment, $this->debug);
+    // Make sure to use a testing-specific container even in the parent site.
+    if (!empty($GLOBALS['drupal_test_info']['test_run_id'])) {
+      $parts[] = $GLOBALS['drupal_test_info']['test_run_id'];
+    }
+    elseif ($prefix = drupal_valid_test_ua()) {
+      $parts[] = $prefix;
+    }
+    return implode('_', $parts);
+  }
+
   /**
    * Initializes the service container.
    */
   protected function initializeContainer() {
     $this->container = NULL;
-    if ($this->compilationIndexCache) {
-      // The name of the compiled container class is generated from the hash of
-      // its contents and cached. This enables multiple compiled containers
-      // (for example, for different states of which modules are enabled) to
-      // exist simultaneously on disk and in memory.
-      if ($cache = $this->compilationIndexCache->get(implode(':', array('service_container', $this->environment, $this->debug)))) {
-        $class = $cache->data;
-        $cache_file = $class . '.php';
-
-        // First, try to load.
-        if (!class_exists($class, FALSE)) {
-          $this->storage->load($cache_file);
-        }
-        // If the load succeeded or the class already existed, use it.
-        if (class_exists($class, FALSE)) {
-          $fully_qualified_class_name = '\\' . $class;
-          $this->container = new $fully_qualified_class_name;
-        }
+    $class = $this->getClassName();
+    $cache_file = $class . '.php';
+
+    if ($this->storage) {
+      // First, try to load.
+      if (!class_exists($class, FALSE)) {
+        $this->storage->load($cache_file);
+      }
+      // If the load succeeded or the class already existed, use it.
+      if (class_exists($class, FALSE)) {
+        $fully_qualified_class_name = '\\' . $class;
+        $this->container = new $fully_qualified_class_name;
       }
     }
+    // First check whether the list of modules changed in this request.
+    if (isset($this->newModuleList)) {
+      if (isset($this->container) && isset($this->moduleList) && array_keys($this->moduleList) !== array_keys($this->newModuleList)) {
+        unset($this->container);
+      }
+      $this->moduleList = $this->newModuleList;
+      unset($this->newModuleList);
+    }
+    // Second, verify that some other request -- for example on another
+    // web frontend or during the installer -- changed the list of enabled
+    // modules.
+    if (isset($this->container)) {
+      $module_list = $this->moduleList ?: $this->container->get('config.factory')->get('system.module')->load()->get('enabled');
+      if (array_keys((array)$module_list) !== $this->container->getParameter('container.modules')) {
+        unset($this->container);
+      }
+    }
+
     if (!isset($this->container)) {
       $this->container = $this->buildContainer();
-      if ($this->compilationIndexCache && !$this->dumpDrupalContainer($this->container, $this->getContainerBaseClass())) {
+      if ($this->storage && !$this->dumpDrupalContainer($this->container, $this->getContainerBaseClass())) {
         // We want to log this as an error but we cannot call watchdog() until
         // the container has been fully built and set in drupal_container().
         $error = 'Container cannot be written to disk';
@@ -174,7 +289,10 @@ protected function initializeContainer() {
    * @return ContainerBuilder The compiled service container
    */
   protected function buildContainer() {
+    $this->initializeBundles();
     $container = $this->getContainerBuilder();
+    $container->setParameter('container.bundles', $this->bundleClasses);
+    $container->setParameter('container.modules', array_keys($this->moduleList));
 
     // Merge in the minimal bootstrap container.
     if ($bootstrap_container = drupal_container()) {
@@ -216,11 +334,8 @@ protected function dumpDrupalContainer(ContainerBuilder $container, $baseClass)
     }
     // Cache the container.
     $dumper = new PhpDumper($container);
-    $content = $dumper->dump(array('class' => 'DrupalServiceContainerStub', 'base_class' => $baseClass));
-    $class = 'c' . hash('sha256', $content);
-    $content = str_replace('DrupalServiceContainerStub', $class, $content);
-    $this->compilationIndexCache->set(implode(':', array('service_container', $this->environment, $this->debug)), $class);
-
+    $class = $this->getClassName();
+    $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass));
     return $this->storage->save($class . '.php', $content);
   }
 
diff --git a/core/lib/Drupal/Core/DrupalKernelInterface.php b/core/lib/Drupal/Core/DrupalKernelInterface.php
index 828f3ba116dd97a915c65b743f7e5ff5d22376ad..d4c8b8be52b52c2b084c5ae177e764b0ea24ec80 100644
--- a/core/lib/Drupal/Core/DrupalKernelInterface.php
+++ b/core/lib/Drupal/Core/DrupalKernelInterface.php
@@ -25,6 +25,8 @@ interface DrupalKernelInterface extends KernelInterface {
    *
    * @param array $module_list
    *   The new list of modules.
+   * @param array $module_path
+   *   List of module paths, keyed by module name.
    */
-  public function updateModules($module_list);
+  public function updateModules(array $module_list, array $module_path = array());
 }
diff --git a/core/lib/Drupal/Core/SystemListing.php b/core/lib/Drupal/Core/SystemListing.php
new file mode 100644
index 0000000000000000000000000000000000000000..6390f2bc71407af755d1e37cb46412cb19498f71
--- /dev/null
+++ b/core/lib/Drupal/Core/SystemListing.php
@@ -0,0 +1,198 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\SystemListing.
+ */
+
+namespace Drupal\Core;
+
+/**
+ * Returns information about system object files (modules, themes, etc.).
+ *
+ * This class requires the list of profiles to be scanned (see
+ * \Drupal\Core\SystemListing::scan) to be passed into the constructor. Also,
+ * info files are not parsed.
+ */
+class SystemListing {
+
+  /**
+   * Construct this listing object.
+   *
+   * @param array $profiles
+   *   A list of profiles to search their directories for in addition to the
+   *   default directories.
+   */
+  function __construct($profiles = array()) {
+    $this->profiles = $profiles;
+  }
+
+  /**
+   * Returns information about system object files (modules, themes, etc.).
+   *
+   * This function is used to find all or some system object files (module
+   * files, theme files, etc.) that exist on the site. It searches in several
+   * locations, depending on what type of object you are looking for. For
+   * instance, if you are looking for modules and call:
+   * @code
+   * $scanner = new SystemListing();
+   * $all_profiles = $profiles_scanner->scan('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles');
+   * @endcode
+   * this function will search:
+   * - the core modules directory; i.e., /core/modules
+   * - the profiles directories as defined by the profiles() method.
+   * - the site-wide modules directory; i.e., /modules
+   * - the all-sites directory; i.e., /sites/all/modules
+   * - the site-specific directory; i.e., /sites/example.com/modules
+   * in that order, and return information about all of the files ending in
+   * .module in those directories.
+   *
+   * The information is returned in an associative array, which can be keyed
+   * on the file name ($key = 'filename'), the file name without the extension
+   * ($key = 'name'), or the full file stream URI ($key = 'uri'). If you use a
+   * key of 'filename' or 'name', files found later in the search will take
+   * precedence over files found earlier (unless they belong to a module or
+   * theme not compatible with Drupal core); if you choose a key of 'uri',
+   * you will get all files found.
+   *
+   * @param string $mask
+   *   The preg_match() regular expression for the files to find. The
+   *   expression must be anchored and use DRUPAL_PHP_FUNCTION_PATTERN for the
+   *   file name part before the extension, since the results could contain
+   *   matches that do not present valid Drupal extensions otherwise.
+   * @param string $directory
+   *   The subdirectory name in which the files are found. For example,
+   *   'modules' will search all 'modules' directories and their
+   *   sub-directories as explained above.
+   * @param string $key
+   *   (optional) The key to be used for the associative array returned.
+   *   Possible values are:
+   *   - 'uri' for the file's URI.
+   *   - 'filename' for the basename of the file.
+   *   - 'name' for the name of the file without the extension.
+   *   For 'name' and 'filename' only the highest-precedence file is returned.
+   *   Defaults to 'name'.
+   *
+   * @return array
+   *   An associative array of file objects, keyed on the chosen key. Each
+   *   element in the array is an object containing file information, with
+   *   properties:
+   *   - 'uri': Full URI of the file.
+   *   - 'filename': File name.
+   *   - 'name': Name of file without the extension.
+   */
+  function scan($mask, $directory, $key = 'name') {
+    if (!in_array($key, array('uri', 'filename', 'name'))) {
+      $key = 'uri';
+    }
+    $config = conf_path();
+    $files = array();
+
+    // Search for the directory in core.
+    $searchdir = array('core/' . $directory);
+    foreach ($this->profiles($directory) as $profile) {
+      $searchdir[] = $profile;
+    }
+
+    // Always search for contributed and custom extensions in top-level
+    // directories as well as sites/all/* directories. If the same extension is
+    // located in both directories, then the latter wins for legacy/historical
+    // reasons.
+    $searchdir[] = $directory;
+    $searchdir[] = 'sites/all/' . $directory;
+
+    if (file_exists("$config/$directory")) {
+      $searchdir[] = "$config/$directory";
+    }
+    // @todo Find a way to skip ./config directories (but not modules/config).
+    $nomask = '/^(CVS|lib|templates|css|js)$/';
+    $files = array();
+    // Get current list of items.
+    foreach ($searchdir as $dir) {
+      $files = array_merge($files, $this->process($files, $this->scanDirectory($dir, $key, $mask, $nomask)));
+    }
+    return $files;
+  }
+
+  /**
+   * List the profiles for this directory.
+   *
+   * This version only returns those passed to the constructor.
+   *
+   * @param string $directory
+   *   The current search directory like 'modules' or 'themes'.
+   *
+   * @return array
+   *   A list of profiles.
+   */
+  protected function profiles($directory) {
+    return $this->profiles;
+  }
+
+  /**
+   * Process the files to add before adding them.
+   *
+   * @param array $files
+   *   Every file found so far.
+   * @param array $files_to_add
+   *   The files found in a single directory.
+   *
+   * @return array
+   *   The processed list of file objects. For example, the SystemListingInfo
+   *   class removes files not compatible with the current core version.
+   */
+  protected function process(array $files, array $files_to_add) {
+    return $files_to_add;
+  }
+
+  /**
+   * Abbreviated version of file_scan_directory().
+   *
+   * @param $dir
+   *   The base directory or URI to scan, without trailing slash.
+   * @param $key
+   *   The key to be used for the returned associative array of files.
+   *     Possible values are 'uri', for the file's URI; 'filename', for the
+   *     basename of the file; and 'name' for the name of the file without the
+   *     extension.
+   * @param $mask
+   *   The preg_match() regular expression of the files to find.
+   * @param $nomask
+   *   The preg_match() regular expression of the files to ignore.
+   *
+   * @return array
+   *   An associative array (keyed on the chosen key) of objects with 'uri',
+   *   'filename', and 'name' members corresponding to the matching files.
+   */
+  protected function scanDirectory($dir, $key, $mask, $nomask) {
+    $files = array();
+    if (is_dir($dir)) {
+      // Avoid warnings when opendir does not have the permissions to open a
+      // directory.
+      if ($handle = @opendir($dir)) {
+        while (FALSE !== ($filename = readdir($handle))) {
+          // Skip this file if it matches the nomask or starts with a dot.
+          if ($filename[0] != '.' && !preg_match($nomask, $filename)) {
+            $uri = "$dir/$filename";
+            if (is_dir($uri)) {
+              // Give priority to files in this folder by merging them in after
+              // any subdirectory files.
+              $files = array_merge($this->scanDirectory($uri, $key, $mask, $nomask), $files);
+            }
+            elseif (preg_match($mask, $filename)) {
+              // Always use this match over anything already set in $files with
+              // the same $options['key'].
+              $file = new \stdClass();
+              $file->uri = $uri;
+              $file->filename = $filename;
+              $file->name = pathinfo($filename, PATHINFO_FILENAME);
+              $files[$file->$key] = $file;
+            }
+          }
+        }
+        closedir($handle);
+      }
+    }
+    return $files;
+  }
+}
diff --git a/core/lib/Drupal/Core/SystemListingInfo.php b/core/lib/Drupal/Core/SystemListingInfo.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d45e1dc501f1d34034af1a4395ebdde91a7d628
--- /dev/null
+++ b/core/lib/Drupal/Core/SystemListingInfo.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\SystemListingInfo.
+ */
+
+namespace Drupal\Core;
+
+/**
+ * Returns information about system object files (modules, themes, etc.).
+ *
+ * This class finds the profile directories itself and also parses info files.
+ */
+class SystemListingInfo extends SystemListing {
+
+  /**
+   * Overrides Drupal\Core\SystemListing::profiles().
+   */
+  protected function profiles($directory) {
+    $searchdir = array();
+    // The 'core/profiles' directory contains pristine collections of modules
+    // and themes as provided by a distribution. It is pristine in the same
+    // way that the 'core/modules' directory is pristine for core; users
+    // should avoid any modification by using the top-level or sites/<domain>
+    // directories.
+    $profile = drupal_get_profile();
+    // For SimpleTest to be able to test modules packaged together with a
+    // distribution we need to include the profile of the parent site (in
+    // which test runs are triggered).
+    if (drupal_valid_test_ua()) {
+      $testing_profile = config('simpletest.settings')->get('parent_profile');
+      if ($testing_profile && $testing_profile != $profile) {
+        $searchdir[] = drupal_get_path('profile', $testing_profile) . '/' . $directory;
+      }
+    }
+    // In case both profile directories contain the same extension, the actual
+    // profile always has precedence.
+    $searchdir[] = drupal_get_path('profile', $profile) . '/' . $directory;
+    return $searchdir;
+  }
+
+  /**
+   * Overrides Drupal\Core\SystemListing::process().
+   */
+  protected function process(array $files, array $files_to_add) {
+    // Duplicate files found in later search directories take precedence over
+    // earlier ones, so we want them to overwrite keys in our resulting
+    // $files array.
+    // The exception to this is if the later file is from a module or theme not
+    // compatible with Drupal core. This may occur during upgrades of Drupal
+    // core when new modules exist in core while older contrib modules with the
+    // same name exist in a directory such as /modules.
+    foreach (array_intersect_key($files_to_add, $files) as $file_key => $file) {
+      // If it has no info file, then we just behave liberally and accept the
+      // new resource on the list for merging.
+      if (file_exists($info_file = dirname($file->uri) . '/' . $file->name . '.info')) {
+        // Get the .info file for the module or theme this file belongs to.
+        $info = drupal_parse_info_file($info_file);
+
+        // If the module or theme is incompatible with Drupal core, remove it
+        // from the array for the current search directory, so it is not
+        // overwritten when merged with the $files array.
+        if (isset($info['core']) && $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
+          unset($files_to_add[$file_key]);
+        }
+      }
+    }
+    return $files_to_add;
+  }
+
+}
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index fdb819aaebada023ce3763de1054010711ad5eeb..27333e8f84cc905506a1e8ee9cc36a01856d3424 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -912,7 +912,7 @@ protected function rebuildContainer() {
     // container in drupal_container(). Drupal\simpletest\TestBase::tearDown()
     // restores the original container.
     // @see Drupal\Core\DrupalKernel::initializeContainer()
-    $this->kernel = new DrupalKernel('testing', FALSE, NULL);
+    $this->kernel = new DrupalKernel('testing', FALSE, drupal_classloader());
     // Booting the kernel is necessary to initialize the new DIC. While
     // normally the kernel gets booted on demand in
     // Symfony\Component\HttpKernel\handle(), this kernel needs manual booting
diff --git a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
index 41070c32bb59a58ca85c16fe0daadaa4a2a66a89..f4dd2949d711519541ff01062d8c1881c7ab3104 100644
--- a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\system\Tests\DrupalKernel;
 
-use Drupal\Core\Cache\MemoryBackend;
 use Drupal\Core\DrupalKernel;
+use Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage;
+use Drupal\Component\PhpStorage\FileReadOnlyStorage;
 use Drupal\simpletest\UnitTestBase;
 use ReflectionClass;
 
@@ -34,21 +35,25 @@ function testCompileDIC() {
     // We need to be able to restore it to the correct one at the end of this
     // test.
     $original_container = drupal_container();
-    global $conf;
-    $conf['php_storage']['service_container'] = array(
-      'class' => 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage',
+    $classloader = drupal_classloader();
+    $configuration = array(
+      'bin' => 'service_container',
+      'directory' => DRUPAL_ROOT . '/' . variable_get('file_public_path', conf_path() . '/files') . '/php',
       'secret' => $GLOBALS['drupal_hash_salt'],
     );
-    $cache = new MemoryBackend('test');
+    // @todo: write a memory based storage backend for testing.
+    $php_storage = new MTimeProtectedFastFileStorage($configuration);
     $module_enabled = array(
       'system' => 'system',
       'user' => 'user',
     );
-    $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache);
+    $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage);
+    $kernel->updateModules($module_enabled);
     $kernel->boot();
     // Instantiate it a second time and we should get the compiled Container
     // class.
-    $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache);
+    $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage);
+    $kernel->updateModules($module_enabled);
     $kernel->boot();
     $container = $kernel->getContainer();
     $refClass = new ReflectionClass($container);
@@ -62,11 +67,9 @@ function testCompileDIC() {
 
     // Now use the read-only storage implementation, simulating a "production"
     // environment.
-    drupal_static_reset('drupal_php_storage');
-    $conf['php_storage']['service_container'] = array(
-      'class' => 'Drupal\Component\PhpStorage\FileReadOnlyStorage',
-    );
-    $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache);
+    $php_storage = new FileReadOnlyStorage($configuration);
+    $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage);
+    $kernel->updateModules($module_enabled);
     $kernel->boot();
     $container = $kernel->getContainer();
     $refClass = new ReflectionClass($container);
@@ -86,17 +89,14 @@ function testCompileDIC() {
 
     // Add another module so that we can test that the new module's bundle is
     // registered to the new container.
-    $module_enabled = array(
-      'system' => 'system',
-      'user' => 'user',
-      'bundle_test' => 'bundle_test',
-    );
-    $cache->flush();
-    $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache);
+    $module_enabled['bundle_test'] = 'bundle_test';
+    $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage);
+    $kernel->updateModules($module_enabled);
     $kernel->boot();
     // Instantiate it a second time and we should still get a ContainerBuilder
     // class because we are using the read-only PHP storage.
-    $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache);
+    $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage);
+    $kernel->updateModules($module_enabled);
     $kernel->boot();
     $container = $kernel->getContainer();
     $refClass = new ReflectionClass($container);
diff --git a/core/modules/system/tests/http.php b/core/modules/system/tests/http.php
index 297e3c749db3bbc7392f17a4b70231c109630f2c..8594648c7b2ffce071c6dc14b51906ca5899b960 100644
--- a/core/modules/system/tests/http.php
+++ b/core/modules/system/tests/http.php
@@ -36,6 +36,6 @@
 
 drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE);
 
-$kernel = new DrupalKernel('prod', FALSE);
+$kernel = new DrupalKernel('prod', FALSE, drupal_classloader());
 $response = $kernel->handle($request)->prepare($request)->send();
 $kernel->terminate($request, $response);
diff --git a/core/modules/system/tests/https.php b/core/modules/system/tests/https.php
index 8e09a5d4e21d90e5bac30134a104056ba11032dc..54d8878c92a490c873e7d94c95386b973308830b 100644
--- a/core/modules/system/tests/https.php
+++ b/core/modules/system/tests/https.php
@@ -35,6 +35,6 @@
 
 drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE);
 
-$kernel = new DrupalKernel('prod', FALSE);
+$kernel = new DrupalKernel('prod', FALSE, drupal_classloader());
 $response = $kernel->handle($request)->prepare($request)->send();
 $kernel->terminate($request, $response);
diff --git a/index.php b/index.php
index a6eb61e2a8049c5e4731435d381ef19df289f7b8..4d562900e7ce39e5f36b7c6817f1d4a3807c4898 100644
--- a/index.php
+++ b/index.php
@@ -28,7 +28,7 @@
 drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE);
 
 // @todo Figure out how best to handle the Kernel constructor parameters.
-$kernel = new DrupalKernel('prod', FALSE, NULL, cache('bootstrap'));
+$kernel = new DrupalKernel('prod', FALSE, drupal_classloader(), drupal_php_storage('service_container'));
 
 // Create a request object from the HTTPFoundation.
 $request = Request::createFromGlobals();