diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index eac2f97..36047a0 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -891,22 +891,17 @@ function drupal_get_filename($type, $name, $filename = NULL) {
     // 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()->has('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.
+    try {
+      $lists = system_list_load(FALSE);
+      if (isset($lists['filepaths'][$type][$name])) {
+        $files[$type][$name] = $lists['filepaths'][$type][$name];
       }
     }
+    catch (Exception $e) {
+      // The php storage 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])) {
diff --git a/core/includes/module.inc b/core/includes/module.inc
index 2db6e99..33e2b64 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -150,137 +150,166 @@ function system_list($type) {
     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);
-    }
+    $lists = system_list_load();
+    system_list_register('module', $lists['bootstrap']);
     // We only return the module names here since module_list() doesn't need
     // the filename itself.
-    $lists['bootstrap'] = array_keys($bootstrap_list);
+    $lists['bootstrap'] = array_keys($lists['bootstrap']);
   }
   // Otherwise build the list for enabled modules and themes.
-  elseif (!isset($lists['module_enabled'])) {
-    if ($cached = cache('bootstrap')->get('system_list')) {
-      $lists = $cached->data;
-    }
-    else {
-      $lists = array(
-        'module_enabled' => array(),
-        'theme' => array(),
-        'filepaths' => array(),
-      );
-      // The module name (rather than the filename) is used as the fallback
-      // weighting in order to guarantee consistent behavior across different
-      // Drupal installations, which might have modules installed in different
-      // locations in the file system. The ordering here must also be
-      // consistent with the one used in module_implements().
-      $enabled_modules = (array) config('system.module')->get('enabled');
-      $module_files = state()->get('system.module.files');
-      foreach ($enabled_modules as $name => $weight) {
-        // Build a list of all enabled modules.
-        $lists['module_enabled'][$name] = $name;
-        // Build a list of filenames so drupal_get_filename can use it.
-        $lists['filepaths'][] = array(
-          'type' => 'module',
-          'name' => $name,
-          'filepath' => $module_files[$name],
-        );
-      }
+  elseif (!isset($lists['items_registered'])) {
+    $lists = system_list_load();
+    // To avoid a separate database lookup for the filepath, prime the
+    // drupal_get_filename() static cache with all enabled modules and themes.
+    system_list_register('module', $lists['filepaths']['module']);
+    system_list_register('theme', $lists['filepaths']['theme']);
+    $lists['items_registered'] = TRUE;
+  }
 
-      // Build a list of themes.
-      $enabled_themes = (array) config('system.theme')->get('enabled');
-      // @todo Themes include all themes, including disabled/uninstalled. This
-      //   system.theme.data state will go away entirely as soon as themes have
-      //   a proper installation status.
-      // @see http://drupal.org/node/1067408
-      $theme_data = state()->get('system.theme.data');
-      if (empty($theme_data)) {
-        // @todo: system_list() may be called from _drupal_bootstrap_code() and
-        // module_load_all(), in which case system.module is not loaded yet.
-        // Prevent a filesystem scan in drupal_load() and include it directly.
-        // @see http://drupal.org/node/1067408
-        require_once DRUPAL_ROOT . '/core/modules/system/system.module';
-        $theme_data = system_rebuild_theme_data();
-      }
-      foreach ($theme_data as $name => $theme) {
-        $theme->status = (int) isset($enabled_themes[$name]);
-        $lists['theme'][$name] = $theme;
-        // Build a list of filenames so drupal_get_filename can use it.
-        if (isset($enabled_themes[$name])) {
-          $lists['filepaths'][] = array(
-            'type' => 'theme',
-            'name' => $name,
-            'filepath' => $theme->filename,
-          );
-        }
+  return $lists[$type];
+}
+
+/**
+ * Prime the drupal_get_filename() static cache and the classloader.
+ */
+function system_list_register($type, $list) {
+  foreach ($list as $name => $filename) {
+    drupal_classloader_register($name, dirname($filename));
+    drupal_get_filename($type, $name, $filename);
+  }
+}
+
+/**
+ * Load the system load from disk and rebuild if necessary.
+ */
+function system_list_load($rebuild_allowed = TRUE) {
+  $lists = array();
+  if (function_exists('system_list_get')) {
+    $lists = system_list_get();
+  }
+  else {
+    drupal_php_storage('bootstrap')->load('system_list');
+    if (function_exists('system_list_get')) {
+      $lists = system_list_get();
+    }
+  }
+  if (empty($lists) && $rebuild_allowed) {
+    // We do not call system_rebuild_module_data() because it calls
+    // hook_system_info_alter() and we are not interested in module info.
+    require_once DRUPAL_ROOT . '/core/modules/system/system.module';
+    $modules = system_list_find_modules();
+
+    $enabled_modules = (array) config('system.module')->get('enabled');
+    $lists['module_enabled'] = array();
+    $lists['filepaths']['module'] = array();
+    foreach ($enabled_modules as $name => $weight) {
+      // Build a list of all enabled modules.
+      $lists['module_enabled'][$name] = $name;
+      $lists['filepaths']['module'][$name] = $modules[$name]->uri;
+    }
+    // We need to load enabled modules because system_list_find_themes wants
+    // to find themes defined by modules. Prime drupal_get_filename() to make
+    // drupal_load() work.
+    system_list_register('module', $lists['filepaths']['module']);
+    foreach ($lists['module_enabled'] as $module) {
+      drupal_load('module', $module);
+    }
+    $lists['theme'] = array();
+    $lists['filepaths']['theme'] = array();
+    $theme_data = system_rebuild_theme_data($enabled_modules);
+    foreach ($theme_data as $name => $theme) {
+      $lists['theme'][$name] = $theme;
+      // Build a list of filenames so drupal_get_filename can use it.
+      if ($theme->status) {
+        $lists['filepaths']['theme'][$name] = $theme->uri;
       }
-      // @todo Move into list_themes(). Read info for a particular requested
-      //   theme from state instead.
-      foreach ($lists['theme'] as $key => $theme) {
-        if (!empty($theme->info['base theme'])) {
-          // Make a list of the theme's base themes.
-          require_once DRUPAL_ROOT . '/core/includes/theme.inc';
-          $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key);
-          // Don't proceed if there was a problem with the root base theme.
-          if (!current($lists['theme'][$key]->base_themes)) {
-            continue;
-          }
-          // Determine the root base theme.
-          $base_key = key($lists['theme'][$key]->base_themes);
-          // Add to the list of sub-themes for each of the theme's base themes.
-          foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) {
-            $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name'];
-          }
-          // Add the base theme's theme engine info.
-          $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_key]->info['engine'];
+    }
+    // @todo Move into list_themes(). Read info for a particular requested
+    //   theme from state instead.
+    foreach ($lists['theme'] as $key => $theme) {
+      if (!empty($theme->info['base theme'])) {
+        // Make a list of the theme's base themes.
+        require_once DRUPAL_ROOT . '/core/includes/theme.inc';
+        $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key);
+        // Don't proceed if there was a problem with the root base theme.
+        if (!current($lists['theme'][$key]->base_themes)) {
+          continue;
         }
-        else {
-          // A plain theme is its own base theme.
-          $base_key = $key;
+        // Determine the root base theme.
+        $base_key = key($lists['theme'][$key]->base_themes);
+        // Add to the list of sub-themes for each of the theme's base themes.
+        foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) {
+          $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name'];
         }
-        // Set the theme engine prefix.
-        $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];
+        // Add the base theme's theme engine info.
+        $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_key]->info['engine'];
       }
-      cache('bootstrap')->set('system_list', $lists);
-    }
-    // To avoid a separate database lookup for the filepath, prime the
-    // drupal_get_filename() static cache with all enabled modules and themes.
-    foreach ($lists['filepaths'] as $item) {
-      drupal_get_filename($item['type'], $item['name'], $item['filepath']);
-      drupal_classloader_register($item['name'], dirname($item['filepath']));
+      else {
+        // A plain theme is its own base theme.
+        $base_key = $key;
+      }
+      // Set the theme engine prefix.
+      $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];
     }
+    // Fill in $lists['bootstrap'] and save the list.
+    _system_update_bootstrap_status($lists);
   }
 
-  return $lists[$type];
+  return $lists;
 }
 
 /**
+ * Save the system lists into a PHP file.
+ *
+ * @param $lists
+ *   The lists from system_list().
+ */
+function _system_list_save($lists) {
+  include_once DRUPAL_ROOT . '/core/includes/utility.inc';
+  $code = "<?php\n";
+  $code .= "function system_list_get() {\n";
+  $code .= '$lists = &drupal_static(__FUNCTION__, ' . drupal_var_export($lists) . ");\n";
+  $code .= 'return $lists;' . "\n}\n";
+  drupal_php_storage('bootstrap')->save('system_list', $code);
+  if (function_exists('system_list_get')) {
+    $stored_lists = &drupal_static('system_list_get');
+    $stored_lists = $lists;
+  }
+  else {
+    drupal_php_storage('bootstrap')->load('system_list');
+  }
+}
+
+/**
+ * Find modules on the filesystem.
+ *
+ * @return array
+ */
+function system_list_find_modules() {
+  // Find modules
+  $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0);
+
+  // Find installation profiles.
+  $profiles = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles', 'name', 0);
+
+  // Include the installation profile in modules that are loaded.
+  $profile = drupal_get_profile();
+  $modules[$profile] = $profiles[$profile];
+  return $modules;
+}
+
+
+/**
  * 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');
+  drupal_php_storage('bootstrap')->delete('system_list');
+  $lists = &drupal_static('system_list_get');
+  $lists = array();
 }
 
 /**
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 4475104..fe4295c 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2774,16 +2774,8 @@ function system_get_module_info($property) {
  *   An associative array of module information.
  */
 function _system_rebuild_module_data() {
-  // Find modules
-  $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0);
-
-  // Find installation profiles.
-  $profiles = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles', 'name', 0);
-
-  // Include the installation profile in modules that are loaded.
+  $modules = system_list_find_modules();
   $profile = drupal_get_profile();
-  $modules[$profile] = $profiles[$profile];
-
   // Installation profile hooks are always executed last.
   $modules[$profile]->weight = 1000;
 
@@ -2882,10 +2874,6 @@ function system_rebuild_module_data() {
     }
     $modules = _module_build_dependencies($modules);
     $modules_cache = $modules;
-
-    // Store filenames to allow system_list() and drupal_get_filename() to
-    // retrieve them without having to rebuild or scan the filesystem.
-    state()->set('system.module.files', $files);
   }
   return $modules_cache;
 }
@@ -2897,14 +2885,27 @@ function system_rebuild_module_data() {
  * implement hooks used during bootstrap, such as hook_boot(). These modules
  * are loaded earlier to invoke the hooks.
  */
-function _system_update_bootstrap_status() {
-  $bootstrap_modules = array();
+function _system_update_bootstrap_status(&$lists = NULL) {
+  if (isset($lists)) {
+    $modules = $lists['module_enabled'];
+    $module_system_available = FALSE;
+  }
+  else {
+    $lists = system_list_load();
+    $module_system_available = TRUE;
+  }
+  $lists['bootstrap'] = array();
   foreach (bootstrap_hooks() as $hook) {
-    foreach (module_implements($hook) as $module) {
-      $bootstrap_modules[$module] = drupal_get_filename('module', $module);
+    if ($module_system_available) {
+      $modules = module_implements($hook);
+    }
+    foreach ($modules as $module) {
+      if ($module_system_available || function_exists($module . '_' . $hook)) {
+        $lists['bootstrap'][$module] = $lists['filepaths']['module'][$module];
+      }
     }
   }
-  state()->set('system.module.bootstrap', $bootstrap_modules);
+  _system_list_save($lists);
 }
 
 /**
@@ -2913,21 +2914,28 @@ function _system_update_bootstrap_status() {
  * @return
  *   An associative array of themes information.
  */
-function _system_rebuild_theme_data() {
+function _system_rebuild_theme_data($modules = NULL) {
   // Find themes
   $themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'themes');
   // Allow modules to add further themes.
-  if ($module_themes = module_invoke_all('system_theme_info')) {
-    foreach ($module_themes as $name => $uri) {
-      // @see file_scan_directory()
-      $themes[$name] = (object) array(
-        'uri' => $uri,
-        'filename' => pathinfo($uri, PATHINFO_FILENAME),
-        'name' => $name,
-      );
+  $needs_check = TRUE;
+  if (!isset($modules)) {
+    $needs_check = FALSE;
+    $modules = module_invoke_all('system_theme_info');
+  }
+  foreach ($modules as $module) {
+    $function = $module . '_system_theme_info';
+    if (function_exists($function)) {
+      foreach ($function() as $name => $uri) {
+        // @see file_scan_directory()
+        $themes[$name] = (object) array(
+          'uri' => $uri,
+          'filename' => pathinfo($uri, PATHINFO_FILENAME),
+          'name' => $name,
+        );
+      }
     }
   }
-
   // Find theme engines
   $engines = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines');
 
@@ -2966,7 +2974,12 @@ function _system_rebuild_theme_data() {
     // Invoke hook_system_info_alter() to give installed modules a chance to
     // modify the data in the .info files if necessary.
     $type = 'theme';
-    drupal_alter('system_info', $themes[$key]->info, $themes[$key], $type);
+    foreach ($modules as $module) {
+      $function = $module . '_system_info_alter';
+      if (!$needs_check || function_exists($function)) {
+        $function($themes[$key]->info, $themes[$key], $type);
+      }
+    }
 
     if (!empty($themes[$key]->info['base theme'])) {
       $sub_themes[] = $key;
@@ -3033,8 +3046,8 @@ function _system_rebuild_theme_data() {
  * @return
  *   Array of all available themes and their data.
  */
-function system_rebuild_theme_data() {
-  $themes = _system_rebuild_theme_data();
+function system_rebuild_theme_data($modules = NULL) {
+  $themes = _system_rebuild_theme_data($modules);
   ksort($themes);
   // @todo This function has no business in determining/setting the status of
   //   a theme, but various other functions expect it to return themes with a
@@ -3044,19 +3057,9 @@ function system_rebuild_theme_data() {
   //   installation status.
   // @see http://drupal.org/node/1067408
   $enabled_themes = (array) config('system.theme')->get('enabled');
-  $files = array();
   foreach ($themes as $name => $theme) {
     $theme->status = (int) isset($enabled_themes[$name]);
-    $files[$name] = $theme->filename;
   }
-  // Replace last known theme data state.
-  // @todo Obsolete with proper installation status for themes.
-  state()->set('system.theme.data', $themes);
-
-  // Store filenames to allow system_list() and drupal_get_filename() to
-  // retrieve them without having to rebuild or scan the filesystem.
-  state()->set('system.theme.files', $files);
-
   return $themes;
 }
 
