diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php
index 75d9014..41f1c30 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -89,7 +89,7 @@ public function getFormId() {
   public function buildForm(array $form, FormStateInterface $form_state) {
     // During the initial form build, add this form object to the form state and
     // allow for initial preparation before form building and processing.
-    if (!isset($form_state['controller'])) {
+    if (!isset($form_state['entity_form_initialized'])) {
       $this->init($form_state);
     }
 
@@ -115,9 +115,8 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    * Initialize the form state and the entity before the first form build.
    */
   protected function init(FormStateInterface $form_state) {
-    // Add the form object to the form state so it can be easily accessed by
-    // module-provided form handlers there.
-    $form_state['controller'] = $this;
+    // Flag that this form has been initialized.
+    $form_state['entity_form_initialized'] = TRUE;
 
     // Prepare the entity to be presented in the entity form.
     $this->prepareEntity();
@@ -158,7 +157,7 @@ public function form(array $form, FormStateInterface $form_state) {
   public function processForm($element, FormStateInterface $form_state, $form) {
     // If the form is cached, process callbacks may not have a valid reference
     // to the entity object, hence we must restore it.
-    $this->entity = $form_state['controller']->getEntity();
+    $this->entity = $form_state->getFormObject()->getEntity();
 
     return $element;
   }
diff --git a/core/lib/Drupal/Core/Form/FormState.php b/core/lib/Drupal/Core/Form/FormState.php
index 50b75f7..d4c29e2 100644
--- a/core/lib/Drupal/Core/Form/FormState.php
+++ b/core/lib/Drupal/Core/Form/FormState.php
@@ -326,7 +326,7 @@ class FormState implements FormStateInterface, \ArrayAccess {
    *  conflict with ones used by the Form API or other modules is to use the
    *  module name as the key name or a prefix for the key name. For example, the
    *  entity form classes use $this->entity in entity forms, or
-   *  $form_state['controller']->getEntity() outside the controller, to store
+   *  $form_state->getFormObject()->getEntity() outside the controller, to store
    *  information about the entity being edited, and this information stays
    *  available across successive clicks of the "Preview" button (if available)
    *  as well as when the "Save" button is finally clicked.
@@ -808,6 +808,14 @@ public function prepareCallback($callback) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getFormObject() {
+    $build_info = $this->get('build_info');
+    return $build_info['callback_object'];
+  }
+
+  /**
    * Wraps drupal_set_message().
    *
    * @return array|null
diff --git a/core/lib/Drupal/Core/Form/FormStateInterface.php b/core/lib/Drupal/Core/Form/FormStateInterface.php
index 164078a..7f0e460 100644
--- a/core/lib/Drupal/Core/Form/FormStateInterface.php
+++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Form;
 
+use Drupal\Core\Annotation\Action;
 use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\Response;
 
@@ -470,4 +471,12 @@ public function setRebuild($rebuild = TRUE);
    */
   public function prepareCallback($callback);
 
+  /**
+   * Returns the form object that is responsible for building this form.
+   *
+   * @return \Drupal\Core\Form\FormInterface
+   *   The form object.
+   */
+  public function getFormObject();
+
 }
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index ab0a5fe..5e87f43 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -161,7 +161,7 @@ function book_node_links_alter(array &$node_links, NodeInterface $node, array &$
  * @see book_pick_book_nojs_submit()
  */
 function book_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
-  $node = $form_state['controller']->getEntity();
+  $node = $form_state->getFormObject()->getEntity();
   $account = \Drupal::currentUser();
   $access = $account->hasPermission('administer book outlines');
   if (!$access) {
@@ -217,7 +217,7 @@ function book_node_builder($entity_type, NodeInterface $entity, &$form, FormStat
  * @see book_form_node_form_alter()
  */
 function book_pick_book_nojs_submit($form, FormStateInterface $form_state) {
-  $node = $form_state['controller']->getEntity();
+  $node = $form_state->getFormObject()->getEntity();
   $node->book = $form_state->getValue('book');
   $form_state['rebuild'] = TRUE;
 }
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 129d42a..368ad0f 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -170,7 +170,7 @@ function contact_form_user_form_alter(&$form, FormStateInterface $form_state) {
     '#open' => TRUE,
     '#weight' => 5,
   );
-  $account = $form_state['controller']->getEntity();
+  $account = $form_state->getFormObject()->getEntity();
   if (!\Drupal::currentUser()->isAnonymous() && $account->id()) {
     $account_data = \Drupal::service('user.data')->get('contact', $account->id(), 'enabled');
   }
@@ -187,7 +187,7 @@ function contact_form_user_form_alter(&$form, FormStateInterface $form_state) {
  * Submit callback for the user profile form to save the contact page setting.
  */
 function contact_user_profile_form_submit($form, FormStateInterface $form_state) {
-  $account = $form_state['controller']->getEntity();
+  $account = $form_state->getFormObject()->getEntity();
   if ($account->id() && $form_state->hasValue('contact')) {
     \Drupal::service('user.data')->set('contact', $account->id(), 'enabled', (int) $form_state->getValue('contact'));
   }
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index d218a8d..8c9d5ce 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -347,7 +347,7 @@ function content_translation_controller($entity_type_id) {
  * @todo Move to \Drupal\content_translation\ContentTranslationManager.
  */
 function content_translation_form_controller(FormStateInterface $form_state) {
-  return isset($form_state['controller']) && $form_state['controller'] instanceof EntityFormInterface ? $form_state['controller'] : FALSE;
+  return $form_state->getFormObject() instanceof EntityFormInterface ? $form_state->getFormObject() : FALSE;
 }
 
 /**
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index ce3b9fa..5c46736 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -94,7 +94,7 @@ function editor_form_filter_admin_overview_alter(&$form, FormStateInterface $for
  */
 function editor_form_filter_format_form_alter(&$form, FormStateInterface $form_state) {
   if (!isset($form_state['editor'])) {
-    $format_id = $form_state['controller']->getEntity()->id();
+    $format_id = $form_state->getFormObject()->getEntity()->id();
     $form_state['editor'] = editor_load($format_id);
   }
   $editor = $form_state['editor'];
@@ -177,7 +177,7 @@ function editor_form_filter_admin_format_editor_configure($form, FormStateInterf
     }
     elseif (empty($editor) || $editor_value !== $editor->getEditor()) {
       $editor = entity_create('editor', array(
-        'format' => $form_state['controller']->getEntity()->id(),
+        'format' => $form_state->getFormObject()->getEntity()->id(),
         'editor' => $editor_value,
       ));
       $form_state['editor'] = $editor;
@@ -216,7 +216,7 @@ function editor_form_filter_admin_format_validate($form, FormStateInterface $for
  */
 function editor_form_filter_admin_format_submit($form, FormStateInterface $form_state) {
   // Delete the existing editor if disabling or switching between editors.
-  $format_id = $form_state['controller']->getEntity()->id();
+  $format_id = $form_state->getFormObject()->getEntity()->id();
   $original_editor = editor_load($format_id);
   if ($original_editor && $original_editor->getEditor() != $form_state->getValue('editor')) {
     $original_editor->delete();
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index 033832c..696d07a 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -151,7 +151,7 @@ function field_ui_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
  */
 function field_ui_form_node_type_form_alter(&$form, FormStateInterface $form_state) {
   // We want to display the button only on add page.
-  if ($form_state['controller']->getEntity()->isNew()) {
+  if ($form_state->getFormObject()->getEntity()->isNew()) {
     $form['actions']['save_continue'] = $form['actions']['submit'];
     $form['actions']['save_continue']['#value'] = t('Save and manage fields');
     $form['actions']['save_continue']['#weight'] = $form['actions']['save_continue']['#weight'] + 5;
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index ed2f05a..039c305 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -387,7 +387,7 @@ function forum_comment_delete(CommentInterface $comment) {
  */
 function forum_form_taxonomy_vocabulary_form_alter(&$form, FormStateInterface $form_state, $form_id) {
   $vid = \Drupal::config('forum.settings')->get('vocabulary');
-  $vocabulary = $form_state['controller']->getEntity();
+  $vocabulary = $form_state->getFormObject()->getEntity();
   if ($vid == $vocabulary->id()) {
     $form['help_forum_vocab'] = array(
       '#markup' => t('This is the designated forum vocabulary. Some of the normal vocabulary options have been removed.'),
@@ -419,7 +419,7 @@ function forum_form_taxonomy_term_form_alter(&$form, FormStateInterface $form_st
  * Implements hook_form_BASE_FORM_ID_alter() for node_form().
  */
 function forum_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
-  $node = $form_state['controller']->getEntity();
+  $node = $form_state->getFormObject()->getEntity();
   if (isset($node->taxonomy_forums) && !$node->isNew()) {
     $forum_terms = $node->taxonomy_forums;
     // If editing, give option to leave shadows.
diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module
index 5790899..820887a 100644
--- a/core/modules/menu_ui/menu_ui.module
+++ b/core/modules/menu_ui/menu_ui.module
@@ -321,7 +321,7 @@ function menu_ui_node_prepare_form(NodeInterface $node, $operation, FormStateInt
 function menu_ui_form_node_form_alter(&$form, FormStateInterface $form_state) {
   // Generate a list of possible parents (not including this link or descendants).
   // @todo This must be handled in a #process handler.
-  $node = $form_state['controller']->getEntity();
+  $node = $form_state->getFormObject()->getEntity();
   $definition = $form_state['menu_link_definition'];
   $type = $node->getType();
   /** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
@@ -446,7 +446,7 @@ function menu_ui_form_node_type_form_alter(&$form, FormStateInterface $form_stat
   /** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
   $menu_parent_selector = \Drupal::service('menu.parent_form_selector');
   $menu_options = menu_ui_get_menus();
-  $type = $form_state['controller']->getEntity();
+  $type = $form_state->getFormObject()->getEntity();
   if ($type->id()) {
     $config_values = \Drupal::config('menu.entity.node.' . $type->id())->get();
   }
@@ -516,7 +516,7 @@ function menu_ui_form_node_type_form_validate(&$form, FormStateInterface $form_s
  * @see menu_ui_form_node_type_form_alter().
  */
 function menu_ui_form_node_type_form_submit(&$form, FormStateInterface $form_state) {
-  $type = $form_state['controller']->getEntity();
+  $type = $form_state->getFormObject()->getEntity();
   \Drupal::config('menu.entity.node.' . $type->id())
     ->set('available_menus', array_values(array_filter($form_state->getValue('menu_options'))))
     ->set('parent', $form_state->getValue('menu_parent'))
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index a100487..4ede02f 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -95,7 +95,7 @@ public function form(array $form, FormStateInterface $form_state) {
 
       // Rebuild the form.
       $form_state['rebuild'] = TRUE;
-      $this->entity = $preview['controller']->getEntity();
+      $this->entity = $preview->getFormObject()->getEntity();
       unset($this->entity->in_preview);
 
       // Remove the entry from the temp store.
diff --git a/core/modules/node/src/ParamConverter/NodePreviewConverter.php b/core/modules/node/src/ParamConverter/NodePreviewConverter.php
index 1b0f118..adf22ee 100644
--- a/core/modules/node/src/ParamConverter/NodePreviewConverter.php
+++ b/core/modules/node/src/ParamConverter/NodePreviewConverter.php
@@ -41,7 +41,7 @@ public function __construct(TempStoreFactory $temp_store_factory) {
   public function convert($value, $definition, $name, array $defaults, Request $request) {
     $store = $this->tempStoreFactory->get('node_preview');
     if ($form_state = $store->get($value)) {
-      return $form_state['controller']->getEntity();
+      return $form_state->getFormObject()->getEntity();
     }
   }
 
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index aeba50b..0bf3781 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -56,7 +56,7 @@ function path_permission() {
  * Implements hook_form_BASE_FORM_ID_alter() for node_form().
  */
 function path_form_node_form_alter(&$form, FormStateInterface $form_state) {
-  $node = $form_state['controller']->getEntity();
+  $node = $form_state->getFormObject()->getEntity();
   if ($node->hasField('path') && $node->get('path')->access('edit')) {
     $form['path_settings'] = array(
       '#type' => 'details',
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 86336f6..59922b7 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -675,7 +675,7 @@ function hook_page_alter(&$page) {
  *
  * One popular use of this hook is to add form elements to the node form. When
  * altering a node form, the node entity can be retrieved by invoking
- * $form_state['controller']->getEntity().
+ * $form_state->getFormObject()->getEntity().
  *
  * In addition to hook_form_alter(), which is called for all forms, there are
  * two more specific form hooks available. The first,
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index ddf052e..dc372f8 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1024,7 +1024,7 @@ function system_user_login($account) {
 function system_user_timezone(&$form, FormStateInterface $form_state) {
   $user = \Drupal::currentUser();
 
-  $account = $form_state['controller']->getEntity();
+  $account = $form_state->getFormObject()->getEntity();
   $form['timezone'] = array(
     '#type' => 'details',
     '#title' => t('Locale settings'),
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 21d6508..c7db020 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -247,7 +247,7 @@ function entity_test_permission() {
  * Implements hook_form_BASE_FORM_ID_alter().
  */
 function entity_test_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
-  $langcode = $form_state['controller']->getFormLangcode($form_state);
+  $langcode = $form_state->getFormObject()->getFormLangcode($form_state);
   \Drupal::state()->set('entity_test.form_langcode', $langcode);
 }
 
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index 18652ad..5ac883f 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -268,7 +268,7 @@ function seven_preprocess_maintenance_page(&$variables) {
  */
 function seven_form_node_form_alter(&$form, FormStateInterface $form_state) {
   /** @var \Drupal\node\NodeInterface $node */
-  $node = $form_state->get('controller')->getEntity();
+  $node = $form_state->getFormObject()->getEntity();
 
   $form['#theme'] = array('node_edit_form');
   $form['#attached']['css'][] = drupal_get_path('module', 'node') . '/css/node.module.css';
