diff --git a/core/core.services.yml b/core/core.services.yml
index afccb31..50922a3 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -470,6 +470,9 @@ services:
       - { name: service_collector, tag: 'module_install.uninstall_validator', call: addUninstallValidator }
     arguments: ['@app.root', '@module_handler', '@kernel']
     lazy: true
+  module_listing:
+    class: Drupal\Core\Extension\ModuleExtensionList
+    arguments: ['@app.root', 'module', '@cache.default', '@info_parser', '@module_handler', '@config.factory']
   content_uninstall_validator:
     class: Drupal\Core\Entity\ContentUninstallValidator
     tags:
diff --git a/core/includes/module.inc b/core/includes/module.inc
index bc50d2c..027b236 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -58,6 +58,7 @@ function system_list($type) {
 function system_list_reset() {
   drupal_static_reset('system_list');
   drupal_static_reset('system_rebuild_module_data');
+  \Drupal::service('module_listing')->reset();
   \Drupal::cache('bootstrap')->delete('system_list');
 }
 
diff --git a/core/lib/Drupal/Core/Extension/ExtensionList.php b/core/lib/Drupal/Core/Extension/ExtensionList.php
new file mode 100644
index 0000000..6e78072
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ExtensionList.php
@@ -0,0 +1,301 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Extension\ExtensionList.
+ */
+
+namespace Drupal\Core\Extension;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Cache\CacheBackendInterface;
+
+/**
+ * Provides available extensions.
+ */
+abstract class ExtensionList {
+
+  /**
+   * The type of the extension, either 'module' or 'theme'.
+   *
+   * @var string
+   */
+  protected $type;
+
+  /**
+   * The app root.
+   *
+   * @var string
+   */
+  protected $root;
+
+  /**
+   * The cache.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cache;
+
+  /**
+   * Default information added to every extension.
+   *
+   * @var array
+   */
+  protected $defaults = [];
+
+  /**
+   * The info parser.
+   *
+   * @var \Drupal\Core\Extension\InfoParserInterface
+   */
+  protected $infoParser;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The static cached extensions.
+   *
+   * @var \Drupal\Core\Extension\Extension[]
+   */
+  protected $extensions;
+
+  /**
+   * Static caching for extension info.
+   *
+   * @var array
+   */
+  protected $extensionInfo;
+
+  /**
+   * A list of extension folder names keyed by extension name.
+   *
+   * @var string
+   */
+  protected $fileNames;
+
+  /**
+   * Constructs a new ExtensionList instance.
+   *
+   * @param string $root
+   *   The app root.
+   * @param string $type
+   *   The extension type.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   *   The cache.
+   * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
+   *   The info parser.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct($root, $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler) {
+    $this->root = $root;
+    $this->type = $type;
+    $this->cache = $cache;
+    $this->infoParser = $info_parser;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * Resets the stored extension list.
+   */
+  public function reset() {
+    $this->extensions = NULL;
+    $this->cache->delete($this->getCacheId());
+    return $this;
+  }
+
+  /**
+   * Returns the used cache ID.
+   *
+   * @return string
+   */
+  protected function getCacheId() {
+    return 'core.extension_listing.' . $this->type;
+  }
+
+  /**
+   * Returns the human readable name of the extension.
+   *
+   * @param string $name
+   *   The extension name.
+   *
+   * @return string
+   *   The human readable name of the extension.
+   */
+  public function getName($name) {
+    $extensions = $this->listExtensions();
+    if (isset($extensions[$name])) {
+      return $extensions[$name]->info['name'];
+    }
+    throw new \InvalidArgumentException(SafeMarkup::format('The @type %name does not exist.', ['@type' => $this->type, '%name' => $name]));
+  }
+
+  /**
+   * Returns a single extension.
+   *
+   * @param string $name
+   *   The extension name.
+   *
+   * @return \Drupal\Core\Extension\Extension
+   */
+  public function getExtension($name) {
+    $extensions = $this->listExtensions();
+    if (!isset($extensions[$name])) {
+      return $extensions[$name];
+    }
+
+    throw new \InvalidArgumentException(SafeMarkup::format('The @type %name does not exist.', ['@type' => $this->type, '%name' => $name]));
+  }
+
+  /**
+   * Returns all available extensions.
+   *
+   * @return \Drupal\Core\Extension\Extension[]
+   */
+  public function listExtensions() {
+    if (isset($this->extensions)) {
+      return $this->extensions;
+    }
+    if ($extensions = $this->cache->get($this->getCacheId())) {
+      $this->extensions = $extensions;
+      return $extensions;
+    }
+    $extensions = $this->doListExtensions();
+    $this->cache->get($this->getCacheId());
+
+    $this->recalculateInfo();
+    $this->recalculateFilenames();
+
+    $this->extensions = $extensions;
+    return $this->extensions;
+  }
+
+  /**
+   * Scans the available extensions.
+   *
+   * Overriding this method gives other code the chance to add additional
+   * extensions to this raw listing.
+   *
+   * @param \Drupal\Core\Extension\ExtensionDiscovery $discovery
+   *   The extension discovery.
+   *
+   * @return \Drupal\Core\Extension\Extension[]
+   */
+  protected function doScanExtensions(ExtensionDiscovery $discovery) {
+    return $discovery->scan($this->type);
+  }
+
+  /**
+   * Build the actual list of extensions before caching it.
+   *
+   * @return \Drupal\Core\Extension\Extension[]
+   */
+  protected function doListExtensions() {
+    $listing = new ExtensionDiscovery($this->root);
+
+    // Find extensions.
+    $extensions = $this->doScanExtensions($listing);
+
+    // Read info files for each extension.
+    foreach ($extensions as $key => $extension) {
+      // Look for the info file.
+      $extension->info = $this->infoParser->parse($extension->getPathname());
+
+      // Add the info file modification time, so it becomes available for
+      // contributed extensions to use for ordering extension lists.
+      $extension->info['mtime'] = $extension->getMTime();
+
+      // Merge in defaults and save.
+      $extensions[$key]->info = $extension->info + $this->defaults;
+
+      // Invoke hook_system_info_alter() to give installed modules a chance to
+      // modify the data in the .info.yml files if necessary.
+      // @todo Remove $type argument, obsolete with $module->getType().
+      $this->moduleHandler->alter('system_info', $extensions[$key]->info, $extensions[$key], $this->type);
+    }
+
+    return $extensions;
+  }
+
+  /**
+   * Returns an array of information about enabled modules or themes.
+   *
+   * This function returns the contents of the .info.yml file for each installed
+   * module or theme.
+   *
+   * @param string $name
+   *   (optional) The name of a module or theme whose information shall be
+   *   returned. If omitted, all records for the provided $type will be returned.
+   *   If $name does not exist in the provided $type or is not enabled, an empty
+   *   array will be returned.
+   *
+   * @return array
+   *   An associative array of module or theme information keyed by name, or only
+   *   information for $name, if given. If no records are available, an empty
+   *   array is returned.
+   */
+  public function getInfo($name = NULL) {
+    $this->extensionInfo;
+    if (!isset($this->extensionInfo)) {
+      if ($cache = $this->cache->get("system.{$this->type}.info")) {
+        $info = $cache->data;
+      }
+      else {
+        $info = $this->recalculateInfo();
+      }
+      $this->extensionInfo = $info;
+    }
+    if (isset($name)) {
+      return isset($this->extensionInfo[$name]) ? $this->extensionInfo[$name] : [];
+    }
+    return $this->extensionInfo;
+  }
+
+  protected function recalculateInfo() {
+    $info = [];
+    foreach ($this->listExtensions() as $extension_name => $filename) {
+      if (isset($data[$extension_name])) {
+        $info[$extension_name] = $data[$extension_name]->info;
+      }
+    }
+    // Store the module information in cache. This cache is cleared by
+    // calling system_rebuild_module_data(), for example, when listing
+    // modules, (un)installing modules, importing configuration, updating
+    // the site and when flushing all the caches.
+    $this->cache->set("system.{$this->type}.info", $info);
+    return $info;
+  }
+
+  /**
+   * Return a list of extension folder names keyed by extension name.
+   *
+   * @return string[]
+   */
+  public function getFilenames() {
+    if (!isset($this->fileNames)) {
+      $file_names = \Drupal::state()->get("system.{$this->type}.files");
+      if (!isset($files)) {
+        $file_names = $this->recalculateFilenames();
+      }
+      $this->fileNames = $file_names;
+    }
+    return $this->fileNames;
+  }
+
+  protected function recalculateFilenames() {
+    $file_names = [];
+    $extensions = $this->listExtensions();
+    ksort($extensions);
+    foreach ($extensions as $extension_name => $extension) {
+      $file_names[$extension_name] = $extension->getPathname();
+    }
+    \Drupal::state()->set("system.{$this->type}.files", $file_names);
+    return $file_names;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ModuleExtensionList.php b/core/lib/Drupal/Core/Extension/ModuleExtensionList.php
new file mode 100644
index 0000000..3561b28
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ModuleExtensionList.php
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Extension\ModuleExtensionList.
+ */
+
+namespace Drupal\Core\Extension;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Provides a list of available modules.
+ */
+class ModuleExtensionList extends ExtensionList {
+
+  use StringTranslationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaults = [
+    'dependencies' => [],
+    'description' => '',
+    'package' => 'Other',
+    'version' => NULL,
+    'php' => DRUPAL_MINIMUM_PHP,
+  ];
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * Constructs a new ModuleExtensionList instance.
+   *
+   * @param string $root
+   *   The app root.
+   * @param string $type
+   *   The extension type.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   *   The cache.
+   * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
+   *   The info parser.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   */
+  public function __construct($root, $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory) {
+    parent::__construct($root, $type, $cache, $info_parser, $module_handler);
+
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doScanExtensions(ExtensionDiscovery $discovery) {
+    $extensions = parent::doScanExtensions($discovery);
+
+    // Find installation profiles.
+    $profiles = $discovery->scan('profile');
+
+    // Include the installation profile in modules that are loaded.
+    if ($profile = drupal_get_profile()) {
+      $extensions[$profile] = $profiles[$profile];
+      // Installation profile hooks are always executed last.
+      $extensions[$profile]->weight = 1000;
+    }
+
+    return $extensions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doListExtensions() {
+    $listing = new ExtensionDiscovery($this->root);
+
+    // Find installation profiles. This needs to happen before performing a
+    // module scan as the module scan requires knowing what the active profile is.
+    // @todo Remove as part of https://www.drupal.org/node/2186491
+    $profiles = $listing->scan('profile');
+    $profile = drupal_get_profile();
+    if ($profile && isset($profiles[$profile])) {
+      // Prime the drupal_get_filename() static cache with the profile info file
+      // location so we can use drupal_get_path() on the active profile during
+      // the module scan.
+      // @todo Remove as part of https://www.drupal.org/node/2186491
+      drupal_get_filename('profile', $profile, $profiles[$profile]->getPathname());
+    }
+
+    // Find modules.
+    $extensions = parent::doListExtensions();
+    // It is possible that a module was marked as required by
+    // hook_system_info_alter() and modules that it depends on are not required.
+    foreach ($extensions as $extension) {
+      $this->moduleDataEnsureRequired($extension, $extensions);
+    }
+
+    if ($profile) {
+      // Installation profiles are hidden by default, unless explicitly
+      // specified otherwise in the .info.yml file.
+      if (!isset($extensions[$profile]->info['hidden'])) {
+        $extensions[$profile]->info['hidden'] = TRUE;
+      }
+
+      if (isset($extensions[$profile])) {
+        // The installation profile is required, if it's a valid module.
+        $extensions[$profile]->info['required'] = TRUE;
+        // Add a default distribution name if the profile did not provide one.
+        // @see install_profile_info()
+        // @see drupal_install_profile_distribution_name()
+        if (!isset($extensions[$profile]->info['distribution']['name'])) {
+          $extensions[$profile]->info['distribution']['name'] = 'Drupal';
+        }
+      }
+    }
+
+    // Add status, weight, and schema version.
+    $installed_modules = $this->configFactory->get('core.extension')->get('module') ?: [];
+    foreach ($extensions as $name => $module) {
+      $module->weight = isset($installed_modules[$name]) ? $installed_modules[$name] : 0;
+      $module->status = (int) isset($installed_modules[$name]);
+      $module->schema_version = SCHEMA_UNINSTALLED;
+      $files[$name] = $module->getPathname();
+    }
+    $extensions = $this->moduleHandler->buildModuleDependencies($extensions);
+
+    return $extensions;
+  }
+
+  /**
+   * Ensures that dependencies of required modules are also required.
+   *
+   * @param \Drupal\Core\Extension\Extension $module
+   *   The module info.
+   * @param \Drupal\Core\Extension\Extension[] $modules
+   *   The array of all module info.
+   */
+  protected function moduleDataEnsureRequired(Extension $module, array $modules = []) {
+    if (!empty($module->info['required'])) {
+      foreach ($module->info['dependencies'] as $dependency) {
+        $dependency_name = ModuleHandler::parseDependency($dependency)['name'];
+        if (!isset($modules[$dependency_name]->info['required'])) {
+          $modules[$dependency_name]->info['required'] = TRUE;
+          $modules[$dependency_name]->info['explanation'] = $this->t('Dependency of required module @module', array('@module' => $module->info['name']));
+          // Ensure any dependencies it has are required.
+         $this->moduleDataEnsureRequired($modules[$dependency_name], $modules);
+        }
+      }
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index b9a9a3e..b3e0bda 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -708,8 +708,7 @@ public function getModuleDirectories() {
    * {@inheritdoc}
    */
   public function getName($module) {
-    $info = system_get_info('module', $module);
-    return isset($info['name']) ? $info['name'] : $module;
+    return \Drupal::service('module_listing')->getName($module);
   }
 
 }
diff --git a/core/modules/book/src/Tests/BookUninstallTest.php b/core/modules/book/src/Tests/BookUninstallTest.php
index 0a18fb1..b46901e 100644
--- a/core/modules/book/src/Tests/BookUninstallTest.php
+++ b/core/modules/book/src/Tests/BookUninstallTest.php
@@ -82,6 +82,10 @@ public function testBookUninstall() {
     $book_node->delete();
     // No nodes exist therefore the book module is not required.
     $module_data = _system_rebuild_module_data();
+    /** @var \Drupal\Core\Extension\ModuleExtensionList $module_listing */
+    $module_listing = \Drupal::service('module_listing');
+    $module_listing->reset();
+    $module_data = $module_listing->listExtensions();
     $this->assertFalse(isset($module_data['book']->info['required']), 'The book module is not required.');
 
     $node = Node::create(array('title' => $this->randomString(), 'type' => $content_type->id()));
diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php
index 5fd9999..f56c59e 100644
--- a/core/modules/filter/src/Tests/FilterAPITest.php
+++ b/core/modules/filter/src/Tests/FilterAPITest.php
@@ -489,7 +489,8 @@ public function testDependencyRemoval() {
 
     drupal_static_reset('filter_formats');
     \Drupal::entityManager()->getStorage('filter_format')->resetCache();
-    $module_data = _system_rebuild_module_data();
+    \Drupal::service('module_listing')->reset();
+    $module_data = \Drupal::service('module_listing')->listExtensions();
     $this->assertFalse(isset($module_data['filter_test']->info['required']), 'The filter_test module is required.');
 
     // Verify that a dependency exists on the module that provides the filter
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index d3b55f9..72767a5 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -881,39 +881,15 @@ function system_check_directory($form_element, FormStateInterface $form_state) {
  */
 function system_get_info($type, $name = NULL) {
   if ($type == 'module') {
-    $info = &drupal_static(__FUNCTION__);
-    if (!isset($info)) {
-      if ($cache = \Drupal::cache()->get('system.module.info')) {
-        $info = $cache->data;
-      }
-      else {
-        $data = system_rebuild_module_data();
-        foreach (\Drupal::moduleHandler()->getModuleList() as $module => $filename) {
-          if (isset($data[$module])) {
-            $info[$module] = $data[$module]->info;
-          }
-        }
-        // Store the module information in cache. This cache is cleared by
-        // calling system_rebuild_module_data(), for example, when listing
-        // modules, (un)installing modules, importing configuration, updating
-        // the site and when flushing all the caches.
-        \Drupal::cache()->set('system.module.info', $info);
-      }
-    }
+    /** @var \Drupal\Core\Extension\ModuleExtensionList $module_listing */
+    $module_listing = \Drupal::service('module_listing');
+    return $module_listing->getInfo($name);
   }
   else {
-    $info = array();
-    $list = system_list($type);
-    foreach ($list as $shortname => $item) {
-      if (!empty($item->status)) {
-        $info[$shortname] = $item->info;
-      }
-    }
+    /** @var \Drupal\Core\Extension\ModuleExtensionList $module_listing */
+    $module_listing = \Drupal::service('theme_listing');
+    return $module_listing->getInfo($name);
   }
-  if (isset($name)) {
-    return isset($info[$name]) ? $info[$name] : array();
-  }
-  return $info;
 }
 
 /**
@@ -923,105 +899,8 @@ function system_get_info($type, $name = NULL) {
  *   An associative array of module information.
  */
 function _system_rebuild_module_data() {
-  $listing = new ExtensionDiscovery(\Drupal::root());
-
-  // Find installation profiles. This needs to happen before performing a
-  // module scan as the module scan requires knowing what the active profile is.
-  // @todo Remove as part of https://www.drupal.org/node/2186491.
-  $profiles = $listing->scan('profile');
-  $profile = drupal_get_profile();
-  if ($profile && isset($profiles[$profile])) {
-    // Prime the drupal_get_filename() static cache with the profile info file
-    // location so we can use drupal_get_path() on the active profile during
-    // the module scan.
-    // @todo Remove as part of https://www.drupal.org/node/2186491.
-    drupal_get_filename('profile', $profile, $profiles[$profile]->getPathname());
-  }
-
-  // Find modules.
-  $modules = $listing->scan('module');
-  // Include the installation profile in modules that are loaded.
-  if ($profile) {
-    $modules[$profile] = $profiles[$profile];
-    // Installation profile hooks are always executed last.
-    $modules[$profile]->weight = 1000;
-  }
-
-  // Set defaults for module info.
-  $defaults = array(
-    'dependencies' => array(),
-    'description' => '',
-    'package' => 'Other',
-    'version' => NULL,
-    'php' => DRUPAL_MINIMUM_PHP,
-  );
-
-  // Read info files for each module.
-  foreach ($modules as $key => $module) {
-    // Look for the info file.
-    $module->info = \Drupal::service('info_parser')->parse($module->getPathname());
-
-    // Add the info file modification time, so it becomes available for
-    // contributed modules to use for ordering module lists.
-    $module->info['mtime'] = $module->getMTime();
-
-    // Merge in defaults and save.
-    $modules[$key]->info = $module->info + $defaults;
-
-    // Installation profiles are hidden by default, unless explicitly specified
-    // otherwise in the .info.yml file.
-    if ($key == $profile && !isset($modules[$key]->info['hidden'])) {
-      $modules[$key]->info['hidden'] = TRUE;
-    }
-
-    // Invoke hook_system_info_alter() to give installed modules a chance to
-    // modify the data in the .info.yml files if necessary.
-    // @todo Remove $type argument, obsolete with $module->getType().
-    $type = 'module';
-    \Drupal::moduleHandler()->alter('system_info', $modules[$key]->info, $modules[$key], $type);
-  }
-
-  // It is possible that a module was marked as required by
-  // hook_system_info_alter() and modules that it depends on are not required.
-  foreach ($modules as $module) {
-    _system_rebuild_module_data_ensure_required($module, $modules);
-  }
-
-
-  if ($profile && isset($modules[$profile])) {
-    // The installation profile is required, if it's a valid module.
-    $modules[$profile]->info['required'] = TRUE;
-    // Add a default distribution name if the profile did not provide one.
-    // @see install_profile_info()
-    // @see drupal_install_profile_distribution_name()
-    if (!isset($modules[$profile]->info['distribution']['name'])) {
-      $modules[$profile]->info['distribution']['name'] = 'Drupal';
-    }
-  }
-
-  return $modules;
-}
-
-/**
- * Ensures that dependencies of required modules are also required.
- *
- * @param \Drupal\Core\Extension\Extension $module
- *   The module info.
- * @param \Drupal\Core\Extension\Extension[] $modules
- *   The array of all module info.
- */
-function _system_rebuild_module_data_ensure_required($module, &$modules) {
-  if (!empty($module->info['required'])) {
-    foreach ($module->info['dependencies'] as $dependency) {
-      $dependency_name = ModuleHandler::parseDependency($dependency)['name'];
-      if (!isset($modules[$dependency_name]->info['required'])) {
-        $modules[$dependency_name]->info['required'] = TRUE;
-        $modules[$dependency_name]->info['explanation'] = t('Dependency of required module @module', array('@module' => $module->info['name']));
-        // Ensure any dependencies it has are required.
-        _system_rebuild_module_data_ensure_required($modules[$dependency_name], $modules);
-      }
-    }
-  }
+  \Drupal::service('module_listing')->reset();
+  return \Drupal::service('module_listing')->listExtensions();
 }
 
 /**
@@ -1031,32 +910,8 @@ function _system_rebuild_module_data_ensure_required($module, &$modules) {
  *   Array of all available modules and their data.
  */
 function system_rebuild_module_data() {
-  $modules_cache = &drupal_static(__FUNCTION__);
-  // Only rebuild once per request. $modules and $modules_cache cannot be
-  // combined into one variable, because the $modules_cache variable is reset by
-  // reference from system_list_reset() during the rebuild.
-  if (!isset($modules_cache)) {
-    $modules = _system_rebuild_module_data();
-    $files = array();
-    ksort($modules);
-    // Add status, weight, and schema version.
-    $installed_modules = \Drupal::config('core.extension')->get('module') ?: array();
-    foreach ($modules as $name => $module) {
-      $module->weight = isset($installed_modules[$name]) ? $installed_modules[$name] : 0;
-      $module->status = (int) isset($installed_modules[$name]);
-      $module->schema_version = SCHEMA_UNINSTALLED;
-      $files[$name] = $module->getPathname();
-    }
-    $modules = \Drupal::moduleHandler()->buildModuleDependencies($modules);
-    $modules_cache = $modules;
-
-    // Store filenames to allow drupal_get_filename() to retrieve them without
-    // having to rebuild or scan the filesystem.
-    \Drupal::state()->set('system.module.files', $files);
-    // Clear the module info cache.
-    \Drupal::cache()->delete('system.module.info');
-    drupal_static_reset('system_get_info');
-  }
+  \Drupal::service('module_listing')->reset();
+  $modules_cache = \Drupal::service('module_listing')->listExtensions();
   return $modules_cache;
 }
 
diff --git a/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php b/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
index 5265b8e..1dd99cd 100644
--- a/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
+++ b/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
@@ -120,6 +120,7 @@ function testDependencyResolution() {
     // Nothing should be installed.
     \Drupal::state()->set('module_test.dependency', 'missing dependency');
     drupal_static_reset('system_rebuild_module_data');
+    \Drupal::service('module_listing')->reset();
 
     try {
       $result = $this->moduleInstaller()->install(array('color'));
@@ -135,6 +136,7 @@ function testDependencyResolution() {
     // Color module depends on Config. Config depends on Help module.
     \Drupal::state()->set('module_test.dependency', 'dependency');
     drupal_static_reset('system_rebuild_module_data');
+    \Drupal::service('module_listing')->reset();
 
     $result = $this->moduleInstaller()->install(array('color'));
     $this->assertTrue($result, 'ModuleHandler::install() returns the correct value.');
@@ -167,6 +169,7 @@ function testDependencyResolution() {
     // sure that Drupal\Core\Extension\ModuleHandler::install() still works.
     \Drupal::state()->set('module_test.dependency', 'version dependency');
     drupal_static_reset('system_rebuild_module_data');
+    \Drupal::service('module_listing')->reset();
 
     $result = $this->moduleInstaller()->install(array('color'));
     $this->assertTrue($result, 'ModuleHandler::install() returns the correct value.');
