diff --git a/core/core.services.yml b/core/core.services.yml
index 5959c44..4ab225b 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1356,7 +1356,7 @@ services:
     lazy: true
   render_cache:
     class: Drupal\Core\Render\RenderCache
-    arguments: ['@request_stack', '@cache_factory', '@cache_contexts_manager']
+    arguments: ['@request_stack', '@cache_factory', '@cache_contexts_manager', '%renderer.config%']
   renderer:
     class: Drupal\Core\Render\Renderer
     arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@render_cache', '%renderer.config%']
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index fcba978..888fe41 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -857,7 +857,7 @@ public function getEntityTypeLabels($group = FALSE) {
   public function getTranslationFromContext(EntityInterface $entity, $langcode = NULL, $context = array()) {
     $translation = $entity;
 
-    if ($entity instanceof TranslatableInterface) {
+    if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) {
       if (empty($langcode)) {
         $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
       }
diff --git a/core/lib/Drupal/Core/Field/FormatterInterface.php b/core/lib/Drupal/Core/Field/FormatterInterface.php
index 4ba0efa..196fb89 100644
--- a/core/lib/Drupal/Core/Field/FormatterInterface.php
+++ b/core/lib/Drupal/Core/Field/FormatterInterface.php
@@ -60,7 +60,7 @@ public function settingsSummary();
    * items.
    *
    * @param \Drupal\Core\Field\FieldItemListInterface[] $entities_items
-   *   Array of field values, keyed by entity ID.
+   *   An array with the field values from the multiple entities being rendered.
    */
   public function prepareView(array $entities_items);
 
diff --git a/core/lib/Drupal/Core/Render/RenderCache.php b/core/lib/Drupal/Core/Render/RenderCache.php
index 3f206a3..498b6b3 100644
--- a/core/lib/Drupal/Core/Render/RenderCache.php
+++ b/core/lib/Drupal/Core/Render/RenderCache.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Render;
 
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Cache\CacheContextsManager;
 use Drupal\Core\Cache\CacheFactoryInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
@@ -39,6 +40,13 @@ class RenderCache implements RenderCacheInterface {
   protected $cacheContextsManager;
 
   /**
+   * The renderer configuration array.
+   *
+   * @var array
+   */
+  protected $rendererConfig;
+
+  /**
    * Constructs a new RenderCache object.
    *
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
@@ -47,11 +55,31 @@ class RenderCache implements RenderCacheInterface {
    *   The cache factory.
    * @param \Drupal\Core\Cache\CacheContextsManager $cache_contexts_manager
    *   The cache contexts manager.
+   * @param array $renderer_config
+   *   The renderer configuration array.
    */
-  public function __construct(RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContextsManager $cache_contexts_manager) {
+  public function __construct(RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContextsManager $cache_contexts_manager, array $renderer_config) {
     $this->requestStack = $request_stack;
     $this->cacheFactory = $cache_factory;
     $this->cacheContextsManager = $cache_contexts_manager;
+    $this->rendererConfig = $renderer_config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createCacheableRenderArray(array $keys) {
+    $build = [
+      '#cache' => [
+        'keys' => $keys,
+        'contexts' => $this->rendererConfig['required_cache_contexts'],
+      ],
+    ];
+
+    CacheableMetadata::createFromRenderArray($build)
+      ->applyTo($build);
+
+    return $build;
   }
 
   /**
@@ -295,7 +323,7 @@ protected function createCacheID(array $elements) {
    * {@inheritdoc}
    */
   public function getCacheableRenderArray(array $elements) {
-    return [
+    $data = [
       '#markup' => $elements['#markup'],
       '#attached' => $elements['#attached'],
       '#post_render_cache' => $elements['#post_render_cache'],
@@ -305,6 +333,12 @@ public function getCacheableRenderArray(array $elements) {
         'max-age' => $elements['#cache']['max-age'],
       ],
     ];
+
+    if (!empty($elements['#cache']['properties']) && is_array($elements['#cache']['properties'])) {
+      $data += array_intersect_key($elements, array_flip($elements['#cache']['properties']));
+    }
+
+    return $data;
   }
 
 }
diff --git a/core/lib/Drupal/Core/Render/RenderCacheInterface.php b/core/lib/Drupal/Core/Render/RenderCacheInterface.php
index 0e24f1b..47688219 100644
--- a/core/lib/Drupal/Core/Render/RenderCacheInterface.php
+++ b/core/lib/Drupal/Core/Render/RenderCacheInterface.php
@@ -17,6 +17,17 @@
 interface RenderCacheInterface {
 
   /**
+   * Creates a new cacheable render array with required metadata pre-filled.
+   *
+   * @param string[] $keys
+   *   The cache keys.
+   *
+   * @return array
+   *   The cacheable render array.
+   */
+  public function createCacheableRenderArray(array $keys);
+
+  /**
    * Gets a cacheable render array for a render array and its rendered output.
    *
    * Given a render array and its rendered output (HTML string), return an array
diff --git a/core/modules/comment/src/Tests/Views/CommentUserNameTest.php b/core/modules/comment/src/Tests/Views/CommentUserNameTest.php
index 2c64bb2..bf06be6 100644
--- a/core/modules/comment/src/Tests/Views/CommentUserNameTest.php
+++ b/core/modules/comment/src/Tests/Views/CommentUserNameTest.php
@@ -152,6 +152,8 @@ public function testUsername() {
 
     $account_switcher->switchTo(new AnonymousUserSession());
     $executable = Views::getView($view_id);
+    $executable->storage->invalidateCaches();
+
     $build = $executable->preview();
     $this->setRawContent($renderer->render($build));
 
diff --git a/core/modules/contact/src/Access/ContactPageAccess.php b/core/modules/contact/src/Access/ContactPageAccess.php
index 0d6ffcb..da35bed 100644
--- a/core/modules/contact/src/Access/ContactPageAccess.php
+++ b/core/modules/contact/src/Access/ContactPageAccess.php
@@ -61,24 +61,24 @@ public function access(UserInterface $user, AccountInterface $account) {
     $contact_account = $user;
 
     // Anonymous users cannot have contact forms.
+    $access = $this->getCacheableResult(AccessResult::forbidden(), $contact_account);
     if ($contact_account->isAnonymous()) {
-      return AccessResult::forbidden()->cachePerPermissions();
+      return $access;
     }
 
     // Users may not contact themselves.
     if ($account->id() == $contact_account->id()) {
-      return AccessResult::forbidden()->cachePerUser();
+      return $access;
     }
 
     // User administrators should always have access to personal contact forms.
-    $access = AccessResult::neutral()->cachePerPermissions();
+    $access = $this->getCacheableResult(AccessResult::neutral(), $contact_account);
     $permission_access = AccessResult::allowedIfHasPermission($account, 'administer users');
     if ($permission_access->isAllowed()) {
       return $access->orIf($permission_access);
     }
 
     // If requested user has been blocked, do not allow users to contact them.
-    $access->cacheUntilEntityChanges($contact_account);
     if ($contact_account->isBlocked()) {
       return $access;
     }
@@ -91,15 +91,32 @@ public function access(UserInterface $user, AccountInterface $account) {
         return $access;
       }
     }
+
     // If the requested user did not save a preference yet, deny access if the
     // configured default is disabled.
-    $contact_settings = $this->configFactory->get('contact.settings');
-    $access->cacheUntilConfigurationChanges($contact_settings);
-    if (!isset($account_data) && !$contact_settings->get('user_default_enabled')) {
+    if (!isset($account_data) && !$this->configFactory->get('contact.settings')->get('user_default_enabled')) {
       return $access;
     }
 
     return $access->orIf(AccessResult::allowedIfHasPermission($account, 'access user contact forms'));
   }
 
+  /**
+   * Returns a cacheable access result.
+   *
+   * @param \Drupal\Core\Access\AccessResult $access
+   *   The access result to be cached.
+   * @param \Drupal\user\UserInterface $contact_account
+   *   The account to be contacted.
+   *
+   * @return \Drupal\Core\Access\AccessResult
+   *   The processed access result.
+   */
+  protected function getCacheableResult(AccessResult $access, UserInterface $contact_account) {
+    return $access
+      ->cachePerUser()
+      ->cacheUntilEntityChanges($contact_account)
+      ->cacheUntilConfigurationChanges($this->configFactory->get('contact.settings'));
+  }
+
 }
diff --git a/core/modules/contact/src/Plugin/views/field/ContactLink.php b/core/modules/contact/src/Plugin/views/field/ContactLink.php
index f746e00..136f403 100644
--- a/core/modules/contact/src/Plugin/views/field/ContactLink.php
+++ b/core/modules/contact/src/Plugin/views/field/ContactLink.php
@@ -8,7 +8,6 @@
 namespace Drupal\contact\Plugin\views\field;
 
 use Drupal\Core\Access\AccessManagerInterface;
-use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Url;
@@ -33,28 +32,6 @@ class ContactLink extends Link {
   protected $accessManager;
 
   /**
-   * Current user object.
-   *
-   * @var \Drupal\Core\Session\AccountInterface
-   */
-  protected $currentUser;
-
-  /**
-   * Gets the current active user.
-   *
-   * @todo: https://drupal.org/node/2105123 put this method in
-   *   \Drupal\Core\Plugin\PluginBase instead.
-   *
-   * @return \Drupal\Core\Session\AccountInterface
-   */
-  protected function currentUser() {
-    if (!$this->currentUser) {
-      $this->currentUser = \Drupal::currentUser();
-    }
-    return $this->currentUser;
-  }
-
-  /**
    * Constructs a ContactLink object.
    *
    * @param array $configuration
@@ -101,21 +78,18 @@ public function access(AccountInterface $account) {
     return TRUE;
   }
 
-
   /**
    * {@inheritdoc}
    */
-  protected function renderLink(EntityInterface $entity, ResultRow $values) {
-
-    if (empty($entity)) {
-      return;
-    }
+  protected function checkLinkAccess(ResultRow $row) {
+    return $this->accessManager->checkNamedRoute('entity.user.contact_form', ['user' => $this->getEntity($row)->id()], $this->currentUser(), TRUE);
+  }
 
-    // Check access when we pull up the user account so we know
-    // if the user has made the contact page available.
-    if (!$this->accessManager->checkNamedRoute('entity.user.contact_form', array('user' => $entity->id()), $this->currentUser())) {
-      return;
-    }
+  /**
+   * {@inheritdoc}
+   */
+  protected function renderMarkup(ResultRow $row) {
+    $entity = $this->getEntity($row);
 
     $this->options['alter']['make_link'] = TRUE;
     $this->options['alter']['url'] =  Url::fromRoute('entity.user.contact_form', ['user' => $entity->id()]);
diff --git a/core/modules/contact/src/Tests/ContactPersonalTest.php b/core/modules/contact/src/Tests/ContactPersonalTest.php
index a586580..102452e 100644
--- a/core/modules/contact/src/Tests/ContactPersonalTest.php
+++ b/core/modules/contact/src/Tests/ContactPersonalTest.php
@@ -152,13 +152,13 @@ function testPersonalContactAccess() {
     // Test that anonymous users can access admin user's contact form.
     $this->drupalGet('user/' . $this->adminUser->id() . '/contact');
     $this->assertResponse(200);
-    $this->assertCacheContext('user.permissions');
+    $this->assertCacheContext('user');
 
     // Revoke the personal contact permission for the anonymous user.
     user_role_revoke_permissions(RoleInterface::ANONYMOUS_ID, array('access user contact forms'));
     $this->drupalGet('user/' . $this->contactUser->id() . '/contact');
     $this->assertResponse(403);
-    $this->assertCacheContext('user.permissions');
+    $this->assertCacheContext('user');
     $this->drupalGet('user/' . $this->adminUser->id() . '/contact');
     $this->assertResponse(403);
 
diff --git a/core/modules/contact/src/Tests/Views/ContactLinkTest.php b/core/modules/contact/src/Tests/Views/ContactLinkTest.php
index 320deb0..4c82d18 100644
--- a/core/modules/contact/src/Tests/Views/ContactLinkTest.php
+++ b/core/modules/contact/src/Tests/Views/ContactLinkTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\contact\Tests\Views;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\views\Tests\ViewTestBase;
 use Drupal\views\Tests\ViewTestData;
 use Drupal\user\Entity\User;
@@ -84,6 +85,8 @@ public function testContactLink() {
 
     // Disable contact link for no_contact.
     $this->userData->set('contact', $no_contact_account->id(), 'enabled', FALSE);
+    // @todo Remove cache invalidation in https://www.drupal.org/node/2477903.
+    Cache::invalidateTags($no_contact_account->getCacheTags());
     $this->drupalGet('test-contact-link');
     $this->assertContactLinks($accounts, array('root', 'admin'));
   }
diff --git a/core/modules/contact/tests/modules/contact_test_views/test_views/views.view.test_contact_link.yml b/core/modules/contact/tests/modules/contact_test_views/test_views/views.view.test_contact_link.yml
index 28b74c8..339f4ff 100644
--- a/core/modules/contact/tests/modules/contact_test_views/test_views/views.view.test_contact_link.yml
+++ b/core/modules/contact/tests/modules/contact_test_views/test_views/views.view.test_contact_link.yml
@@ -3,12 +3,13 @@ status: true
 dependencies:
   module:
     - contact
+    - user
 id: test_contact_link
 label: test_contact_link
 module: views
 description: ''
 tag: ''
-base_table: users
+base_table: users_field_data
 base_field: uid
 core: 8.x
 display:
@@ -69,7 +70,7 @@ display:
       fields:
         name:
           id: name
-          table: users
+          table: users_field_data
           field: name
           label: ''
           alter:
@@ -111,7 +112,7 @@ display:
       filters:
         status:
           value: true
-          table: users
+          table: users_field_data
           field: status
           id: status
           expose:
@@ -127,10 +128,22 @@ display:
       empty: {  }
       relationships: {  }
       arguments: {  }
+      display_extenders: {  }
+    cache_metadata:
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+      cacheable: false
   page_1:
     display_plugin: page
     id: page_1
     display_title: Page
     position: 1
     display_options:
+      display_extenders: {  }
       path: test-contact-link
+    cache_metadata:
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+      cacheable: false
diff --git a/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php b/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php
index e934e0f..c7f3797 100644
--- a/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php
+++ b/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php
@@ -100,6 +100,7 @@ public function testIntegration() {
 
     // Disable replacing variables and check that the tokens aren't replaced.
     $view->destroy();
+    $view->storage->invalidateCaches();
     $view->initHandlers();
     $this->executeView($view);
     $view->initStyle();
diff --git a/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php b/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
index 92353f0..f3be429 100644
--- a/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
+++ b/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
@@ -117,6 +117,7 @@ protected function setUp() {
    *   The view to add field data to.
    */
   protected function prepareView(ViewExecutable $view) {
+    $view->storage->invalidateCaches();
     $view->initDisplay();
     foreach ($this->fieldStorages as $field_storage) {
       $field_name = $field_storage->getName();
@@ -205,7 +206,7 @@ public function _testMultipleFieldRender() {
       foreach ($pure_items as $j => $item) {
         $items[] = $pure_items[$j]['value'];
       }
-      $this->assertEqual($rendered_field, implode(', ', $items), 'Make sure that the amount of items is limited.');
+      $this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited.');
     }
 
     // Test that an empty field is rendered without error.
@@ -227,7 +228,7 @@ public function _testMultipleFieldRender() {
       foreach ($pure_items as $j => $item) {
         $items[] = $pure_items[$j]['value'];
       }
-      $this->assertEqual($rendered_field, implode(', ', $items), 'Make sure that the amount of items is limited.');
+      $this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited and the offset is correct.');
     }
     $view->destroy();
 
@@ -248,7 +249,7 @@ public function _testMultipleFieldRender() {
       foreach ($pure_items as $j => $item) {
         $items[] = $pure_items[$j]['value'];
       }
-      $this->assertEqual($rendered_field, implode(', ', $items), 'Make sure that the amount of items is limited.');
+      $this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited and they are reversed.');
     }
     $view->destroy();
 
@@ -266,7 +267,7 @@ public function _testMultipleFieldRender() {
       $pure_items = $this->nodes[$i]->{$field_name}->getValue();
       $items[] = $pure_items[0]['value'];
       $items[] = $pure_items[4]['value'];
-      $this->assertEqual($rendered_field, implode(', ', $items), 'Make sure that the amount of items is limited.');
+      $this->assertEqual($rendered_field, implode(', ', $items), 'Items are limited to first and last.');
     }
     $view->destroy();
 
@@ -286,7 +287,7 @@ public function _testMultipleFieldRender() {
       foreach ($pure_items as $j => $item) {
         $items[] = $pure_items[$j]['value'];
       }
-      $this->assertEqual($rendered_field, implode(':', $items), 'Make sure that the amount of items is limited.');
+      $this->assertEqual($rendered_field, implode(':', $items), 'The amount of items is limited and the custom separator is correct.');
     }
     $view->destroy();
 
@@ -305,7 +306,7 @@ public function _testMultipleFieldRender() {
       foreach ($pure_items as $j => $item) {
         $items[] = $pure_items[$j]['value'];
       }
-      $this->assertEqual($rendered_field, implode('<h2>test</h2>', $items), 'Make sure that the amount of items is limited.');
+      $this->assertEqual($rendered_field, implode('<h2>test</h2>', $items), 'The custom separator is correctly escaped.');
     }
     $view->destroy();
   }
diff --git a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
index c8549e6..7d513eb 100644
--- a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
@@ -383,10 +383,20 @@ protected function doTestEntityTranslationAPI($entity_type) {
 
     // Verify that changing the default translation flag causes an exception to
     // be thrown.
-    $message = 'The default translation flag cannot be changed.';
     foreach ($entity->getTranslationLanguages() as $t_langcode => $language) {
       $translation = $entity->getTranslation($t_langcode);
       $default = $translation->isDefaultTranslation();
+
+      $message = 'The default translation flag can be reassigned the same value.';
+      try {
+        $translation->{$default_langcode_key}->value = $default;
+        $this->pass($message);
+      }
+      catch (\LogicException $e) {
+        $this->fail($message);
+      }
+
+      $message = 'The default translation flag cannot be changed.';
       try {
         $translation->{$default_langcode_key}->value = !$default;
         $this->fail($message);
@@ -394,6 +404,7 @@ protected function doTestEntityTranslationAPI($entity_type) {
       catch (\LogicException $e) {
         $this->pass($message);
       }
+
       $this->assertEqual($translation->{$default_langcode_key}->value, $default);
     }
 
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.views.inc b/core/modules/system/tests/modules/entity_test/entity_test.views.inc
new file mode 100644
index 0000000..d74af47
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/entity_test.views.inc
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * Implements hook_views_data_alter().
+ */
+function entity_test_views_data_alter(&$data) {
+  $data['entity_test']['name_alias'] = $data['entity_test']['name'];
+  $data['entity_test']['name_alias']['field']['real field'] = 'name';
+}
diff --git a/core/modules/taxonomy/src/Tests/Views/TermNameFieldTest.php b/core/modules/taxonomy/src/Tests/Views/TermNameFieldTest.php
index 6a2d65c..35aa448 100644
--- a/core/modules/taxonomy/src/Tests/Views/TermNameFieldTest.php
+++ b/core/modules/taxonomy/src/Tests/Views/TermNameFieldTest.php
@@ -43,12 +43,11 @@ public function testTermNameField() {
     $view = Views::getView('test_taxonomy_term_name');
     $display =& $view->storage->getDisplay('default');
     $display['display_options']['fields']['name']['convert_spaces'] = TRUE;
-
+    $view->storage->invalidateCaches();
     $this->executeView($view);
 
     $this->assertEqual(str_replace(' ', '-', $this->term1->getName()), $view->getStyle()->getField(0, 'name'));
     $this->assertEqual($this->term2->getName(), $view->getStyle()->getField(1, 'name'));
-
   }
 
 }
diff --git a/core/modules/user/src/Plugin/views/field/Link.php b/core/modules/user/src/Plugin/views/field/Link.php
index 78057cc..6a64dc8 100644
--- a/core/modules/user/src/Plugin/views/field/Link.php
+++ b/core/modules/user/src/Plugin/views/field/Link.php
@@ -10,11 +10,11 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RedirectDestinationTrait;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\views\Plugin\views\field\FieldPluginBase;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\Plugin\views\field\CacheableEntityLinkTrait;
+use Drupal\views\Plugin\views\field\FieldPluginBase;
 use Drupal\views\ResultRow;
 use Drupal\views\ViewExecutable;
-use Drupal\Core\Entity\EntityInterface;
 
 /**
  * Field handler to present a link to the user.
@@ -25,6 +25,7 @@
  */
 class Link extends FieldPluginBase {
 
+  use CacheableEntityLinkTrait;
   use RedirectDestinationTrait;
 
   /**
@@ -82,29 +83,10 @@ public function query() {
   /**
    * {@inheritdoc}
    */
-  public function render(ResultRow $values) {
-    if ($entity = $this->getEntity($values)) {
-      return $this->renderLink($entity, $values);
-    }
-  }
-
-  /**
-   * Alters the field to render a link.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   * @param \Drupal\views\ResultRow $values
-   *   The current row of the views result.
-   *
-   * @return string
-   *   The actual rendered text (without the link) of this field.
-   */
-  protected function renderLink(EntityInterface $entity, ResultRow $values) {
-    $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('View');
-
+  protected function renderMarkup(ResultRow $row) {
     $this->options['alter']['make_link'] = TRUE;
-    $this->options['alter']['url'] = $entity->urlInfo();
-
-    return $text;
+    $this->options['alter']['url'] = $this->getEntity($row)->urlInfo($this->getLinkRel());
+    return !empty($this->options['text']) ? $this->options['text'] : $this->t('View');
   }
 
 }
diff --git a/core/modules/user/src/Plugin/views/field/LinkCancel.php b/core/modules/user/src/Plugin/views/field/LinkCancel.php
index b29c426..8cb214a 100644
--- a/core/modules/user/src/Plugin/views/field/LinkCancel.php
+++ b/core/modules/user/src/Plugin/views/field/LinkCancel.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\user\Plugin\views\field;
 
-use Drupal\Core\Entity\EntityInterface;
 use Drupal\views\ResultRow;
 
 /**
@@ -22,17 +21,17 @@ class LinkCancel extends Link {
   /**
    * {@inheritdoc}
    */
-  protected function renderLink(EntityInterface $entity, ResultRow $values) {
-    if ($entity && $entity->access('delete')) {
-      $this->options['alter']['make_link'] = TRUE;
-
-      $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Cancel account');
-
-      $this->options['alter']['url'] = $entity->urlInfo('cancel-form');
-      $this->options['alter']['query'] = $this->getDestinationArray();
+  protected function renderMarkup(ResultRow $row) {
+    parent::renderMarkup($row);
+    $this->options['alter']['query'] = $this->getDestinationArray();
+    return !empty($this->options['text']) ? $this->options['text'] : $this->t('Cancel account');
+  }
 
-      return $text;
-    }
+  /**
+   * {@inheritdoc}
+   */
+  protected function getLinkRel() {
+    return 'cancel-form';
   }
 
 }
diff --git a/core/modules/user/src/Plugin/views/field/LinkEdit.php b/core/modules/user/src/Plugin/views/field/LinkEdit.php
index 23a4f5a..b3f22ce 100644
--- a/core/modules/user/src/Plugin/views/field/LinkEdit.php
+++ b/core/modules/user/src/Plugin/views/field/LinkEdit.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\user\Plugin\views\field;
 
-use Drupal\Core\Entity\EntityInterface;
 use Drupal\views\ResultRow;
 
 /**
@@ -22,16 +21,17 @@ class LinkEdit extends Link {
   /**
    * {@inheritdoc}
    */
-  protected function renderLink(EntityInterface $entity, ResultRow $values) {
-    if ($entity && $entity->access('update')) {
-      $this->options['alter']['make_link'] = TRUE;
-
-      $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Edit');
-
-      $this->options['alter']['url'] = $entity->urlInfo('edit-form', ['query' => ['destination' => $this->getDestinationArray()]]);
+  protected function renderMarkup(ResultRow $row) {
+    parent::renderMarkup($row);
+    $this->options['alter']['query'] = $this->getDestinationArray();
+    return !empty($this->options['text']) ? $this->options['text'] : $this->t('Edit');
+  }
 
-      return $text;
-    }
+  /**
+   * {@inheritdoc}
+   */
+  protected function getLinkRel() {
+    return 'edit-form';
   }
 
 }
diff --git a/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php b/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
index 9ce52f2..c2c0c93 100644
--- a/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
+++ b/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
@@ -33,6 +33,7 @@ public function testUserName() {
     $view->field['name']->options['link_to_user'] = TRUE;
     $view->field['name']->options['type'] = 'user_name';
     $view->field['name']->init($view, $view->getDisplay('default'));
+    $view->field['name']->options['id'] = 'name';
     $this->executeView($view);
 
     $anon_name = $this->config('user.settings')->get('anonymous');
diff --git a/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php b/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php
index 8470efd..697ae8a 100644
--- a/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php
+++ b/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php
@@ -15,7 +15,7 @@
 /**
  * Renders entities in a configured language.
  */
-class ConfigurableLanguageRenderer extends RendererBase {
+class ConfigurableLanguageRenderer extends EntityTranslationRendererBase {
 
   /**
    * A specific language code for rendering if available.
diff --git a/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php b/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php
index bab75d8..dbd3a72 100644
--- a/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php
+++ b/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php
@@ -12,7 +12,7 @@
 /**
  * Renders entities in their default language.
  */
-class DefaultLanguageRenderer extends RendererBase {
+class DefaultLanguageRenderer extends EntityTranslationRendererBase {
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
new file mode 100644
index 0000000..8865a5f
--- /dev/null
+++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
@@ -0,0 +1,286 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Entity\Render\EntityFieldRenderer.
+ */
+
+namespace Drupal\views\Entity\Render;
+
+use Drupal\Core\Entity\Entity\EntityViewDisplay;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\views\Plugin\views\field\Field;
+use Drupal\views\Plugin\views\query\QueryPluginBase;
+use Drupal\views\ResultRow;
+use Drupal\views\ViewExecutable;
+
+/**
+ * Renders entity fields.
+ *
+ * This is used to build render arrays for all entity field values of a view
+ * result set sharing the same relationship. An entity translation renderer is
+ * used internally to handle entity language properly.
+ */
+class EntityFieldRenderer extends RendererBase {
+  use EntityTranslationRenderTrait;
+
+  /**
+   * The relationship being handled.
+   *
+   * @var string
+   */
+  protected $relationship;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * A list of indexes of rows whose fields have already been rendered.
+   *
+   * @var int[]
+   */
+  protected $processedRows = [];
+
+  /**
+   * Constructs an EntityFieldRenderer object.
+   *
+   * @param \Drupal\views\ViewExecutable $view
+   *   The view whose fields are being rendered.
+   * @param string $relationship
+   *   The relationship to be handled.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(ViewExecutable $view, $relationship, LanguageManagerInterface $language_manager, EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager) {
+    parent::__construct($view, $language_manager, $entity_type);
+    $this->relationship = $relationship;
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheContexts() {
+    return $this->getEntityTranslationRenderer()->getCacheContexts();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEntityTypeId() {
+    return $this->entityType->id();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntityManager() {
+    return $this->entityManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+
+  protected function getLanguageManager() {
+    return $this->languageManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getView() {
+    return $this->view;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query(QueryPluginBase $query, $relationship = NULL) {
+    $this->getEntityTranslationRenderer()->query($query, $relationship);
+  }
+
+  /**
+   * Renders entity field data.
+   *
+   * @param \Drupal\views\ResultRow $row
+   *   A single row of the query result.
+   * @param \Drupal\views\Plugin\views\field\Field $field
+   *   (optional) A field to be rendered.
+   *
+   * @return array
+   *   A renderable array for the entity data contained in the result row.
+   */
+  public function render(ResultRow $row, Field $field = NULL) {
+    // The method is called for each field in each result row. In order to
+    // leverage multiple-entity building of formatter output, we build the
+    // render arrays for all fields in all rows on the first call.
+    if (!isset($this->build)) {
+      $this->build = $this->buildFields($this->view->result);
+    }
+
+    if (isset($field)) {
+      $field_id = $field->options['id'];
+      // Pick the render array for the row / field we are being asked to render,
+      // and remove it from $this->build to free memory as we progress.
+      if (isset($this->build[$row->index][$field_id])) {
+        $build = $this->build[$row->index][$field_id];
+        unset($this->build[$row->index][$field_id]);
+      }
+      else {
+        // In the uncommon case where a field gets rendered several times
+        // (typically through direct Views API calls), the pre-computed render
+        // array was removed by the unset() above. We have to manually rebuild
+        // the render array for the row.
+        $build = $this->buildFields([$row])[$row->index][$field_id];
+      }
+    }
+    else {
+      // Same logic as above, in the case where we are being called for a whole
+      // row.
+      if (isset($this->build[$row->index])) {
+        $build = $this->build[$row->index];
+        unset($this->build[$row->index]);
+      }
+      else {
+        $build = $this->buildFields([$row])[$row->index];
+      }
+    }
+
+    return $build;
+  }
+
+  /**
+   * Builds the render arrays for all fields of all result rows.
+   *
+   * The output is built using EntityViewDisplay objects to leverage
+   * multiple-entity building and ensure a common code path with regular entity
+   * view.
+   * - Each relationship is handled by a separate EntityFieldRenderer instance,
+   *   since it operates on its own set of entities. This also ensures different
+   *   entity types are handled separately, as they imply different
+   *   relationships.
+   * - Within each relationship, the fields to render are arranged in unique
+   *   sets containing each field at most once (an EntityViewDisplay can
+   *   only process a field once with given display options, but a View can
+   *   contain the same field several times with different display options).
+   * - For each set of fields, entities are processed by bundle, so that
+   *   formatters can operate on the proper field definition for the bundle.
+   *
+   * @param \Drupal\views\ResultRow[] $values
+   *   An array of all ResultRow objects returned from the query.
+   *
+   * @return array
+   *   A renderable array for the fields handled by this renderer.
+   *
+   * @see \Drupal\Core\Entity\Entity\EntityViewDisplay
+   */
+  protected function buildFields(array $values) {
+    $build = [];
+
+    if ($values && ($field_ids = $this->getRenderableFieldIds())) {
+      $entity_type_id = $this->getEntityTypeId();
+
+      // Collect the entities for the relationship, fetch the right translation,
+      // and group by bundle. For each result row, the corresponding entity can
+      // be obtained from any of the fields handlers, so we arbitrarily use the
+      // first one.
+      $entities_by_bundles = [];
+      $field = $this->view->field[current($field_ids)];
+      foreach ($values as $result_row) {
+        $entity = $field->getEntity($result_row);
+        $entities_by_bundles[$entity->bundle()][$result_row->index] = $this->getEntityTranslation($entity, $result_row);
+      }
+
+      // Determine unique sets of fields that can be processed by the same
+      // display. Fields that appear several times in the View open additional
+      // "overflow" displays.
+      $display_sets = [];
+      foreach ($field_ids as $field_id) {
+        $field = $this->view->field[$field_id];
+        $index = 0;
+        while (isset($display_sets[$index][$field->definition['field_name']])) {
+          $index++;
+        }
+        $display_sets[$index][$field_id] = $field;
+      }
+
+      // For each set of fields, build the output by bundle.
+      foreach ($display_sets as $display_fields) {
+        foreach ($entities_by_bundles as $bundle => $bundle_entities) {
+          // Create the display, and configure the field display options.
+          $display = EntityViewDisplay::create([
+            'targetEntityType' => $entity_type_id,
+            'bundle' => $bundle,
+            'status' => TRUE,
+          ]);
+          foreach ($display_fields as $field_id => $field) {
+            $display->setComponent($field->definition['field_name'], [
+              'type' => $field->options['type'],
+              'settings' => $field->options['settings'],
+            ]);
+          }
+          // Let the display build the render array for the entities.
+          $display_build = $display->buildMultiple($bundle_entities);
+          // Collect the field render arrays and index them using our internal
+          // row indexes and field IDs.
+          foreach ($display_build as $row_index => $entity_build) {
+            foreach ($display_fields as $field_id => $field) {
+              $build[$row_index][$field_id] = !empty($entity_build[$field->definition['field_name']]) ? $entity_build[$field->definition['field_name']] : [];
+            }
+          }
+        }
+      }
+    }
+
+    return $build;
+  }
+
+  /**
+   * Returns a list of names of entity fields to be rendered.
+   *
+   * @return string[]
+   *   An associative array of views fields.
+   */
+  protected function getRenderableFieldIds() {
+    $field_ids = [];
+    foreach ($this->view->field as $field_id => $field) {
+      if ($field instanceof Field && $field->relationship == $this->relationship) {
+        $field_ids[] = $field_id;
+      }
+    }
+    return $field_ids;
+  }
+
+  /**
+   * Returns the entity translation matching the configured row language.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object the field value being processed is attached to.
+   * @param \Drupal\views\ResultRow $row
+   *   The result row the field value being processed belongs to.
+   *
+   * @return \Drupal\Core\Entity\FieldableEntityInterface
+   *   The entity translation object for the specified row.
+   */
+  public function getEntityTranslation(EntityInterface $entity, ResultRow $row) {
+    // We assume the same language should be used for all entity fields
+    // belonging to a single row, even if they are attached to different entity
+    // types. Below we apply language fallback to ensure a valid value is always
+    // picked.
+    $langcode = $this->getEntityTranslationRenderer()->getLangcode($row);
+    return $this->entityManager->getTranslationFromContext($entity, $langcode);
+  }
+
+}
diff --git a/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php b/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php
index 1be7364..f874c75 100644
--- a/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php
+++ b/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php
@@ -10,25 +10,25 @@
 use Drupal\views\Plugin\views\PluginBase;
 
 /**
- * Trait used to instantiate the view's entity language render.
+ * Trait used to instantiate the view's entity translation renderer.
  */
 trait EntityTranslationRenderTrait {
 
   /**
    * The renderer to be used to render the entity row.
    *
-   * @var \Drupal\views\Entity\Render\RendererBase
+   * @var \Drupal\views\Entity\Render\EntityTranslationRendererBase
    */
-  protected $entityLanguageRenderer;
+  protected $entityTranslationRenderer;
 
   /**
    * Returns the current renderer.
    *
-   * @return \Drupal\views\Entity\Render\RendererBase
+   * @return \Drupal\views\Entity\Render\EntityTranslationRendererBase
    *   The configured renderer.
    */
   protected function getEntityTranslationRenderer() {
-    if (!isset($this->entityLanguageRenderer)) {
+    if (!isset($this->entityTranslationRenderer)) {
       $view = $this->getView();
       $rendering_language = $view->display_handler->getOption('rendering_language');
       $langcode = NULL;
@@ -52,9 +52,9 @@ protected function getEntityTranslationRenderer() {
       }
       $class = '\Drupal\views\Entity\Render\\' . $renderer;
       $entity_type = $this->getEntityManager()->getDefinition($this->getEntityTypeId());
-      $this->entityLanguageRenderer = new $class($view, $this->getLanguageManager(), $entity_type, $langcode);
+      $this->entityTranslationRenderer = new $class($view, $this->getLanguageManager(), $entity_type, $langcode);
     }
-    return $this->entityLanguageRenderer;
+    return $this->entityTranslationRenderer;
   }
 
   /**
diff --git a/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php b/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php
new file mode 100644
index 0000000..4da6672
--- /dev/null
+++ b/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Entity\Render\EntityTranslationRendererBase.
+ */
+
+namespace Drupal\views\Entity\Render;
+
+use Drupal\views\Plugin\views\query\QueryPluginBase;
+use Drupal\views\ResultRow;
+
+/**
+ * Defines a base class for entity translation renderers.
+ */
+abstract class EntityTranslationRendererBase extends RendererBase {
+
+  /**
+   * Returns the language code associated to the given row.
+   *
+   * @param \Drupal\views\ResultRow $row
+   *   The result row.
+   *
+   * @return string
+   *   A language code.
+   */
+  abstract public function getLangcode(ResultRow $row);
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query(QueryPluginBase $query, $relationship = NULL) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(array $result) {
+    $view_builder = $this->view->rowPlugin->entityManager->getViewBuilder($this->entityType->id());
+
+    /** @var \Drupal\views\ResultRow $row */
+    foreach ($result as $row) {
+      // @todo Take relationships into account.
+      //   See https://www.drupal.org/node/2457999.
+      $entity = $row->_entity;
+      $entity->view = $this->view;
+      $this->build[$entity->id()] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render(ResultRow $row) {
+    $entity_id = $row->_entity->id();
+    return $this->build[$entity_id];
+  }
+
+}
diff --git a/core/modules/views/src/Entity/Render/RendererBase.php b/core/modules/views/src/Entity/Render/RendererBase.php
index a0baec6..deb1806 100644
--- a/core/modules/views/src/Entity/Render/RendererBase.php
+++ b/core/modules/views/src/Entity/Render/RendererBase.php
@@ -8,7 +8,6 @@
 namespace Drupal\views\Entity\Render;
 
 use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\query\QueryPluginBase;
@@ -16,7 +15,7 @@
 use Drupal\views\ViewExecutable;
 
 /**
- * Defines a base class for entity row renderers.
+ * Defines a base class for entity renderers.
  */
 abstract class RendererBase implements CacheablePluginInterface {
 
@@ -46,7 +45,7 @@
    *
    * @var array
    */
-  protected $build = array();
+  protected $build;
 
   /**
    * Constructs a renderer object.
@@ -79,17 +78,6 @@ public function getCacheContexts() {
   }
 
   /**
-   * Returns the language code associated to the given row.
-   *
-   * @param \Drupal\views\ResultRow $row
-   *   The result row.
-   *
-   * @return string
-   *   A language code.
-   */
-  abstract public function getLangcode(ResultRow $row);
-
-  /**
    * Alters the query if needed.
    *
    * @param \Drupal\views\Plugin\views\query\QueryPluginBase $query
@@ -97,38 +85,26 @@ public function getCacheContexts() {
    * @param string $relationship
    *   (optional) The relationship, used by a field.
    */
-  public function query(QueryPluginBase $query, $relationship = NULL) {
-  }
+  abstract public function query(QueryPluginBase $query, $relationship = NULL);
 
   /**
-   * Runs before each row is rendered.
+   * Runs before each entity is rendered.
    *
    * @param $result
    *   The full array of results from the query.
    */
   public function preRender(array $result) {
-    $view_builder = $this->view->rowPlugin->entityManager->getViewBuilder($this->entityType->id());
-
-    /** @var \Drupal\views\ResultRow $row */
-    foreach ($result as $row) {
-      $entity = $row->_entity;
-      $entity->view = $this->view;
-      $this->build[$entity->id()] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row));
-    }
   }
 
   /**
-   * Renders a row object.
+   * Renders entity data.
    *
    * @param \Drupal\views\ResultRow $row
    *   A single row of the query result.
    *
    * @return array
-   *   The renderable array of a single row.
+   *   A renderable array for the entity data contained in the result row.
    */
-  public function render(ResultRow $row) {
-    $entity_id = $row->_entity->id();
-    return $this->build[$entity_id];
-  }
+  abstract public function render(ResultRow $row);
 
 }
diff --git a/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php b/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php
index cf0c296..c94243b 100644
--- a/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php
+++ b/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php
@@ -12,9 +12,9 @@
 use Drupal\views\ResultRow;
 
 /**
- * Renders entity translations in their active language.
+ * Renders entity translations in their row language.
  */
-class TranslationLanguageRenderer extends RendererBase {
+class TranslationLanguageRenderer extends EntityTranslationRendererBase {
 
   /**
    * Stores the field alias of the langcode column.
diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php
index 34b4ab0..36bb9be 100644
--- a/core/modules/views/src/Entity/View.php
+++ b/core/modules/views/src/Entity/View.php
@@ -333,6 +333,7 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
 
     // @todo Remove if views implements a view_builder controller.
     views_invalidate_cache();
+    $this->invalidateCaches();
 
     // Rebuild the router if this is a new view, or it's status changed.
     if (!isset($this->original) || ($this->status() != $this->original->status())) {
@@ -448,4 +449,13 @@ public function __sleep() {
     return $keys;
   }
 
+  /**
+   * Invalidates cache tags.
+   */
+  public function invalidateCaches() {
+    // Invalidate cache tags for cached rows.
+    $tags = $this->getCacheTags();
+    \Drupal::service('cache_tags.invalidator')->invalidateTags($tags);
+  }
+
 }
diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php
index 9c0f49b..523cd0e 100644
--- a/core/modules/views/src/Plugin/views/PluginBase.php
+++ b/core/modules/views/src/Plugin/views/PluginBase.php
@@ -112,6 +112,13 @@
   protected $renderer;
 
   /**
+   * Stores the render API render cache.
+   *
+   * @var \Drupal\Core\Render\RenderCacheInterface
+   */
+  protected $renderCache;
+
+  /**
    * Constructs a PluginBase object.
    *
    * @param array $configuration
@@ -596,4 +603,17 @@ protected function getRenderer() {
     return $this->renderer;
   }
 
+  /**
+   * Returns the render API render cache.
+   *
+   * @return \Drupal\Core\Render\RenderCacheInterface
+   *   The render cache.
+   */
+  protected function getRenderCache() {
+    if (!isset($this->renderCache)) {
+      $this->renderCache = \Drupal::service('render_cache');
+    }
+    return $this->renderCache;
+  }
+
 }
diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
index 224a964..6672962 100644
--- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Render\RendererInterface;
 use Drupal\views\Plugin\views\PluginBase;
 use Drupal\Core\Database\Query\Select;
+use Drupal\views\ResultRow;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -406,6 +407,68 @@ protected function prepareViewResult(array $result) {
   public function alterCacheMetadata(&$is_cacheable, array &$cache_contexts) {
   }
 
+  /**
+   * Returns the row cache tags.
+   *
+   * @param ResultRow $row
+   *   A result row.
+   *
+   * @return string[]
+   *   The row cache tags.
+   */
+  public function getRowCacheTags(ResultRow $row) {
+    $tags = !empty($row->_entity) ? $row->_entity->getCacheTags() : [];
+
+    if (!empty($row->_relationship_entities)) {
+      foreach ($row->_relationship_entities as $entity) {
+        $tags = Cache::mergeTags($tags, $entity->getCacheTags());
+      }
+    }
+
+    return $tags;
+  }
+
+  /**
+   * Returns the row cache keys.
+   *
+   * @param \Drupal\views\ResultRow $row
+   *   A result row.
+   *
+   * @return string[]
+   *   The row cache keys.
+   */
+  public function getRowCacheKeys(ResultRow $row) {
+    return [
+      'views',
+      'fields',
+      $this->view->id(),
+      $this->view->current_display,
+      $this->getRowId($row),
+    ];
+  }
+
+  /**
+   * Returns a unique identifier for the specified row.
+   *
+   * @param \Drupal\views\ResultRow $row
+   *   A result row.
+   *
+   * @return string
+   *   The row identifier.
+   */
+  public function getRowId(ResultRow $row) {
+    // Here we compute a unique identifier for the row by computing the hash of
+    // its data. We exclude the current index, since the same row could have a
+    // different result index depending on the user permissions. We exclude also
+    // entity data, since serializing entity objects is very expensive. Instead
+    // we include entity cache tags, which are enough to identify all the
+    // entities associated with the row.
+    $row_data = array_diff_key((array) $row, array_flip(['index', '_entity', '_relationship_entities'])) + $this->getRowCacheTags($row);
+
+    // Finally we compute a hash of row data and return it as row identifier.
+    return hash('sha256', serialize($row_data));
+  }
+
 }
 
 /**
diff --git a/core/modules/views/src/Plugin/views/field/CacheableEntityLinkTrait.php b/core/modules/views/src/Plugin/views/field/CacheableEntityLinkTrait.php
new file mode 100644
index 0000000..5f7fd83
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/field/CacheableEntityLinkTrait.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\field\CacheableEntityLinkTrait.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\views\ResultRow;
+
+/**
+ * Trait used to handle render cache for entity link field handlers.
+ */
+trait CacheableEntityLinkTrait {
+
+  use CacheableLinkTrait {
+    render as parentRender;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render(ResultRow $row) {
+    return $this->getEntity($row) ? $this->parentRender($row) : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkLinkAccess(ResultRow $row) {
+    $rel = $this->getLinkRel();
+    $url = $this->getEntity($row)->urlInfo($rel);
+    return \Drupal::accessManager()->checkNamedRoute($url->getRouteName(), $url->getRouteParameters(), $this->currentUser(), TRUE);
+  }
+
+  /**
+   * Returns the entity link relationship identifying the link route.
+   */
+  protected function getLinkRel() {
+    return 'canonical';
+  }
+
+  /**
+   * Returns the linked entity
+   *
+   * @param \Drupal\views\ResultRow $row
+   *   A view result row.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The linked entity.
+   */
+  protected abstract function getEntity(ResultRow $row);
+
+}
diff --git a/core/modules/views/src/Plugin/views/field/CacheableLinkTrait.php b/core/modules/views/src/Plugin/views/field/CacheableLinkTrait.php
new file mode 100644
index 0000000..5e900db
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/field/CacheableLinkTrait.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\field\CacheableLinkTrait.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Core\Access\AccessResultInterface;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\views\ResultRow;
+
+/**
+ * Trait used to handle render cache for link field handlers.
+ */
+trait CacheableLinkTrait {
+
+  /**
+   * Current user object.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Gets the current active user.
+   *
+   * @todo: https://drupal.org/node/2105123 put this method in
+   *   \Drupal\Core\Plugin\PluginBase instead.
+   *
+   * @return \Drupal\Core\Session\AccountInterface
+   */
+  protected function currentUser() {
+    if (!$this->currentUser) {
+      $this->currentUser = \Drupal::currentUser();
+    }
+    return $this->currentUser;
+  }
+
+  /**
+   * Renders a view field.
+   *
+   * @param \Drupal\views\ResultRow $row
+   *   A view result row.
+   *
+   * @return array
+   *   The field renderable array.
+   *
+   * @see \Drupal\views\Plugin\views\field\FieldPluginBase::render()
+   */
+  public function render(ResultRow $row) {
+    $build = [];
+
+    $access = $this->checkLinkAccess($row);
+    if ($access instanceof AccessResultInterface) {
+      BubbleableMetadata::createFromObject($access)->applyTo($build);
+      $access = $access->isAllowed();
+    }
+    if ($access) {
+      $build['#markup'] = $this->renderMarkup($row);
+    }
+
+    return $build;
+  }
+
+  /**
+   * Renders the link markup.
+   *
+   * @param \Drupal\views\ResultRow $row
+   *   A view result row.
+   *
+   * @return string
+   *   The link markup.
+   */
+  protected abstract function renderMarkup(ResultRow $row);
+
+  /**
+   * Checks access to the link route.
+   *
+   * @param \Drupal\views\ResultRow $row
+   *   A view result row.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
+   */
+  protected abstract function checkLinkAccess(ResultRow $row);
+
+}
diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php
index 5013b21..4192719 100644
--- a/core/modules/views/src/Plugin/views/field/Field.php
+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -17,13 +17,12 @@
 use Drupal\Core\Field\FormatterPluginManager;
 use Drupal\Core\Form\FormHelper;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\views\FieldAPIHandlerTrait;
-use Drupal\views\Entity\Render\EntityTranslationRenderTrait;
+use Drupal\views\Entity\Render\EntityFieldRenderer;
 use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ResultRow;
@@ -31,7 +30,7 @@
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * A field that displays fieldapi fields.
+ * A field that displays entity field data.
  *
  * @ingroup views_field_handlers
  *
@@ -40,8 +39,6 @@
  * @ViewsField("field")
  */
 class Field extends FieldPluginBase implements CacheablePluginInterface, MultiItemsFieldHandlerInterface {
-  use EntityTranslationRenderTrait;
-
   use FieldAPIHandlerTrait;
 
   /**
@@ -115,6 +112,13 @@ class Field extends FieldPluginBase implements CacheablePluginInterface, MultiIt
   protected $fieldTypePluginManager;
 
   /**
+   * Static cache for ::getEntityFieldRenderer().
+   *
+   * @var \Drupal\views\Entity\Render\EntityFieldRenderer
+   */
+  protected $entityFieldRenderer;
+
+  /**
    * Constructs a \Drupal\field\Plugin\views\field\Field object.
    *
    * @param array $configuration
@@ -202,13 +206,6 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
   /**
    * {@inheritdoc}
    */
-  public function getEntityTypeId() {
-    return $this->getEntityType();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   protected function getEntityManager() {
     return $this->entityManager;
   }
@@ -216,19 +213,6 @@ protected function getEntityManager() {
   /**
    * {@inheritdoc}
    */
-  protected function getLanguageManager() {
-    return $this->languageManager;
-  }
-  /**
-   * {@inheritdoc}
-   */
-  protected function getView() {
-    return $this->view;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function access(AccountInterface $account) {
     $access_control_handler = $this->entityManager->getAccessControlHandler($this->getEntityType());
     return $access_control_handler->fieldAccess('view', $this->getFieldDefinition(), $account);
@@ -272,8 +256,8 @@ public function query($use_groupby = FALSE) {
       $this->addAdditionalFields($fields);
     }
 
-    // Let the configured entity translation renderer alter the query if needed.
-    $this->getEntityTranslationRenderer()->query($this->query, $this->relationship);
+    // Let the entity field renderer alter the query if needed.
+    $this->getEntityFieldRenderer()->query($this->query, $this->relationship);
   }
 
   /**
@@ -686,6 +670,7 @@ public function submitGroupByForm(&$form, FormStateInterface $form_state) {
    */
   public function renderItems($items) {
     if (!empty($items)) {
+      $items = $this->prepareItemsByDelta($items);
       if ($this->options['multi_type'] == 'separator' || !$this->options['group_rows']) {
         $separator = $this->options['multi_type'] == 'separator' ? SafeMarkup::checkAdminXss($this->options['separator']) : '';
         $build = [
@@ -707,6 +692,110 @@ public function renderItems($items) {
   }
 
   /**
+   * Adapts the $items according to the delta configuration.
+   *
+   * This selects displayed deltas, reorders items, and takes offsets into
+   * account.
+   *
+   * @param array $all_values
+   *   The items for individual rendering.
+   *
+   * @return array
+   *   The manipulated items.
+   */
+  protected function prepareItemsByDelta(array $all_values) {
+    if ($this->options['delta_reversed']) {
+      $all_values = array_reverse($all_values);
+    }
+
+    // We are supposed to show only certain deltas.
+    if ($this->limit_values) {
+      $row = $this->view->result[$this->view->row_index];
+
+      // Offset is calculated differently when row grouping for a field is not
+      // enabled. Since there are multiple rows, delta needs to be taken into
+      // account, so that different values are shown per row.
+      if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($row->{$this->aliases['delta']})) {
+        $delta_limit = 1;
+        $offset = $row->{$this->aliases['delta']};
+      }
+      // Single fields don't have a delta available so choose 0.
+      elseif (!$this->options['group_rows'] && !$this->multiple) {
+        $delta_limit = 1;
+        $offset = 0;
+      }
+      else {
+        $delta_limit = $this->options['delta_limit'];
+        $offset = intval($this->options['delta_offset']);
+
+        // We should only get here in this case if there is an offset, and in
+        // that case we are limiting to all values after the offset.
+        if ($delta_limit === 0) {
+          $delta_limit = count($all_values) - $offset;
+        }
+      }
+
+      // Determine if only the first and last values should be shown.
+      $delta_first_last = $this->options['delta_first_last'];
+
+      $new_values = array();
+      for ($i = 0; $i < $delta_limit; $i++) {
+        $new_delta = $offset + $i;
+
+        if (isset($all_values[$new_delta])) {
+          // If first-last option was selected, only use the first and last
+          // values.
+          if (!$delta_first_last
+            // Use the first value.
+            || $new_delta == $offset
+            // Use the last value.
+            || $new_delta == ($delta_limit + $offset - 1)) {
+            $new_values[] = $all_values[$new_delta];
+          }
+        }
+      }
+      $all_values = $new_values;
+    }
+
+    return $all_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$values) {
+    parent::preRender($values);
+    $this->getEntityFieldRenderer()->preRender($values);
+  }
+
+  /**
+   * Returns the entity field renderer.
+   *
+   * @return \Drupal\views\Entity\Render\EntityFieldRenderer
+   *   The entity field renderer.
+   */
+  protected function getEntityFieldRenderer() {
+    if (!isset($this->entityFieldRenderer)) {
+      // This can be invoked during field handler initialization in which case
+      // view fields are not set yet.
+      if (!empty($this->view->field)) {
+        foreach ($this->view->field as $field) {
+          // An entity field renderer can handle only a single relationship.
+          if ($field->relationship == $this->relationship && isset($field->entityFieldRenderer)) {
+            $this->entityFieldRenderer = $field->entityFieldRenderer;
+            break;
+          }
+        }
+      }
+      if (!isset($this->entityFieldRenderer)) {
+        $entity_type = $this->entityManager->getDefinition($this->getEntityType());
+        $this->entityFieldRenderer = new EntityFieldRenderer($this->view, $this->relationship, $this->languageManager, $entity_type, $this->entityManager);
+      }
+    }
+    return $this->entityFieldRenderer;
+  }
+
+  /**
    * Gets an array of items for the field.
    *
    * @param \Drupal\views\ResultRow $values
@@ -716,76 +805,77 @@ public function renderItems($items) {
    *   An array of items for the field.
    */
   public function getItems(ResultRow $values) {
-    $original_entity = $this->getEntity($values);
-    if (!$original_entity) {
-      return array();
+    if (!$this->displayHandler->useGroupBy()) {
+      $build_list = $this->getEntityFieldRenderer()->render($values, $this);
     }
-    $entity = $this->process_entity($values, $original_entity);
-    if (!$entity) {
-      return array();
+    else {
+      // For grouped results we need to retrieve a massaged entity having
+      // grouped field values to ensure that "grouped by" values, especially
+      // those with multiple cardinality work properly. See
+      // \Drupal\views\Tests\QueryGroupByTest::testGroupByFieldWithCardinality.
+      $display = [
+        'type' => $this->options['type'],
+        'settings' => $this->options['settings'],
+        'label' => 'hidden',
+      ];
+      $build_list = $this->createEntityForGroupBy($this->getEntity($values), $values)
+        ->{$this->definition['field_name']}
+        ->view($display);
     }
 
-    $display = array(
-      'type' => $this->options['type'],
-      'settings' => $this->options['settings'],
-      'label' => 'hidden',
-    );
-    $render_array = $entity->get($this->definition['field_name'])->view($display);
+    if (!$build_list) {
+      return [];
+    }
 
     if ($this->options['field_api_classes']) {
-      return array(array('rendered' => $this->renderer->render($render_array)));
+      return [['rendered' => $this->renderer->render($build_list)]];
     }
 
-    $items = array();
-    foreach (Element::children($render_array) as $delta) {
-      $items[$delta]['rendered'] = $render_array[$delta];
+    // Render using the formatted data itself.
+    $items = [];
+    foreach (Element::children($build_list) as $delta) {
+      $items[$delta]['rendered'] = $build_list[$delta];
       // Merge the cacheability metadata of the top-level render array into
       // each child because they will most likely be rendered individually.
-      if (isset($render_array['#cache'])) {
-        CacheableMetadata::createFromRenderArray($render_array)
+      if (isset($build_list['#cache'])) {
+        CacheableMetadata::createFromRenderArray($build_list)
           ->merge(CacheableMetadata::createFromRenderArray($items[$delta]['rendered']))
           ->applyTo($items[$delta]['rendered']);
       }
       // Add the raw field items (for use in tokens).
-      $items[$delta]['raw'] = $render_array['#items'][$delta];
+      $items[$delta]['raw'] = $build_list['#items'][$delta];
     }
     return $items;
   }
 
   /**
-   * Process an entity before using it for rendering.
-   *
-   * Replaces values with aggregated values if aggregation is enabled.
-   * Takes delta settings into account (@todo remove in #1758616).
+   * Creates a fake entity with grouped field values.
    *
-   * @param \Drupal\views\ResultRow $values
-   *   The result row object containing the values.
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity to be processed.
+   * @param \Drupal\views\ResultRow $row
+   *   The result row object containing the values.
    *
-   * @return
-   *   TRUE if the processing completed successfully, otherwise FALSE.
+   * @return bool|\Drupal\Core\Entity\FieldableEntityInterface
+   *   Returns a new entity object containing the grouped field values.
    */
-  function process_entity(ResultRow $values, EntityInterface $entity) {
-    $processed_entity = clone $entity;
+  protected function createEntityForGroupBy(EntityInterface $entity, ResultRow $row) {
+    // Retrieve the correct translation object.
+    $processed_entity = clone $this->getEntityFieldRenderer()->getEntityTranslation($entity, $row);
 
-    $langcode = $this->getFieldLangcode($processed_entity, $values);
-    $processed_entity = $processed_entity->getTranslation($langcode);
-
-    // If we are grouping, copy our group fields into the cloned entity.
-    // It's possible this will cause some weirdness, but there's only
-    // so much we can hope to do.
-    if (!empty($this->group_fields)) {
+    // Copy our group fields into the cloned entity. It is possible this will
+    // cause some weirdness, but there is only so much we can hope to do.
+    if (!empty($this->group_fields) && isset($entity->{$this->definition['field_name']})) {
       // first, test to see if we have a base value.
       $base_value = array();
       // Note: We would copy original values here, but it can cause problems.
-      // For example, text fields store cached filtered values as
-      // 'safe_value' which doesn't appear anywhere in the field definition
-      // so we can't affect it. Other side effects could happen similarly.
+      // For example, text fields store cached filtered values as 'safe_value'
+      // which does not appear anywhere in the field definition so we cannot
+      // affect it. Other side effects could happen similarly.
       $data = FALSE;
       foreach ($this->group_fields as $field_name => $column) {
-        if (property_exists($values, $this->aliases[$column])) {
-          $base_value[$field_name] = $values->{$this->aliases[$column]};
+        if (property_exists($row, $this->aliases[$column])) {
+          $base_value[$field_name] = $row->{$this->aliases[$column]};
           if (isset($base_value[$field_name])) {
             $data = TRUE;
           }
@@ -803,62 +893,6 @@ function process_entity(ResultRow $values, EntityInterface $entity) {
       }
     }
 
-    // The field we are trying to display doesn't exist on this entity.
-    if (!isset($processed_entity->{$this->definition['field_name']})) {
-      return FALSE;
-    }
-
-    // We are supposed to show only certain deltas.
-    if ($this->limit_values && !empty($processed_entity->{$this->definition['field_name']})) {
-      $all_values = !empty($processed_entity->{$this->definition['field_name']}) ? $processed_entity->{$this->definition['field_name']}->getValue() : array();
-      if ($this->options['delta_reversed']) {
-        $all_values = array_reverse($all_values);
-      }
-
-      // Offset is calculated differently when row grouping for a field is
-      // not enabled. Since there are multiple rows, the delta needs to be
-      // taken into account, so that different values are shown per row.
-      if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($values->{$this->aliases['delta']})) {
-        $delta_limit = 1;
-        $offset = $values->{$this->aliases['delta']};
-      }
-      // Single fields don't have a delta available so choose 0.
-      elseif (!$this->options['group_rows'] && !$this->multiple) {
-        $delta_limit = 1;
-        $offset = 0;
-      }
-      else {
-        $delta_limit = $this->options['delta_limit'];
-        $offset = intval($this->options['delta_offset']);
-
-        // We should only get here in this case if there's an offset, and
-        // in that case we're limiting to all values after the offset.
-        if ($delta_limit === 0) {
-          $delta_limit = count($all_values) - $offset;
-        }
-      }
-
-      // Determine if only the first and last values should be shown
-      $delta_first_last = $this->options['delta_first_last'];
-
-      $new_values = array();
-      for ($i = 0; $i < $delta_limit; $i++) {
-        $new_delta = $offset + $i;
-
-        if (isset($all_values[$new_delta])) {
-          // If first-last option was selected, only use the first and last values
-          if (!$delta_first_last
-            // Use the first value.
-            || $new_delta == $offset
-            // Use the last value.
-            || $new_delta == ($delta_limit + $offset - 1)) {
-            $new_values[] = $all_values[$new_delta];
-          }
-        }
-      }
-      $processed_entity->{$this->definition['field_name']} = $new_values;
-    }
-
     return $processed_entity;
   }
 
@@ -897,39 +931,6 @@ protected function addSelfTokens(&$tokens, $item) {
   }
 
   /**
-   * Return the code of the language the field should be displayed in.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object the field value being processed is attached to.
-   * @param \Drupal\views\ResultRow $row
-   *   The result row the field value being processed belongs to.
-   *
-   * @return string
-   *   The field language code.
-   */
-  protected function getFieldLangcode(EntityInterface $entity, ResultRow $row) {
-    if ($this->getFieldDefinition()->isTranslatable()) {
-      // Even if the current field is not attached to the main entity, we use it
-      // to determine the field language, as we assume the same language should
-      // be used for all values belonging to a single row, when possible. Below
-      // we apply language fallback to ensure a valid value is always picked.
-      $langcode = $this->getEntityTranslationRenderer()->getLangcode($row);
-
-      // Give the Entity Field API a chance to fallback to a different language
-      // (or LanguageInterface::LANGCODE_NOT_SPECIFIED), in case the field has
-      // no data for the selected language. FieldItemListInterface::view() does
-      // this as well, but since the returned language code is used before
-      // calling it, the fallback needs to happen explicitly.
-      $langcode = $this->entityManager->getTranslationFromContext($entity, $langcode)->language()->getId();
-
-      return $langcode;
-    }
-    else {
-      return LanguageInterface::LANGCODE_NOT_SPECIFIED;
-    }
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function calculateDependencies() {
@@ -958,9 +959,7 @@ public function isCacheable() {
    * {@inheritdoc}
    */
   public function getCacheContexts() {
-    $contexts = $this->getEntityTranslationRenderer()->getCacheContexts();
-
-    return $contexts;
+    return $this->getEntityFieldRenderer()->getCacheContexts();
   }
 
   /**
@@ -977,11 +976,19 @@ protected function getTableMapping() {
    * {@inheritdoc}
    */
   public function getValue(ResultRow $values, $field = NULL) {
-    if ($field === NULL) {
-      return $this->getEntity($values)->{$this->definition['field_name']}->value;
+    /** @var \Drupal\Core\Field\FieldItemListInterface $field_item_list */
+    $field_item_list = $this->getEntity($values)->{$this->definition['field_name']};
+    $field_item_definition = $field_item_list->getFieldDefinition();
+
+    if ($field_item_definition->getFieldStorageDefinition()->getCardinality() == 1) {
+      return $field ? $field_item_list->$field : $field_item_list->value;
     }
 
-    return $this->getEntity($values)->{$this->definition['field_name']}->$field;
+    $values = [];
+    foreach ($field_item_list as $field_item) {
+      $values[] = $field ? $field_item->$field : $field_item->value;
+    }
+    return $values;
   }
 
 }
diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
index c07a5b8..d9222cd 100644
--- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php
+++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
@@ -8,10 +8,13 @@
 namespace Drupal\views\Plugin\views\style;
 
 use Drupal\Component\Utility\Html;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\views\PluginBase;
+use Drupal\Core\Render\Element;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\Plugin\views\PluginBase;
 use Drupal\views\Plugin\views\wizard\WizardInterface;
+use Drupal\views\ResultRow;
 use Drupal\views\ViewExecutable;
 
 /**
@@ -637,27 +640,120 @@ protected function renderFields(array $result) {
     }
 
     if (!isset($this->rendered_fields)) {
-      $this->rendered_fields = array();
+      $this->rendered_fields = [];
       $this->view->row_index = 0;
-      $keys = array_keys($this->view->field);
+      $field_ids = array_keys($this->view->field);
+
+      // Only tokens preceding the specified one are rendered, so when
+      // generating them we need to pick the last available field.
+      $render_tokens_field_id = end($field_ids);
 
       // If all fields have a field::access FALSE there might be no fields, so
       // there is no reason to execute this code.
-      if (!empty($keys)) {
-        foreach ($result as $count => $row) {
-          $this->view->row_index = $count;
-          foreach ($keys as $id) {
-            $this->rendered_fields[$count][$id] = $this->view->field[$id]->theme($row);
+      if (!empty($field_ids)) {
+        $renderer = $this->getRenderer();
+        $render_cache = $this->getRenderCache();
+        /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache_plugin */
+        $cache_plugin = $this->view->display_handler->getPlugin('cache');
+
+        /** @var \Drupal\views\ResultRow $row */
+        foreach ($result as $index => $row) {
+          $this->view->row_index = $index;
+
+          // Here we implement render caching for result rows. Since we never
+          // build a render array for single rows, given that style templates
+          // need individual field markup to support proper theming, we build
+          // a raw render array containing all field render arrays and cache it,
+          // after manually pre-rendering it and discarding the global markup.
+          // This allow us to cache the markup of the various children, that is
+          // individual fields, which is then available for style template
+          // preprocess functions, later in the rendering workflow.
+          // @todo Fetch all the available cached row items in one single cache
+          //   get operation, once https://www.drupal.org/node/2453945 is fixed.
+          $keys = $cache_plugin->getRowCacheKeys($row);
+          $cache_array = $render_cache->createCacheableRenderArray($keys);
+          $data = $render_cache->get($cache_array);
+
+          if (!$data) {
+            // If we have a cache miss, render field markup via the renderer so
+            // bubbleable metadata can be preserved before caching the result.
+            $data = [
+              '#pre_render' => [[$this, 'elementPreRenderRow']],
+              '#row' => $row,
+            ];
+            // We add pre-bubbling cacheable metadata to the original array to
+            // make cache redirects work when storing the cache entry.
+            $original = $cache_array + $data;
+            $renderer->render($data);
+
+            // We do not need the full markup, just individual fields.
+            $data['#markup'] = '';
+            foreach ($field_ids as $id) {
+              $data[$id] = ['#markup' => $data[$id]['#markup']];
+            }
+
+            // We need to cache also row render tokens, since they rely on data
+            // saved in the field handler objects, which is populated only when
+            // actually rendering field values.
+            $data['#views_render_tokens'] = $this->view->field[$render_tokens_field_id]->getRenderTokens([]);
+
+            // Set cacheable metadata.
+            $data['#cache'] = [
+              'keys' => $cache_array['#cache']['keys'],
+              'contexts' => Cache::mergeContexts($data['#cache']['contexts'], $cache_array['#cache']['contexts']),
+              'tags' => Cache::mergeTags($data['#cache']['tags'], $cache_plugin->getRowCacheTags($row)),
+              'properties' => array_merge($field_ids, ['#views_render_tokens']),
+            ];
+            $renderer->addCacheableDependency($data, $this->view->storage);
+
+            // Manually set the render cache item so we can keep markup for
+            // individual fields.
+            $render_cache->set($data, $original);
           }
+          else {
+            // Render the data to mark strings as safe and push cacheable
+            // metadata to the stack if we are dealing with a cached item.
+            $renderer->render($data);
+          }
+
+          // Extract field output from the render array.
+          $rendered_fields = &$this->rendered_fields[$index];
+          array_walk($field_ids, function($id) use (&$rendered_fields, $data) {
+            $rendered_fields[$id] = $data[$id]['#markup'];
+          });
 
-          $this->rowTokens[$count] = $this->view->field[$id]->getRenderTokens(array());
+          $this->rowTokens[$index] = $data['#views_render_tokens'];
         }
       }
+
       unset($this->view->row_index);
     }
   }
 
   /**
+   * #pre_render callback for view display rendering.
+   *
+   * @see self::render()
+   *
+   * @param array $element
+   *   The element to #pre_render
+   *
+   * @return array
+   *   The processed element.
+   */
+  public function elementPreRenderRow(array $element) {
+    $keys = array_keys($this->view->field);
+    $row = $element['#row'];
+    unset($element['#row']);
+
+    foreach ($keys as $id) {
+      $element[$id] = ['#markup' => $this->view->field[$id]->theme($row)];
+    }
+
+    return $element;
+  }
+
+  /**
    * Gets a rendered field.
    *
    * @param int $index
diff --git a/core/modules/views/src/ResultRow.php b/core/modules/views/src/ResultRow.php
index d93d056..2d1f722 100644
--- a/core/modules/views/src/ResultRow.php
+++ b/core/modules/views/src/ResultRow.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\views;
 
+use Drupal\Core\Cache\Cache;
+
 /**
  * A class representing a view result row.
  */
diff --git a/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php b/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
index 1d83776..6a8a12d 100644
--- a/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
+++ b/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
@@ -211,6 +211,7 @@ protected function checkLanguageRenderers($display, $values) {
    */
   protected function assertTranslations($display, $renderer_id, array $expected, $message = '', $group = 'Other') {
     $view = Views::getView('test_entity_row_renderers');
+    $view->storage->invalidateCaches();
     $view->setDisplay($display);
     $view->getDisplay()->setOption('rendering_language', $renderer_id);
     $view->preview();
diff --git a/core/modules/views/src/Tests/Handler/FieldCounterTest.php b/core/modules/views/src/Tests/Handler/FieldCounterTest.php
index 1290e67..fd17ffd 100644
--- a/core/modules/views/src/Tests/Handler/FieldCounterTest.php
+++ b/core/modules/views/src/Tests/Handler/FieldCounterTest.php
@@ -57,6 +57,7 @@ function testSimple() {
     $counter = $view->style_plugin->getField(2, 'counter');
     $this->assertEqual($counter, 3, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 3, '@counter' => $counter)));
     $view->destroy();
+    $view->storage->invalidateCaches();
 
     $view->setDisplay();
     $rand_start = rand(5, 10);
diff --git a/core/modules/views/src/Tests/Handler/FieldFieldTest.php b/core/modules/views/src/Tests/Handler/FieldFieldTest.php
index 4982cdd..942379b 100644
--- a/core/modules/views/src/Tests/Handler/FieldFieldTest.php
+++ b/core/modules/views/src/Tests/Handler/FieldFieldTest.php
@@ -7,10 +7,12 @@
 
 namespace Drupal\views\Tests\Handler;
 
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\entity_test\Entity\EntityTestRev;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\user\Entity\User;
 use Drupal\views\Plugin\views\field\Field;
 use Drupal\views\Tests\ViewUnitTestBase;
 use Drupal\views\Views;
@@ -31,7 +33,7 @@ class FieldFieldTest extends ViewUnitTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $testViews = ['test_field_field_test', 'test_field_field_revision_test'];
+  public static $testViews = ['test_field_field_test', 'test_field_alias_test', 'test_field_field_complex_test', 'test_field_field_revision_test', 'test_field_field_revision_complex_test'];
 
   /**
    * The stored test entities.
@@ -48,14 +50,43 @@ class FieldFieldTest extends ViewUnitTestBase {
   protected $entityRevision;
 
   /**
+   * Stores a couple of test users.
+   *
+   * @var \Drupal\user\UserInterface[]
+   */
+  protected $testUsers;
+
+  /**
+   * The admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
 
     $this->installEntitySchema('entity_test');
+    $this->installEntitySchema('user');
     $this->installEntitySchema('entity_test_rev');
 
+    // Bypass any field access.
+    $this->adminUser = User::create();
+    $this->adminUser->save();
+    $this->container->get('current_user')->setAccount($this->adminUser);
+
+    $this->testUsers = [];
+    for ($i = 0; $i < 5; $i++) {
+      $this->testUsers[$i] = User::create([
+        'name' => 'test ' . $i,
+        'timezone' => User::getAllowedTimezones()[$i],
+      ]);
+      $this->testUsers[$i]->save();
+    }
+
     // Setup a field storage and field, but also change the views data for the
     // entity_test entity type.
     $field_storage = FieldStorageConfig::create([
@@ -72,11 +103,30 @@ protected function setUp() {
     ]);
     $field->save();
 
+    $field_storage_multiple = FieldStorageConfig::create([
+      'field_name' => 'field_test_multiple',
+      'type' => 'integer',
+      'entity_type' => 'entity_test',
+      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+    ]);
+    $field_storage_multiple->save();
+
+    $field_multiple = FieldConfig::create([
+      'field_name' => 'field_test_multiple',
+      'entity_type' => 'entity_test',
+      'bundle' => 'entity_test',
+    ]);
+    $field_multiple->save();
+
     $random_number = (string) 30856;
+    $random_number_multiple = (string) 1370359990;
     for ($i = 0; $i < 5; $i++) {
       $this->entities[$i] = $entity = EntityTest::create([
         'bundle' => 'entity_test',
+        'name' => 'test ' . $i,
         'field_test' => $random_number[$i],
+        'field_test_multiple' => [$random_number_multiple[$i * 2], $random_number_multiple[$i * 2 + 1]],
+        'user_id' => $this->testUsers[$i]->id(),
       ]);
       $entity->save();
     }
@@ -97,30 +147,58 @@ protected function setUp() {
     ]);
     $field->save();
 
+    $field_storage_multiple = FieldStorageConfig::create([
+      'field_name' => 'field_test_multiple',
+      'type' => 'integer',
+      'entity_type' => 'entity_test_rev',
+      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+    ]);
+    $field_storage_multiple->save();
+
+    $field_multiple = FieldConfig::create([
+      'field_name' => 'field_test_multiple',
+      'entity_type' => 'entity_test_rev',
+      'bundle' => 'entity_test_rev',
+    ]);
+    $field_multiple->save();
+
     $this->entityRevision = [];
     $this->entityRevision[0] = $entity = EntityTestRev::create([
       'name' => 'base value',
       'field_test' => 1,
+      'field_test_multiple' => [1, 3, 7],
+      'user_id' =>  $this->testUsers[0]->id(),
     ]);
     $entity->save();
+    $original_entity = clone $entity;
 
-    $entity = clone $entity;
+    $entity = clone $original_entity;
     $entity->setNewRevision(TRUE);
     $entity->name->value = 'revision value1';
     $entity->field_test->value = 2;
+    $entity->field_test_multiple[0]->value = 0;
+    $entity->field_test_multiple[1]->value = 3;
+    $entity->field_test_multiple[2]->value = 5;
+    $entity->user_id->target_id = $this->testUsers[1]->id();
     $entity->save();
     $this->entityRevision[1] = $entity;
 
-    $entity = clone $entity;
+    $entity = clone $original_entity;
     $entity->setNewRevision(TRUE);
     $entity->name->value = 'revision value2';
     $entity->field_test->value = 3;
+    $entity->field_test_multiple[0]->value = 9;
+    $entity->field_test_multiple[1]->value = 9;
+    $entity->field_test_multiple[2]->value = 9;
+    $entity->user_id->target_id = $this->testUsers[2]->id();
     $entity->save();
     $this->entityRevision[2] = $entity;
 
-    $this->entityRevision[0] = $entity = EntityTestRev::create([
+    $this->entityRevision[3] = $entity = EntityTestRev::create([
       'name' => 'next entity value',
       'field_test' => 4,
+      'field_test_multiple' => [2, 9, 9],
+      'user_id' => $this->testUsers[3]->id(),
     ]);
     $entity->save();
 
@@ -186,6 +264,114 @@ public function testSimpleRender() {
   }
 
   /**
+   * Tests the result of a view with complex field configuration.
+   *
+   * A complex field configuration contains multiple times the same field, with
+   * different delta limit / offset.
+   */
+  public function testFieldAlias() {
+    $executable = Views::getView('test_field_alias_test');
+    $executable->execute();
+
+    $this->assertTrue($executable->field['id'] instanceof Field);
+    $this->assertTrue($executable->field['name'] instanceof Field);
+    $this->assertTrue($executable->field['name_alias'] instanceof Field);
+
+    $this->assertIdenticalResultset($executable,
+      [
+        ['id' => 1, 'name' => 'test 0', 'name_alias' => 'test 0'],
+        ['id' => 2, 'name' => 'test 1', 'name_alias' => 'test 1'],
+        ['id' => 3, 'name' => 'test 2', 'name_alias' => 'test 2'],
+        ['id' => 4, 'name' => 'test 3', 'name_alias' => 'test 3'],
+        ['id' => 5, 'name' => 'test 4', 'name_alias' => 'test 4'],
+      ],
+      ['id' => 'id', 'name' => 'name', 'name_alias' => 'name_alias']
+    );
+  }
+
+  /**
+   * Tests the result of a view with complex field configuration.
+   *
+   * A complex field configuration contains multiple times the same field, with
+   * different delta limit / offset.
+   */
+  public function testFieldAliasRender() {
+    $executable = Views::getView('test_field_alias_test');
+    $executable->execute();
+
+    for ($i = 0; $i < 5; $i++) {
+      $this->assertEqual($i + 1, $executable->getStyle()->getField($i, 'id'));
+      $this->assertEqual('test ' . $i, $executable->getStyle()->getField($i, 'name'));
+      $this->assertEqual('<a href="' . EntityTest::load($i + 1)->url() . '">test ' . $i . '</a>', $executable->getStyle()->getField($i, 'name_alias'));
+    }
+  }
+
+  /**
+   * Tests the result of a view with complex field configuration.
+   *
+   * A complex field configuration contains multiple times the same field, with
+   * different delta limit / offset.
+   */
+  public function testComplexExecute() {
+    $executable = Views::getView('test_field_field_complex_test');
+    $executable->execute();
+
+    $timezones = [];
+    foreach ($this->testUsers as $user) {
+      $timezones[] = $user->getTimeZone();
+    }
+
+    $this->assertTrue($executable->field['field_test_multiple'] instanceof Field);
+    $this->assertTrue($executable->field['field_test_multiple_1'] instanceof Field);
+    $this->assertTrue($executable->field['field_test_multiple_2'] instanceof Field);
+    $this->assertTrue($executable->field['timezone'] instanceof Field);
+
+    $this->assertIdenticalResultset($executable,
+      [
+        ['timezone' => $timezones[0], 'field_test_multiple' => [1, 3], 'field_test_multiple_1' => [1, 3], 'field_test_multiple_2' => [1, 3]],
+        ['timezone' => $timezones[1], 'field_test_multiple' => [7, 0], 'field_test_multiple_1' => [7, 0], 'field_test_multiple_2' => [7, 0]],
+        ['timezone' => $timezones[2], 'field_test_multiple' => [3, 5], 'field_test_multiple_1' => [3, 5], 'field_test_multiple_2' => [3, 5]],
+        ['timezone' => $timezones[3], 'field_test_multiple' => [9, 9], 'field_test_multiple_1' => [9, 9], 'field_test_multiple_2' => [9, 9]],
+        ['timezone' => $timezones[4], 'field_test_multiple' => [9, 0], 'field_test_multiple_1' => [9, 0], 'field_test_multiple_2' => [9, 0]],
+      ],
+      ['timezone' => 'timezone', 'field_test_multiple' => 'field_test_multiple', 'field_test_multiple_1' => 'field_test_multiple_1', 'field_test_multiple_2' => 'field_test_multiple_2']
+    );
+  }
+
+  /**
+   * Tests the output of a view with complex field configuration.
+   */
+  public function testComplexRender() {
+    $executable = Views::getView('test_field_field_complex_test');
+    $executable->execute();
+
+    $this->assertEqual($this->testUsers[0]->getTimeZone(), $executable->getStyle()->getField(0, 'timezone'));
+    $this->assertEqual("1, 3", $executable->getStyle()->getField(0, 'field_test_multiple'));
+    $this->assertEqual("1", $executable->getStyle()->getField(0, 'field_test_multiple_1'));
+    $this->assertEqual("3", $executable->getStyle()->getField(0, 'field_test_multiple_2'));
+
+    $this->assertEqual($this->testUsers[1]->getTimeZone(), $executable->getStyle()->getField(1, 'timezone'));
+    $this->assertEqual("7, 0", $executable->getStyle()->getField(1, 'field_test_multiple'));
+    $this->assertEqual("7", $executable->getStyle()->getField(1, 'field_test_multiple_1'));
+    $this->assertEqual("0", $executable->getStyle()->getField(1, 'field_test_multiple_2'));
+
+    $this->assertEqual($this->testUsers[2]->getTimeZone(), $executable->getStyle()->getField(2, 'timezone'));
+    $this->assertEqual("3, 5", $executable->getStyle()->getField(2, 'field_test_multiple'));
+    $this->assertEqual("3", $executable->getStyle()->getField(2, 'field_test_multiple_1'));
+    $this->assertEqual("5", $executable->getStyle()->getField(2, 'field_test_multiple_2'));
+
+    $this->assertEqual($this->testUsers[3]->getTimeZone(), $executable->getStyle()->getField(3, 'timezone'));
+    $this->assertEqual("9, 9", $executable->getStyle()->getField(3, 'field_test_multiple'));
+    $this->assertEqual("9", $executable->getStyle()->getField(3, 'field_test_multiple_1'));
+    $this->assertEqual("9", $executable->getStyle()->getField(3, 'field_test_multiple_2'));
+
+    $this->assertEqual($this->testUsers[4]->getTimeZone(), $executable->getStyle()->getField(4, 'timezone'));
+    $this->assertEqual("9, 0", $executable->getStyle()->getField(4, 'field_test_multiple'));
+    $this->assertEqual("9", $executable->getStyle()->getField(4, 'field_test_multiple_1'));
+    $this->assertEqual("0", $executable->getStyle()->getField(4, 'field_test_multiple_2'));
+  }
+
+  /**
    * Tests the revision result.
    */
   public function testRevisionExecute() {
@@ -207,7 +393,7 @@ public function testRevisionExecute() {
   }
 
   /**
-   * Tests the output of a revision view with base fields and configurable fields.
+   * Tests the output of a revision view with base and configurable fields.
    */
   public function testRevisionRender() {
     $executable = Views::getView('test_field_field_revision_test');
@@ -232,7 +418,93 @@ public function testRevisionRender() {
     $this->assertEqual(4, $executable->getStyle()->getField(3, 'revision_id'));
     $this->assertEqual(4, $executable->getStyle()->getField(3, 'field_test'));
     $this->assertEqual('next entity value', $executable->getStyle()->getField(3, 'name'));
+  }
+
+  /**
+   * Tests the result set of a complex revision view.
+   */
+  public function testRevisionComplexExecute() {
+    $executable = Views::getView('test_field_field_revision_complex_test');
+    $executable->execute();
+
+    $timezones = [];
+    foreach ($this->testUsers as $user) {
+      $timezones[] = $user->getTimeZone();
+    }
+
+    $this->assertTrue($executable->field['id'] instanceof Field);
+    $this->assertTrue($executable->field['revision_id'] instanceof Field);
+    $this->assertTrue($executable->field['timezone'] instanceof Field);
+    $this->assertTrue($executable->field['field_test_multiple'] instanceof Field);
+    $this->assertTrue($executable->field['field_test_multiple_1'] instanceof Field);
+    $this->assertTrue($executable->field['field_test_multiple_2'] instanceof Field);
+
+    $this->assertIdenticalResultset($executable,
+      [
+        ['id' => 1, 'field_test' => 1, 'revision_id' => 1, 'uid' => $this->testUsers[0]->id(), 'timezone' => $timezones[0], 'field_test_multiple' => [1, 3, 7], 'field_test_multiple_1' => [1, 3, 7], 'field_test_multiple_2' => [1, 3, 7]],
+        ['id' => 1, 'field_test' => 2, 'revision_id' => 2, 'uid' => $this->testUsers[1]->id(), 'timezone' => $timezones[1], 'field_test_multiple' => [0, 3, 5], 'field_test_multiple_1' => [0, 3, 5], 'field_test_multiple_2' => [0, 3, 5]],
+        ['id' => 1, 'field_test' => 3, 'revision_id' => 3, 'uid' => $this->testUsers[2]->id(), 'timezone' => $timezones[2], 'field_test_multiple' => [9, 9, 9], 'field_test_multiple_1' => [9, 9, 9], 'field_test_multiple_2' => [9, 9, 9]],
+        ['id' => 2, 'field_test' => 4, 'revision_id' => 4, 'uid' => $this->testUsers[3]->id(), 'timezone' => $timezones[3], 'field_test_multiple' => [2, 9, 9], 'field_test_multiple_1' => [2, 9, 9], 'field_test_multiple_2' => [2, 9, 9]],
+      ],
+      ['entity_test_rev_revision_id' => 'id', 'revision_id' => 'revision_id', 'users_field_data_entity_test_rev_revision_uid' => 'uid', 'timezone' => 'timezone', 'field_test_multiple' => 'field_test_multiple', 'field_test_multiple_1' => 'field_test_multiple_1', 'field_test_multiple_2' => 'field_test_multiple_2']
+    );
+  }
+
+  /**
+   * Tests the output of a revision view with base fields and configurable fields.
+   */
+  public function testRevisionComplexRender() {
+    $executable = Views::getView('test_field_field_revision_complex_test');
+    $executable->execute();
+
+    $this->assertEqual(1, $executable->getStyle()->getField(0, 'id'));
+    $this->assertEqual(1, $executable->getStyle()->getField(0, 'revision_id'));
+    $this->assertEqual($this->testUsers[0]->getTimeZone(), $executable->getStyle()->getField(0, 'timezone'));
+    $this->assertEqual('1, 3, 7', $executable->getStyle()->getField(0, 'field_test_multiple'));
+    $this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test_multiple_1'));
+    $this->assertEqual('3, 7', $executable->getStyle()->getField(0, 'field_test_multiple_2'));
+
+    $this->assertEqual(1, $executable->getStyle()->getField(1, 'id'));
+    $this->assertEqual(2, $executable->getStyle()->getField(1, 'revision_id'));
+    $this->assertEqual($this->testUsers[1]->getTimeZone(), $executable->getStyle()->getField(1, 'timezone'));
+    $this->assertEqual('0, 3, 5', $executable->getStyle()->getField(1, 'field_test_multiple'));
+    $this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test_multiple_1'));
+    $this->assertEqual('3, 5', $executable->getStyle()->getField(1, 'field_test_multiple_2'));
+
+    $this->assertEqual(1, $executable->getStyle()->getField(2, 'id'));
+    $this->assertEqual(3, $executable->getStyle()->getField(2, 'revision_id'));
+    $this->assertEqual($this->testUsers[2]->getTimeZone(), $executable->getStyle()->getField(2, 'timezone'));
+    $this->assertEqual('9, 9, 9', $executable->getStyle()->getField(2, 'field_test_multiple'));
+    $this->assertEqual('9', $executable->getStyle()->getField(2, 'field_test_multiple_1'));
+    $this->assertEqual('9, 9', $executable->getStyle()->getField(2, 'field_test_multiple_2'));
+
+    $this->assertEqual(2, $executable->getStyle()->getField(3, 'id'));
+    $this->assertEqual(4, $executable->getStyle()->getField(3, 'revision_id'));
+    $this->assertEqual($this->testUsers[3]->getTimeZone(), $executable->getStyle()->getField(3, 'timezone'));
+    $this->assertEqual('2, 9, 9', $executable->getStyle()->getField(3, 'field_test_multiple'));
+    $this->assertEqual('2', $executable->getStyle()->getField(3, 'field_test_multiple_1'));
+    $this->assertEqual('9, 9', $executable->getStyle()->getField(3, 'field_test_multiple_2'));
+  }
+
+  /**
+   * Tests that a field not available for every bundle is rendered as empty.
+   */
+  public function testMissingBundleFieldRender() {
+    // Create a new bundle not having the test field attached.
+    $bundle = $this->randomMachineName();
+    entity_test_create_bundle($bundle);
+
+    $entity = EntityTest::create([
+      'type' => $bundle,
+      'name' => $this->randomString(),
+      'user_id' => $this->testUsers[0]->id(),
+    ]);
+    $entity->save();
+
+    $executable = Views::getView('test_field_field_test');
+    $executable->execute();
 
+    $this->assertIdentical('', $executable->getStyle()->getField(1, 'field_test'));
   }
 
 }
diff --git a/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php b/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
index a5f18d2..2aa0e9c 100644
--- a/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
+++ b/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
@@ -85,8 +85,13 @@ public function testGroupRows() {
 
     // Test ungrouped rows.
     $this->executeView($view);
+    $view->render();
+
+    $view->row_index = 0;
     $this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[0]), 'a');
+    $view->row_index = 1;
     $this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[1]), 'b');
+    $view->row_index = 2;
     $this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[2]), 'c');
   }
 
diff --git a/core/modules/views/src/Tests/QueryGroupByTest.php b/core/modules/views/src/Tests/QueryGroupByTest.php
index b580e0a..e17ceba 100644
--- a/core/modules/views/src/Tests/QueryGroupByTest.php
+++ b/core/modules/views/src/Tests/QueryGroupByTest.php
@@ -7,6 +7,11 @@
 
 namespace Drupal\views\Tests;
 
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\entity_test\Entity\EntityTestMul;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\views\Views;
 
 /**
@@ -21,14 +26,14 @@ class QueryGroupByTest extends ViewUnitTestBase {
    *
    * @var array
    */
-  public static $testViews = array('test_group_by_in_filters', 'test_aggregate_count', 'test_group_by_count');
+  public static $testViews = array('test_group_by_in_filters', 'test_aggregate_count', 'test_group_by_count', 'test_group_by_count_multicardinality');
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = array('entity_test', 'system', 'field', 'user');
+  public static $modules = array('entity_test', 'system', 'field', 'user', 'language');
 
   /**
    * The storage for the test entity type.
@@ -45,8 +50,11 @@ protected function setUp() {
 
     $this->installEntitySchema('user');
     $this->installEntitySchema('entity_test');
+    $this->installEntitySchema('entity_test_mul');
 
     $this->storage = $this->container->get('entity.manager')->getStorage('entity_test');
+
+    ConfigurableLanguage::createFromLangcode('it')->save();
   }
 
 
@@ -205,4 +213,84 @@ public function testGroupByBaseField() {
     $this->assertTrue(strpos($view->build_info['query'], 'GROUP BY entity_test.id'), 'GROUP BY field includes the base table name when grouping on the base field.');
   }
 
+  /**
+   * Tests grouping a field with cardinality > 1.
+   */
+  public function testGroupByFieldWithCardinality() {
+    $field_storage = FieldStorageConfig::create([
+      'type' => 'integer',
+      'field_name' => 'field_test',
+      'cardinality' => 4,
+      'entity_type' => 'entity_test_mul',
+    ]);
+    $field_storage->save();
+    $field = FieldConfig::create([
+      'field_name' => 'field_test',
+      'entity_type' => 'entity_test_mul',
+      'bundle' => 'entity_test_mul',
+    ]);
+    $field->save();
+
+    $entities = [];
+    $entity = EntityTestMul::create([
+      'field_test' => [1, 1, 1],
+    ]);
+    $entity->save();
+    $entities[] = $entity;
+
+    $entity = EntityTestMul::create([
+      'field_test' => [2, 2, 2],
+    ]);
+    $entity->save();
+    $entities[] = $entity;
+
+    $entity = EntityTestMul::create([
+      'field_test' => [2, 2, 2],
+    ]);
+    $entity->save();
+    $entities[] = $entity;
+
+    $view = Views::getView('test_group_by_count_multicardinality');
+    $this->executeView($view);
+    $this->assertEqual(2, count($view->result));
+
+    $this->assertEqual(3, $view->getStyle()->getField(0, 'id'));
+    $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test'));
+    $this->assertEqual(6, $view->getStyle()->getField(1, 'id'));
+    $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test'));
+
+    $entities[2]->field_test[0]->value = 3;
+    $entities[2]->field_test[1]->value = 4;
+    $entities[2]->field_test[2]->value = 5;
+    $entities[2]->save();
+
+    $view = Views::getView('test_group_by_count_multicardinality');
+    $this->executeView($view);
+    $this->assertEqual(5, count($view->result));
+
+    $this->assertEqual(3, $view->getStyle()->getField(0, 'id'));
+    $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test'));
+    $this->assertEqual(3, $view->getStyle()->getField(1, 'id'));
+    $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test'));
+    $this->assertEqual(1, $view->getStyle()->getField(2, 'id'));
+    $this->assertEqual('3', $view->getStyle()->getField(2, 'field_test'));
+    $this->assertEqual(1, $view->getStyle()->getField(3, 'id'));
+    $this->assertEqual('4', $view->getStyle()->getField(3, 'field_test'));
+    $this->assertEqual(1, $view->getStyle()->getField(4, 'id'));
+    $this->assertEqual('5', $view->getStyle()->getField(4, 'field_test'));
+
+    // Check that translated values are correctly retrieved and are not grouped
+    // into the original entity.
+    $translation = $entity->addTranslation('it');
+    $translation->field_test = [6, 6, 6];
+    $translation->save();
+
+    $view = Views::getView('test_group_by_count_multicardinality');
+    $this->executeView($view);
+
+    $this->assertEqual(6, count($view->result));
+    $this->assertEqual(3, $view->getStyle()->getField(5, 'id'));
+    $this->assertEqual('6', $view->getStyle()->getField(5, 'field_test'));
+  }
+
 }
diff --git a/core/modules/views/src/Tests/ViewResultAssertionTrait.php b/core/modules/views/src/Tests/ViewResultAssertionTrait.php
index b8ad34f..f88c8c5 100644
--- a/core/modules/views/src/Tests/ViewResultAssertionTrait.php
+++ b/core/modules/views/src/Tests/ViewResultAssertionTrait.php
@@ -113,7 +113,18 @@ protected function assertIdenticalResultsetHelper($view, $expected_result, $colu
       $row = array();
       foreach ($column_map as $expected_column) {
         // The comparison will be done on the string representation of the value.
-        $row[$expected_column] = (string) (is_object($value) ? $value->$expected_column : $value[$expected_column]);
+        if (is_object($value)) {
+          $row[$expected_column] = (string) $value->$expected_column;
+        }
+        // This case is about fields with multiple values.
+        elseif (is_array($value[$expected_column])) {
+          foreach (array_keys($value[$expected_column]) as $delta) {
+            $row[$expected_column][$delta] = (string) $value[$expected_column][$delta];
+          }
+        }
+        else {
+          $row[$expected_column] = (string) $value[$expected_column];
+        }
       }
       $expected_result[$key] = $row;
     }
@@ -140,4 +151,3 @@ protected function assertIdenticalResultsetHelper($view, $expected_result, $colu
   }
 
 }
-
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_alias_test.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_alias_test.yml
new file mode 100644
index 0000000..6e178cc
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_alias_test.yml
@@ -0,0 +1,64 @@
+langcode: und
+status: true
+dependencies: {  }
+id: test_field_alias_test
+module: views
+description: ''
+tag: ''
+base_table: entity_test
+base_field: id
+core: '8'
+display:
+  default:
+    display_options:
+      access:
+        type: none
+      cache:
+        type: none
+      fields:
+        id:
+          id: id
+          table: entity_test
+          field: id
+          plugin_id: field
+          entity_type: entity_test
+          entity_field: id
+        name:
+          id: name
+          table: entity_test
+          field: name
+          plugin_id: field
+          entity_type: entity_test
+          entity_field: name
+          type: string
+          settings:
+            link_to_entity: false
+        name_alias:
+          id: name_alias
+          table: entity_test
+          field: name_alias
+          plugin_id: field
+          entity_type: entity_test
+          entity_field: name
+          type: string
+          settings:
+            link_to_entity: true
+      relationships:
+        user_id:
+          table: entity_test
+          field: user_id
+          id: user_id
+          plugin_id: standard
+      sorts:
+        id:
+          id: id
+          table: entity_test
+          field: id
+          plugin_id: standard
+          order: asc
+      style:
+        type: html_list
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: 0
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_complex_test.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_complex_test.yml
new file mode 100644
index 0000000..58ee430
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_complex_test.yml
@@ -0,0 +1,79 @@
+langcode: und
+status: true
+dependencies: {  }
+id: test_field_field_complex_test
+module: views
+description: ''
+tag: ''
+base_table: entity_test
+base_field: id
+core: '8'
+display:
+  default:
+    display_options:
+      access:
+        type: none
+      cache:
+        type: none
+      fields:
+        id:
+          id: id
+          table: entity_test
+          field: id
+          plugin_id: field
+          entity_type: entity_test
+          entity_field: id
+        field_test_multiple:
+          id: field_test_multiple
+          table: entity_test__field_test_multiple
+          field: field_test_multiple
+          plugin_id: field
+          entity_type: entity_test
+          entity_field: field_test_multiple
+          delta_limit: 0
+          group_rows: true
+        field_test_multiple_1:
+          id: field_test_multiple_1
+          table: entity_test__field_test_multiple
+          field: field_test_multiple
+          plugin_id: field
+          entity_type: entity_test
+          entity_field: field_test_multiple
+          delta_limit: 1
+          group_rows: true
+        field_test_multiple_2:
+          id: field_test_multiple_2
+          table: entity_test__field_test_multiple
+          field: field_test_multiple
+          plugin_id: field
+          entity_type: entity_test
+          entity_field: field_test_multiple
+          delta_limit: 0
+          delta_offset: 1
+          group_rows: true
+        timezone:
+          id: timezone
+          table: users_field_data
+          field: timezone
+          plugin_id: field
+          relationship: user_id
+          alter: {}
+      relationships:
+        user_id:
+          table: entity_test
+          field: user_id
+          id: user_id
+          plugin_id: standard
+      sorts:
+        id:
+          id: id
+          table: entity_test
+          field: id
+          plugin_id: standard
+          order: asc
+      style:
+        type: html_list
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: 0
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_test.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_complex_test.yml
similarity index 50%
copy from core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_test.yml
copy to core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_complex_test.yml
index 5a49a08..b0122a3 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_test.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_complex_test.yml
@@ -1,7 +1,7 @@
 langcode: und
 status: true
 dependencies: {  }
-id: test_field_field_revision_test
+id: test_field_field_revision_complex_test
 module: views
 description: ''
 tag: ''
@@ -30,20 +30,47 @@ display:
           plugin_id: field
           entity_type: entity_test_rev
           entity_field: revision_id
-        field_test:
-          id: field_test
-          table: entity_test__field_test
-          field: field_test
+        field_test_multiple:
+          id: field_test_multiple
+          table: entity_test_rev__field_test_multiple
+          field: field_test_multiple
           plugin_id: field
           entity_type: entity_test_rev
-          entity_field: field_test
-        name:
-          id: name
-          table: entity_test_rev_revision
-          field: name
+          entity_field: field_test_multiple
+          delta_limit: 0
+          group_rows: true
+        field_test_multiple_1:
+          id: field_test_multiple_1
+          table: entity_test_rev__field_test_multiple
+          field: field_test_multiple
+          plugin_id: field
+          entity_type: entity_test_rev
+          entity_field: field_test_multiple
+          delta_limit: 1
+          group_rows: true
+        field_test_multiple_2:
+          id: field_test_multiple_2
+          table: entity_test_rev__field_test_multiple
+          field: field_test_multiple
           plugin_id: field
           entity_type: entity_test_rev
-          entity_field: name
+          entity_field: field_test_multiple
+          delta_limit: 0
+          delta_offset: 1
+          group_rows: true
+        timezone:
+          id: timezone
+          table: users_field_data
+          field: timezone
+          plugin_id: field
+          relationship: user_id
+          alter: {}
+      relationships:
+        user_id:
+          table: entity_test_rev_revision
+          field: user_id
+          id: user_id
+          plugin_id: standard
       sorts:
         revision_id:
           id: revision_id
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_test.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_test.yml
index 5a49a08..d85d4d1 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_test.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_field_revision_test.yml
@@ -32,7 +32,7 @@ display:
           entity_field: revision_id
         field_test:
           id: field_test
-          table: entity_test__field_test
+          table: entity_test_rev__field_test
           field: field_test
           plugin_id: field
           entity_type: entity_test_rev
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_by_count_multicardinality.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_by_count_multicardinality.yml
new file mode 100644
index 0000000..9020ee1
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_by_count_multicardinality.yml
@@ -0,0 +1,80 @@
+langcode: en
+status: true
+dependencies: {  }
+id: test_group_by_count_multicardinality
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: entity_test_mul_property_data
+base_field: nid
+core: '8'
+display:
+  default:
+    display_options:
+      access:
+        type: none
+      cache:
+        type: none
+      exposed_form:
+        type: basic
+      fields:
+        id:
+          alter:
+            alter_text: false
+            ellipsis: true
+            html: false
+            make_link: false
+            strip_tags: false
+            trim: false
+            word_boundary: true
+          empty_zero: false
+          field: id
+          group_type: count
+          hide_empty: false
+          id: id
+          table: entity_test_mul_property_data
+          plugin_id: field
+          entity_type: entity_test_mul
+          entity_field: id
+          plugin_id: field
+        field_test:
+          alter:
+            alter_text: false
+            ellipsis: true
+            html: false
+            make_link: false
+            strip_tags: false
+            trim: false
+            word_boundary: true
+          group_type: group
+          group_column: value
+          empty_zero: false
+          field: field_test
+          hide_empty: false
+          id: field_test
+          table: entity_test_mul__field_test
+          entity_type: entity_test_mul
+          entity_field: field_test
+          plugin_id: field
+      sorts:
+        field_test_value:
+          table: entity_test__field_test
+          field: field_test
+          id: field_ttest_value
+          entity_type: entity_test_mul
+          entity_field: field_test
+          group_type: group
+          order: ASC
+          plugin_id: standard
+      group_by: true
+      pager:
+        type: some
+      style:
+        type: default
+      row:
+        type: fields
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: 0
diff --git a/core/modules/views/tests/modules/views_test_render_cache/views_test_render_cache.info.yml b/core/modules/views/tests/modules/views_test_render_cache/views_test_render_cache.info.yml
new file mode 100644
index 0000000..1fd4a9d
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_render_cache/views_test_render_cache.info.yml
@@ -0,0 +1,9 @@
+name: 'Views Test Render Cache'
+type: module
+description: 'Performs query alterations to help testing render cached views.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+  - views
+  - node
diff --git a/core/modules/views/tests/modules/views_test_render_cache/views_test_render_cache.module b/core/modules/views/tests/modules/views_test_render_cache/views_test_render_cache.module
new file mode 100644
index 0000000..f6b99f0
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_render_cache/views_test_render_cache.module
@@ -0,0 +1,7 @@
+<?php
+
+/**
+ * @file
+ * Contains the Views Test Render Cache module.
+ */
+
diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
index 9a04103..0e45537 100644
--- a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
@@ -492,12 +492,12 @@ public function providerTestRenderAsLinkWithPathAndTokens() {
   /**
    * Sets up a test field.
    *
-   * @return \Drupal\Tests\views\Unit\Plugin\field\TestField|\PHPUnit_Framework_MockObject_MockObject
+   * @return \Drupal\Tests\views\Unit\Plugin\field\FieldPluginBaseTestField|\PHPUnit_Framework_MockObject_MockObject
    *   The test field.
    */
   protected function setupTestField(array $options = []) {
-    /** @var \Drupal\Tests\views\Unit\Plugin\field\TestField $field */
-    $field = $this->getMock('Drupal\Tests\views\Unit\Plugin\field\TestField', ['l'], [$this->configuration, $this->pluginId, $this->pluginDefinition]);
+    /** @var \Drupal\Tests\views\Unit\Plugin\field\FieldPluginBaseTestField $field */
+    $field = $this->getMock('Drupal\Tests\views\Unit\Plugin\field\FieldPluginBaseTestField', ['l'], [$this->configuration, $this->pluginId, $this->pluginDefinition]);
     $field->init($this->executable, $this->display, $options);
     $field->setLinkGenerator($this->linkGenerator);
 
@@ -506,7 +506,7 @@ protected function setupTestField(array $options = []) {
 
 }
 
-class TestField extends FieldPluginBase {
+class FieldPluginBaseTestField extends FieldPluginBase {
 
   public function setLinkGenerator(LinkGeneratorInterface $link_generator) {
     $this->linkGenerator = $link_generator;
diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php
index 9ca73d4..90bd2cc 100644
--- a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php
@@ -7,9 +7,11 @@
 
 namespace Drupal\Tests\views\Unit\Plugin\field;
 
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Tests\UnitTestCase;
 use Drupal\Tests\views\Unit\Plugin\HandlerTestTrait;
 use Drupal\views\Plugin\views\field\Field;
+use Drupal\views\ResultRow;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 /**
@@ -438,6 +440,7 @@ public function testQueryWithGroupByForBaseField() {
     ];
     $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
+    $handler->view->field = [$handler];
 
     $this->setupLanguageRenderer($handler, $definition);
 
@@ -499,6 +502,7 @@ public function testQueryWithGroupByForConfigField() {
     ];
     $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
+    $handler->view->field = [$handler];
 
     $this->setupLanguageRenderer($handler, $definition);
 
@@ -551,6 +555,85 @@ public function testQueryWithGroupByForConfigField() {
   }
 
   /**
+   * @covers ::prepareItemsByDelta
+   *
+   * @dataProvider providerTestPrepareItemsByDelta
+   */
+  public function testPrepareItemsByDelta(array $options, array $expected_values) {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'integer',
+    ];
+    $handler = new FieldTestField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler->view = $this->executable;
+    $handler->view->field = [$handler];
+
+    $this->setupLanguageRenderer($handler, $definition);
+
+    $field_storage = $this->getConfigFieldStorage();
+    $field_storage->expects($this->any())
+      ->method('getCardinality')
+      ->willReturn(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
+
+    $this->entityManager->expects($this->any())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'integer' => $field_storage,
+      ]);
+
+    $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
+    $table_mapping
+      ->expects($this->any())
+      ->method('getFieldColumnName')
+      ->with($field_storage, 'value')
+      ->willReturn('integer_value');
+    $entity_storage = $this->getMock('Drupal\Core\Entity\Sql\SqlEntityStorageInterface');
+    $entity_storage->expects($this->any())
+      ->method('getTableMapping')
+      ->willReturn($table_mapping);
+    $this->entityManager->expects($this->any())
+      ->method('getStorage')
+      ->with('test_entity')
+      ->willReturn($entity_storage);
+
+    $options = [
+      'group_column' => 'value',
+      'group_columns' => [],
+      'table' => 'test_entity__integer',
+    ] + $options;
+    $handler->init($this->executable, $this->display, $options);
+
+    $this->executable->row_index = 0;
+    $this->executable->result = [0 => new ResultRow([])];
+
+    $items = [3, 1, 4, 1, 5, 9];
+    $this->assertEquals($expected_values, $handler->executePrepareItemsByDelta($items));
+  }
+
+  /**
+   * Provides test data for testPrepareItemsByDelta().
+   */
+  public function providerTestPrepareItemsByDelta() {
+    $data = [];
+
+    // Let's display all values.
+    $data[] = [[], [3, 1, 4, 1, 5, 9]];
+    // Test just reversed deltas.
+    $data[] = [['delta_reversed' => TRUE], [9, 5, 1, 4, 1, 3]];
+
+    // Test combinations of delta limit, offset and first_last.
+    $data[] = [['group_rows' => TRUE, 'delta_limit' => 3], [3, 1, 4]];
+    $data[] = [['group_rows' => TRUE, 'delta_limit' => 3, 'delta_offset' => 2], [4, 1, 5]];
+    $data[] = [['group_rows' => TRUE, 'delta_reversed' => TRUE, 'delta_limit' => 3, 'delta_offset' => 2], [1, 4, 1]];
+    $data[] = [['group_rows' => TRUE, 'delta_first_last' => TRUE], [3, 9]];
+    $data[] = [['group_rows' => TRUE, 'delta_limit' => 1, 'delta_first_last' => TRUE], [3]];
+    $data[] = [['group_rows' => TRUE, 'delta_offset' => 1, 'delta_first_last' => TRUE], [1, 9]];
+
+    return $data;
+  }
+
+  /**
    * Returns a mocked base field storage object.
    *
    * @return \Drupal\Core\Field\FieldStorageDefinitionInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -640,3 +723,11 @@ protected function setupLanguageRenderer(Field $handler, $definition) {
   }
 
 }
+
+class FieldTestField extends Field {
+
+  public function executePrepareItemsByDelta(array $all_values) {
+    return $this->prepareItemsByDelta($all_values);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
index 5762e9e..397cb9d 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
@@ -1139,6 +1139,9 @@ public function testGetTranslationFromContext() {
       ->method('getTranslation')
       ->with('custom_langcode')
       ->will($this->returnValue($translated_entity));
+    $entity->expects($this->any())
+      ->method('getTranslationLanguages')
+      ->will($this->returnValue([new Language(['id' => 'en']), new Language(['id' => 'custom_langcode'])]));
 
     $this->assertSame($entity, $this->entityManager->getTranslationFromContext($entity));
     $this->assertSame($translated_entity, $this->entityManager->getTranslationFromContext($entity, 'custom_langcode'));
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
index 903adce..843be7a 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
@@ -126,7 +126,7 @@ protected function setUp() {
         }
         return $keys;
       });
-    $this->renderCache = new RenderCache($this->requestStack, $this->cacheFactory, $this->cacheContextsManager);
+    $this->renderCache = new RenderCache($this->requestStack, $this->cacheFactory, $this->cacheContextsManager, $this->rendererConfig);
     $this->renderer = new Renderer($this->controllerResolver, $this->themeManager, $this->elementInfo, $this->renderCache, $this->rendererConfig);
 
     $container = new ContainerBuilder();
