diff --git a/core/2572605-50.patch b/core/2572605-50.patch
new file mode 100644
index 0000000..2461d39
--- /dev/null
+++ b/core/2572605-50.patch
@@ -0,0 +1,551 @@
+diff --git a/composer.lock b/composer.lock
+index 8694dc4..5f3a006 100644
+--- a/composer.lock
++++ b/composer.lock
+@@ -2257,20 +2257,21 @@
+         },
+         {
+             "name": "twig/twig",
+-            "version": "v1.35.0",
++            "version": "v2.4.4",
+             "source": {
+                 "type": "git",
+                 "url": "https://github.com/twigphp/Twig.git",
+-                "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f"
++                "reference": "eddb97148ad779f27e670e1e3f19fb323aedafeb"
+             },
+             "dist": {
+                 "type": "zip",
+-                "url": "https://api.github.com/repos/twigphp/Twig/zipball/daa657073e55b0a78cce8fdd22682fddecc6385f",
+-                "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f",
++                "url": "https://api.github.com/repos/twigphp/Twig/zipball/eddb97148ad779f27e670e1e3f19fb323aedafeb",
++                "reference": "eddb97148ad779f27e670e1e3f19fb323aedafeb",
+                 "shasum": ""
+             },
+             "require": {
+-                "php": ">=5.3.3"
++                "php": "^7.0",
++                "symfony/polyfill-mbstring": "~1.0"
+             },
+             "require-dev": {
+                 "psr/container": "^1.0",
+@@ -2280,7 +2281,7 @@
+             "type": "library",
+             "extra": {
+                 "branch-alias": {
+-                    "dev-master": "1.35-dev"
++                    "dev-master": "2.4-dev"
+                 }
+             },
+             "autoload": {
+@@ -2318,7 +2319,7 @@
+             "keywords": [
+                 "templating"
+             ],
+-            "time": "2017-09-27T18:06:46+00:00"
++            "time": "2017-09-27T18:10:31+00:00"
+         },
+         {
+             "name": "wikimedia/composer-merge-plugin",
+diff --git a/core/composer.json b/core/composer.json
+index 31bc51a..626f6a5 100644
+--- a/core/composer.json
++++ b/core/composer.json
+@@ -18,7 +18,7 @@
+         "symfony/process": "~3.2.8",
+         "symfony/polyfill-iconv": "^1.0",
+         "symfony/yaml": "~3.2.8",
+-        "twig/twig": "^1.35.0",
++        "twig/twig": "^1.35.0|^2.4.0",
+         "doctrine/common": "^2.5",
+         "doctrine/annotations": "^1.2",
+         "guzzlehttp/guzzle": "^6.2.1",
+diff --git a/core/lib/Drupal/Core/Template/Loader/StringLoader.php b/core/lib/Drupal/Core/Template/Loader/StringLoader.php
+index 6325b9e..d0590a2 100644
+--- a/core/lib/Drupal/Core/Template/Loader/StringLoader.php
++++ b/core/lib/Drupal/Core/Template/Loader/StringLoader.php
+@@ -44,6 +44,14 @@ public function getSource($name) {
+   /**
+    * {@inheritdoc}
+    */
++  public function getSourceContext($name) {
++    $name = (string) $name;
++    return new \Twig_Source($name, $name);
++  }
++
++  /**
++   * {@inheritdoc}
++   */
+   public function getCacheKey($name) {
+     return $name;
+   }
+diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php
+index 21755a5..5271155 100644
+--- a/core/lib/Drupal/Core/Template/TwigEnvironment.php
++++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php
+@@ -25,6 +25,8 @@ class TwigEnvironment extends \Twig_Environment {
+ 
+   protected $twigCachePrefix = '';
+ 
++  protected $templateClassPrefix = '__TwigTemplate_';
++
+   /**
+    * Constructs a TwigEnvironment object and stores cache and storage
+    * internally.
+@@ -42,7 +44,7 @@ class TwigEnvironment extends \Twig_Environment {
+    * @param array $options
+    *   The options for the Twig environment.
+    */
+-  public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, StateInterface $state, \Twig_LoaderInterface $loader = NULL, $options = []) {
++  public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, StateInterface $state, \Twig_LoaderInterface $loader = NULL, array $options = []) {
+     // Ensure that twig.engine is loaded, given that it is needed to render a
+     // template because functions like TwigExtension::escapeFilter() are called.
+     require_once $root . '/core/themes/engines/twig/twig.engine';
+@@ -57,11 +59,6 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension
+     ];
+     // Ensure autoescaping is always on.
+     $options['autoescape'] = 'html';
+-
+-    $policy = new TwigSandboxPolicy();
+-    $sandbox = new \Twig_Extension_Sandbox($policy, TRUE);
+-    $this->addExtension($sandbox);
+-
+     if ($options['cache'] === TRUE) {
+       $current = $state->get('twig_extension_hash_prefix', ['twig_extension_hash' => '']);
+       if ($current['twig_extension_hash'] !== $twig_extension_hash || empty($current['twig_cache_prefix'])) {
+@@ -78,8 +75,11 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension
+       $options['cache'] = new TwigPhpStorageCache($cache, $this->twigCachePrefix);
+     }
+ 
+-    $this->loader = $loader;
+-    parent::__construct($this->loader, $options);
++    $this->setLoader($loader);
++    parent::__construct($this->getLoader(), $options);
++    $policy = new TwigSandboxPolicy();
++    $sandbox = new \Twig_Extension_Sandbox($policy, TRUE);
++    $this->addExtension($sandbox);
+   }
+ 
+   /**
+@@ -110,7 +110,7 @@ public function getTemplateClass($name, $index = NULL) {
+     // node.html.twig for the output of each node and the same compiled class.
+     $cache_index = $name . (NULL === $index ? '' : '_' . $index);
+     if (!isset($this->templateClasses[$cache_index])) {
+-      $this->templateClasses[$cache_index] = $this->templateClassPrefix . hash('sha256', $this->loader->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index);
++      $this->templateClasses[$cache_index] = $this->templateClassPrefix . hash('sha256', $this->getLoader()->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index);
+     }
+     return $this->templateClasses[$cache_index];
+   }
+@@ -140,7 +140,7 @@ public function getTemplateClass($name, $index = NULL) {
+   public function renderInline($template_string, array $context = []) {
+     // Prefix all inline templates with a special comment.
+     $template_string = '{# inline_template_start #}' . $template_string;
+-    return Markup::create($this->loadTemplate($template_string, NULL)->render($context));
++    return Markup::create($this->createTemplate($template_string)->render($context));
+   }
+ 
+ }
+diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php
+index 377efe6..7f2738f 100644
+--- a/core/lib/Drupal/Core/Template/TwigExtension.php
++++ b/core/lib/Drupal/Core/Template/TwigExtension.php
+@@ -65,7 +65,7 @@ class TwigExtension extends \Twig_Extension {
+    * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
+    *   The date formatter.
+    */
+-  public function __construct(RendererInterface $renderer, UrlGeneratorInterface $url_generator, ThemeManagerInterface $theme_manager, DateFormatterInterface $date_formatter) {
++  public function __construct(RendererInterface $renderer, UrlGeneratorInterface $url_generator = NULL, ThemeManagerInterface $theme_manager = NULL, DateFormatterInterface $date_formatter = NULL) {
+     $this->renderer = $renderer;
+     $this->urlGenerator = $url_generator;
+     $this->themeManager = $theme_manager;
+@@ -177,7 +177,7 @@ public function getFilters() {
+       new \Twig_SimpleFilter('safe_join', [$this, 'safeJoin'], ['needs_environment' => TRUE, 'is_safe' => ['html']]),
+ 
+       // Array filters.
+-      new \Twig_SimpleFilter('without', 'twig_without'),
++      new \Twig_SimpleFilter('without', [$this, 'withoutFilter']),
+ 
+       // CSS class and ID filters.
+       new \Twig_SimpleFilter('clean_class', '\Drupal\Component\Utility\Html::getClass'),
+@@ -630,4 +630,35 @@ public function createAttribute(array $attributes = []) {
+     return new Attribute($attributes);
+   }
+ 
++  /**
++   * Removes child elements from a copy of the original array.
++   *
++   * Creates a copy of the renderable array and removes child elements by key
++   * specified through filter's arguments. The copy can be printed without these
++   * elements. The original renderable array is still available and can be used
++   * to print child elements in their entirety in the twig template.
++   *
++   * @param array|object $element
++   *   The parent renderable array to exclude the child items.
++   *
++   * @return array
++   *   The filtered renderable array.
++   */
++  public function withoutFilter($element) {
++    if ($element instanceof \ArrayAccess) {
++      $filtered_element = clone $element;
++    }
++    else {
++      $filtered_element = $element;
++    }
++    $args = func_get_args();
++    unset($args[0]);
++    foreach ($args as $arg) {
++      if (isset($filtered_element[$arg])) {
++        unset($filtered_element[$arg]);
++      }
++    }
++    return $filtered_element;
++  }
++
+ }
+diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
+index 264a511..06fc8dd 100644
+--- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php
++++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
+@@ -17,13 +17,21 @@ class TwigNodeTrans extends \Twig_Node {
+   /**
+    * {@inheritdoc}
+    */
+-  public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) {
+-    parent::__construct([
+-      'count' => $count,
+-      'body' => $body,
+-      'plural' => $plural,
+-      'options' => $options,
+-    ], [], $lineno, $tag);
++  public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node $options = NULL, $lineno, $tag = NULL) {
++    $nodes = [];
++    if (NULL !== $body) {
++      $nodes['body'] = $body;
++    }
++    if (NULL !== $count) {
++      $nodes['count'] = $count;
++    }
++    if (NULL !== $plural) {
++      $nodes['plural'] = $plural;
++    }
++    if (NULL !== $options) {
++      $nodes['options'] = $options;
++    }
++    parent::__construct($nodes, [], $lineno, $tag);
+   }
+ 
+   /**
+@@ -32,29 +40,27 @@ public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_N
+   public function compile(\Twig_Compiler $compiler) {
+     $compiler->addDebugInfo($this);
+ 
+-    $options = $this->getNode('options');
+-
+     list($singular, $tokens) = $this->compileString($this->getNode('body'));
+     $plural = NULL;
+ 
+-    if (NULL !== $this->getNode('plural')) {
++    if ($this->hasNode('plural')) {
+       list($plural, $pluralTokens) = $this->compileString($this->getNode('plural'));
+       $tokens = array_merge($tokens, $pluralTokens);
+     }
+ 
+     // Start writing with the function to be called.
+-    $compiler->write('echo ' . (empty($plural) ? 't' : '\Drupal::translation()->formatPlural') . '(');
++    $compiler->write('echo ' . ($this->hasNode('plural') ? '\Drupal::translation()->formatPlural' : 't') . '(');
+ 
+     // Move the count to the beginning of the parameters list.
+-    if (!empty($plural)) {
+-      $compiler->raw('abs(')->subcompile($this->getNode('count'))->raw('), ');
++    if ($this->hasNode('plural')) {
++      $compiler->raw('abs(')->subcompile($this->hasNode('count') ? $this->getNode('count') : NULL)->raw('), ');
+     }
+ 
+     // Write the singular text parameter.
+     $compiler->subcompile($singular);
+ 
+     // Write the plural text parameter, if necessary.
+-    if (!empty($plural)) {
++    if ($this->hasNode('plural')) {
+       $compiler->raw(', ')->subcompile($plural);
+     }
+ 
+@@ -67,8 +73,8 @@ public function compile(\Twig_Compiler $compiler) {
+     $compiler->raw(')');
+ 
+     // Write any options passed.
+-    if (!empty($options)) {
+-      $compiler->raw(', ')->subcompile($options);
++    if ($this->hasNode('options')) {
++      $compiler->raw(', ')->subcompile($this->getNode('options'));
+     }
+ 
+     // Write function closure.
+@@ -103,7 +109,7 @@ protected function compileString(\Twig_Node $body) {
+       $text = '';
+ 
+       foreach ($body as $node) {
+-        if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof \Twig_Node_SetTemp) {
++        if (get_class($node) === 'Twig_Node') {
+           $node = $node->getNode(1);
+         }
+ 
+@@ -157,7 +163,7 @@ protected function compileString(\Twig_Node $body) {
+             if (!is_null($args)) {
+               $argName = $args->getAttribute('name');
+             }
+-            $expr = new \Twig_Node_Expression_Name($argName, $n->getLine());
++            $expr = new \Twig_Node_Expression_Name($argName, $n->getTemplateLine());
+           }
+           $placeholder = sprintf('%s%s', $argPrefix, $argName);
+           $text .= $placeholder;
+@@ -176,7 +182,7 @@ protected function compileString(\Twig_Node $body) {
+       $text = $body->getAttribute('data');
+     }
+ 
+-    return [new \Twig_Node([new \Twig_Node_Expression_Constant(trim($text), $body->getLine())]), $tokens];
++    return [new \Twig_Node([new \Twig_Node_Expression_Constant(trim($text), $body->getTemplateLine())]), $tokens];
+   }
+ 
+ }
+diff --git a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
+index 1ebfa57..f63edee 100644
+--- a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
++++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
+@@ -33,7 +33,7 @@ protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) {
+         return $node;
+       }
+       $class = get_class($node);
+-      $line = $node->getLine();
++      $line = $node->getTemplateLine();
+       return new $class(
+         new \Twig_Node_Expression_Function('render_var', new \Twig_Node([$node->getNode('expr')]), $line),
+         $line
+diff --git a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
+index 96f5560..559319c 100644
+--- a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
++++ b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
+@@ -21,10 +21,10 @@ class TwigTransTokenParser extends \Twig_TokenParser {
+   public function parse(\Twig_Token $token) {
+     $lineno = $token->getLine();
+     $stream = $this->parser->getStream();
+-    $body = NULL;
+-    $options = NULL;
+-    $count = NULL;
+     $plural = NULL;
++    $count = NULL;
++    $options = NULL;
++    $body = NULL;
+ 
+     if (!$stream->test(\Twig_Token::BLOCK_END_TYPE) && $stream->test(\Twig_Token::STRING_TYPE)) {
+       $body = $this->parser->getExpressionParser()->parseExpression();
+@@ -55,14 +55,14 @@ public function parse(\Twig_Token $token) {
+   /**
+    * Detect a 'plural' switch or the end of a 'trans' tag.
+    */
+-  public function decideForFork($token) {
++  public function decideForFork(\Twig_Token $token) {
+     return $token->test(['plural', 'endtrans']);
+   }
+ 
+   /**
+    * Detect the end of a 'trans' tag.
+    */
+-  public function decideForEnd($token) {
++  public function decideForEnd(\Twig_Token $token) {
+     return $token->test('endtrans');
+   }
+ 
+diff --git a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php
+index 93be201..9888caa 100644
+--- a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php
++++ b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php
+@@ -21,7 +21,7 @@ class TestExtension extends \Twig_Extension {
+    */
+   public function getFunctions() {
+     return [
+-      'testfunc' => new \Twig_Function_Function(['Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFunction']),
++      new \Twig_SimpleFunction('testfunc', [$this, 'testFunction']),
+     ];
+   }
+ 
+@@ -39,7 +39,7 @@ public function getFunctions() {
+    */
+   public function getFilters() {
+     return [
+-      'testfilter' => new \Twig_Filter_Function(['Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFilter']),
++      new \Twig_SimpleFilter('testfilter', [$this, 'testFilter']),
+     ];
+   }
+ 
+diff --git a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
+index 5d6f61f..d93a73d 100644
+--- a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
++++ b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
+@@ -132,12 +132,17 @@ public function testCacheFilename() {
+     $expected = strlen($prefix) + 2 + 2 * TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH;
+     $this->assertEquals($expected, strlen($key));
+ 
+-    $original_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig');
++    $template_path = 'core/modules/system/templates/container.html.twig';
++    $cache = $environment->getCache();
++    $class = $environment->getTemplateClass($template_path);
++    $original_filename = $cache->generateKey($template_path, $class);
+     \Drupal::getContainer()->set('twig', NULL);
+ 
+     \Drupal::service('module_installer')->install(['twig_extension_test']);
+     $environment = \Drupal::service('twig');
+-    $new_extension_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig');
++    $cache = $environment->getCache();
++    $class = $environment->getTemplateClass($template_path);
++    $new_extension_filename = $cache->generateKey($template_path, $class);
+     \Drupal::getContainer()->set('twig', NULL);
+ 
+     $this->assertNotEqual($new_extension_filename, $original_filename);
+diff --git a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php
+index c9bccce..e5e7e9a 100644
+--- a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php
++++ b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php
+@@ -265,10 +265,10 @@ public function testChainAddRemoveClasses() {
+    * @group legacy
+    */
+   public function testTwigAddRemoveClasses($template, $expected, $seed_attributes = []) {
+-    $loader = new \Twig_Loader_String();
++    $loader = new \Twig_Loader_Array([]);
+     $twig = new \Twig_Environment($loader);
+     $data = ['attributes' => new Attribute($seed_attributes)];
+-    $result = $twig->render($template, $data);
++    $result = $twig->createTemplate($template)->render($data);
+     $this->assertEquals($expected, $result);
+   }
+ 
+@@ -281,7 +281,7 @@ public function testTwigAddRemoveClasses($template, $expected, $seed_attributes
+    */
+   public function providerTestAttributeClassHelpers() {
+     return [
+-      ["{{ attributes.class }}", ''],
++      ["{{ attributes }}", ''],
+       ["{{ attributes.addClass('everest').class }}", 'everest'],
+       ["{{ attributes.addClass(['k2', 'kangchenjunga']).class }}", 'k2 kangchenjunga'],
+       ["{{ attributes.addClass('lhotse', 'makalu', 'cho-oyu').class }}", 'lhotse makalu cho-oyu'],
+diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
+index a872e30..4e790d8 100644
+--- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
++++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
+@@ -11,6 +11,19 @@
+ use Drupal\Core\Url;
+ use Drupal\Tests\UnitTestCase;
+ 
++if (!function_exists(__NAMESPACE__ . 't')) {
++
++  function t($string, array $args = []) {
++    return strtr($string, $args);
++  }
++
++}
++if (!function_exists(__NAMESPACE__ . 'file_create_url')) {
++
++  function file_create_url() {}
++
++}
++
+ /**
+  * Tests the twig extension.
+  *
+@@ -78,7 +91,8 @@ public function setUp() {
+    * @group legacy
+    */
+   public function testEscaping($template, $expected) {
+-    $twig = new \Twig_Environment(NULL, [
++    $loader = new \Twig_Loader_Filesystem();
++    $twig = new \Twig_Environment($loader, [
+       'debug' => TRUE,
+       'cache' => FALSE,
+       'autoescape' => 'html',
+@@ -86,7 +100,8 @@ public function testEscaping($template, $expected) {
+     ]);
+     $twig->addExtension($this->systemUnderTest);
+ 
+-    $nodes = $twig->parse($twig->tokenize($template));
++    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
++    $nodes = $twig->parse($twig->tokenize(new \Twig_Source($template, $name)));
+ 
+     $this->assertSame($expected, $nodes->getNode('body')
+       ->getNode(0)
+@@ -140,10 +155,11 @@ public function testActiveTheme() {
+       ->method('getActiveTheme')
+       ->willReturn($active_theme);
+ 
+-    $loader = new \Twig_Loader_String();
++    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
++    $loader = new \Twig_Loader_Array([$name => '{{ active_theme() }}']);
+     $twig = new \Twig_Environment($loader);
+     $twig->addExtension($this->systemUnderTest);
+-    $result = $twig->render('{{ active_theme() }}');
++    $result = $twig->render($name);
+     $this->assertEquals('test_theme', $result);
+   }
+ 
+@@ -177,10 +193,11 @@ public function testActiveThemePath() {
+       ->method('getActiveTheme')
+       ->willReturn($active_theme);
+ 
+-    $loader = new \Twig_Loader_String();
++    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
++    $loader = new \Twig_Loader_Array([$name => '{{ active_theme_path() }}']);
+     $twig = new \Twig_Environment($loader);
+     $twig->addExtension($this->systemUnderTest);
+-    $result = $twig->render('{{ active_theme_path() }}');
++    $result = $twig->render($name);
+     $this->assertEquals('foo/bar', $result);
+   }
+ 
+@@ -192,7 +209,8 @@ public function testActiveThemePath() {
+    * @group legacy
+    */
+   public function testSafeStringEscaping() {
+-    $twig = new \Twig_Environment(NULL, [
++    $loader = new \Twig_Loader_Filesystem();
++    $twig = new \Twig_Environment($loader, [
+       'debug' => TRUE,
+       'cache' => FALSE,
+       'autoescape' => 'html',
+@@ -278,7 +296,8 @@ public function providerTestRenderVar() {
+    * @group legacy
+    */
+   public function testEscapeWithGeneratedLink() {
+-    $twig = new \Twig_Environment(NULL, [
++    $loader = new \Twig_Loader_Filesystem();
++    $twig = new \Twig_Environment($loader, [
+         'debug' => TRUE,
+         'cache' => FALSE,
+         'autoescape' => 'html',
+@@ -336,7 +355,8 @@ public function testRenderVarWithGeneratedLink() {
+    * @covers ::createAttribute
+    */
+   public function testCreateAttribute() {
+-    $loader = new StringLoader();
++    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
++    $loader = new \Twig_Loader_Array([$name => "{% for iteration in iterations %}<div{{ create_attribute(iteration) }}></div>{% endfor %}"]);
+     $twig = new \Twig_Environment($loader);
+     $twig->addExtension($this->systemUnderTest);
+ 
+@@ -345,12 +365,15 @@ public function testCreateAttribute() {
+       ['id' => 'puppies', 'data-value' => 'foo', 'data-lang' => 'en'],
+       [],
+     ];
+-    $result = $twig->render("{% for iteration in iterations %}<div{{ create_attribute(iteration) }}></div>{% endfor %}", ['iterations' => $iterations]);
++    $result = $twig->render($name, ['iterations' => $iterations]);
+     $expected = '<div class="kittens" data-toggle="modal" data-lang="es"></div><div id="puppies" data-value="foo" data-lang="en"></div><div></div>';
+     $this->assertEquals($expected, $result);
+ 
+     // Test default creation of empty attribute object and using its method.
+-    $result = $twig->render("<div{{ create_attribute().addClass('meow') }}></div>");
++    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
++    $loader = new \Twig_Loader_Array(array($name => "<div{{ create_attribute().addClass('meow') }}></div>"));
++    $twig->setLoader($loader);
++    $result = $twig->render($name);
+     $expected = '<div class="meow"></div>';
+     $this->assertEquals($expected, $result);
+   }
diff --git a/core/2817833-38.patch b/core/2817833-38.patch
new file mode 100644
index 0000000..84009ad
--- /dev/null
+++ b/core/2817833-38.patch
@@ -0,0 +1,458 @@
+diff --git a/core/2817833-36.patch b/core/2817833-36.patch
+new file mode 100644
+index 0000000..a94cc5f
+--- /dev/null
++++ b/core/2817833-36.patch
+@@ -0,0 +1,226 @@
++diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
++index 8bc17f1..b6ca09a 100644
++--- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
+++++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
++@@ -3,6 +3,7 @@
++ namespace Drupal\migrate\Plugin\migrate\id_map;
++ 
++ use Drupal\Component\Utility\Unicode;
+++use Drupal\Core\Database\DatabaseException;
++ use Drupal\Core\Field\BaseFieldDefinition;
++ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
++ use Drupal\Core\Plugin\PluginBase;
++@@ -161,6 +162,18 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
++     $this->migration = $migration;
++     $this->eventDispatcher = $event_dispatcher;
++     $this->message = new MigrateMessage();
+++
+++    if (!isset($this->database)) {
+++      $this->database = \Drupal::database();
+++    }
+++
+++    // Default generated table names, limited to 63 characters.
+++    $machine_name = str_replace(':', '__', $this->migration->id());
+++    $prefix_length = strlen($this->database->tablePrefix());
+++    $this->mapTableName = 'migrate_map_' . Unicode::strtolower($machine_name);
+++    $this->mapTableName = Unicode::substr($this->mapTableName, 0, 63 - $prefix_length);
+++    $this->messageTableName = 'migrate_message_' . Unicode::strtolower($machine_name);
+++    $this->messageTableName = Unicode::substr($this->messageTableName, 0, 63 - $prefix_length);
++   }
++ 
++   /**
++@@ -246,7 +259,6 @@ protected function destinationIdFields() {
++    *   The map table name.
++    */
++   public function mapTableName() {
++-    $this->init();
++     return $this->mapTableName;
++   }
++ 
++@@ -257,7 +269,6 @@ public function mapTableName() {
++    *   The message table name.
++    */
++   public function messageTableName() {
++-    $this->init();
++     return $this->messageTableName;
++   }
++ 
++@@ -278,9 +289,6 @@ public function getQualifiedMapTableName() {
++    *   The database connection object.
++    */
++   public function getDatabase() {
++-    if (!isset($this->database)) {
++-      $this->database = \Drupal::database();
++-    }
++     $this->init();
++     return $this->database;
++   }
++@@ -291,13 +299,6 @@ public function getDatabase() {
++   protected function init() {
++     if (!$this->initialized) {
++       $this->initialized = TRUE;
++-      // Default generated table names, limited to 63 characters.
++-      $machine_name = str_replace(':', '__', $this->migration->id());
++-      $prefix_length = strlen($this->getDatabase()->tablePrefix());
++-      $this->mapTableName = 'migrate_map_' . Unicode::strtolower($machine_name);
++-      $this->mapTableName = Unicode::substr($this->mapTableName, 0, 63 - $prefix_length);
++-      $this->messageTableName = 'migrate_message_' . Unicode::strtolower($machine_name);
++-      $this->messageTableName = Unicode::substr($this->messageTableName, 0, 63 - $prefix_length);
++       $this->ensureTables();
++     }
++   }
++@@ -696,21 +697,17 @@ public function prepareUpdate() {
++    * {@inheritdoc}
++    */
++   public function processedCount() {
++-    return $this->getDatabase()->select($this->mapTableName())
++-      ->countQuery()
++-      ->execute()
++-      ->fetchField();
+++    return $this->countHelper(NULL, $this->mapTableName());
++   }
++ 
++   /**
++    * {@inheritdoc}
++    */
++   public function importedCount() {
++-    return $this->getDatabase()->select($this->mapTableName())
++-      ->condition('source_row_status', [MigrateIdMapInterface::STATUS_IMPORTED, MigrateIdMapInterface::STATUS_NEEDS_UPDATE], 'IN')
++-      ->countQuery()
++-      ->execute()
++-      ->fetchField();
+++    return $this->countHelper([
+++      MigrateIdMapInterface::STATUS_IMPORTED,
+++      MigrateIdMapInterface::STATUS_NEEDS_UPDATE,
+++    ]);
++   }
++ 
++   /**
++@@ -737,20 +734,28 @@ public function messageCount() {
++   /**
++    * Counts records in a table.
++    *
++-   * @param int $status
++-   *   An integer for the source_row_status column.
+++   * @param int|array $status
+++   *   (optional) Status code(s) to filter the source_row_status column.
++    * @param string $table
++    *   (optional) The table to work. Defaults to NULL.
++    *
++    * @return int
++    *   The number of records.
++    */
++-  protected function countHelper($status, $table = NULL) {
++-    $query = $this->getDatabase()->select($table ?: $this->mapTableName());
+++  protected function countHelper($status = NULL, $table = NULL) {
+++    // Use database directly to avoid creating tables.
+++    $query = $this->database->select($table ?: $this->mapTableName());
++     if (isset($status)) {
++-      $query->condition('source_row_status', $status);
+++      $query->condition('source_row_status', $status, is_array($status) ? 'IN' : '=');
+++    }
+++    try {
+++      $count = $query->countQuery()->execute()->fetchField();
+++    }
+++    catch (DatabaseException $e) {
+++      // The table does not exist, therefore there are no records.
+++      $count = 0;
++     }
++-    return $query->countQuery()->execute()->fetchField();
+++    return $count;
++   }
++ 
++   /**
++@@ -977,7 +982,7 @@ function (array $id) {
++     $ids = [0];
++     foreach ($map_tables as $map_table) {
++       if (!$this->getDatabase()->schema()->tableExists($map_table)) {
++-        break;
+++        continue;
++       }
++ 
++       $query = $this->getDatabase()->select($map_table, 'map')
++diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
++index 2ad2b3d..63649c0 100644
++--- a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
+++++ b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
++@@ -1010,4 +1010,23 @@ private function getIdMapContents() {
++     return $contents;
++   }
++ 
+++  public function testMapTableCreation() {
+++    $id_map = $this->getIdMap();
+++
+++    $map_table_name = $id_map->mapTableName();
+++    $message_table_name = $id_map->messageTableName();
+++    $this->assertEquals('migrate_map_sql_idmap_test', $map_table_name);
+++    $this->assertEquals('migrate_message_sql_idmap_test', $message_table_name);
+++
+++    // Check that tables don't exist.
+++    $this->assertFalse($this->database->schema()->tableExists($map_table_name));
+++    $this->assertFalse($this->database->schema()->tableExists($message_table_name));
+++
+++    $id_map->getDatabase();
+++
+++    // Check that tables do exist.
+++    $this->assertTrue($this->database->schema()->tableExists($map_table_name));
+++    $this->assertTrue($this->database->schema()->tableExists($message_table_name));
+++  }
+++
++ }
++diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
++index a0bb591..37c6e09 100644
++--- a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
+++++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
++@@ -59,8 +59,9 @@ public function testMultipleMigrationWithoutIdConflicts() {
++ 
++     // Insert data in the d6_node:page migration mappping table to simulate a
++     // previously migrated node.
++-    $table_name = $this->getMigration('d6_node:page')->getIdMap()->mapTableName();
++-    $this->container->get('database')->insert($table_name)
+++    $id_map = $this->getMigration('d6_node:page')->getIdMap();
+++    $table_name = $id_map->mapTableName();
+++    $id_map->getDatabase()->insert($table_name)
++       ->fields([
++         'source_ids_hash' => 1,
++         'sourceid1' => 1,
++@@ -156,8 +157,9 @@ public function testDraftRevisionIdConflicts() {
++ 
++     // Insert data in the d6_node_revision:page migration mappping table to
++     // simulate a previously migrated node revison.
++-    $table_name = $this->getMigration('d6_node_revision:page')->getIdMap()->mapTableName();
++-    $this->container->get('database')->insert($table_name)
+++    $id_map = $this->getMigration('d6_node_revision:page')->getIdMap();
+++    $table_name = $id_map->mapTableName();
+++    $id_map->getDatabase()->insert($table_name)
++       ->fields([
++         'source_ids_hash' => 1,
++         'sourceid1' => 1,
++diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
++index 1d74423..aa0354b 100644
++--- a/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
+++++ b/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
++@@ -59,8 +59,9 @@ public function testMultipleMigrationWithoutIdConflicts() {
++ 
++     // Insert data in the d7_node:page migration mappping table to simulate a
++     // previously migrated node.
++-    $table_name = $this->getMigration('d7_node:page')->getIdMap()->mapTableName();
++-    $this->container->get('database')->insert($table_name)
+++    $id_map = $this->getMigration('d7_node:page')->getIdMap();
+++    $table_name = $id_map->mapTableName();
+++    $id_map->getDatabase()->insert($table_name)
++       ->fields([
++         'source_ids_hash' => 1,
++         'sourceid1' => 1,
++@@ -155,8 +156,9 @@ public function testDraftRevisionIdConflicts() {
++ 
++     // Insert data in the d7_node_revision:page migration mappping table to
++     // simulate a previously migrated node revison.
++-    $table_name = $this->getMigration('d7_node_revision:page')->getIdMap()->mapTableName();
++-    $this->container->get('database')->insert($table_name)
+++    $id_map = $this->getMigration('d7_node_revision:page')->getIdMap();
+++    $table_name = $id_map->mapTableName();
+++    $id_map->getDatabase()->insert($table_name)
++       ->fields([
++         'source_ids_hash' => 1,
++         'sourceid1' => 1,
+diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
+index 5949b59..ec927f5 100644
+--- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
++++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
+@@ -3,6 +3,7 @@
+ namespace Drupal\migrate\Plugin\migrate\id_map;
+ 
+ use Drupal\Component\Utility\Unicode;
++use Drupal\Core\Database\DatabaseException;
+ use Drupal\Core\Field\BaseFieldDefinition;
+ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+ use Drupal\Core\Plugin\PluginBase;
+@@ -161,6 +162,18 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
+     $this->migration = $migration;
+     $this->eventDispatcher = $event_dispatcher;
+     $this->message = new MigrateMessage();
++
++    if (!isset($this->database)) {
++      $this->database = \Drupal::database();
++    }
++
++    // Default generated table names, limited to 63 characters.
++    $machine_name = str_replace(':', '__', $this->migration->id());
++    $prefix_length = strlen($this->database->tablePrefix());
++    $this->mapTableName = 'migrate_map_' . Unicode::strtolower($machine_name);
++    $this->mapTableName = Unicode::substr($this->mapTableName, 0, 63 - $prefix_length);
++    $this->messageTableName = 'migrate_message_' . Unicode::strtolower($machine_name);
++    $this->messageTableName = Unicode::substr($this->messageTableName, 0, 63 - $prefix_length);
+   }
+ 
+   /**
+@@ -246,7 +259,6 @@ protected function destinationIdFields() {
+    *   The map table name.
+    */
+   public function mapTableName() {
+-    $this->init();
+     return $this->mapTableName;
+   }
+ 
+@@ -257,7 +269,6 @@ public function mapTableName() {
+    *   The message table name.
+    */
+   public function messageTableName() {
+-    $this->init();
+     return $this->messageTableName;
+   }
+ 
+@@ -278,9 +289,6 @@ public function getQualifiedMapTableName() {
+    *   The database connection object.
+    */
+   public function getDatabase() {
+-    if (!isset($this->database)) {
+-      $this->database = \Drupal::database();
+-    }
+     $this->init();
+     return $this->database;
+   }
+@@ -291,13 +299,6 @@ public function getDatabase() {
+   protected function init() {
+     if (!$this->initialized) {
+       $this->initialized = TRUE;
+-      // Default generated table names, limited to 63 characters.
+-      $machine_name = str_replace(':', '__', $this->migration->id());
+-      $prefix_length = strlen($this->getDatabase()->tablePrefix());
+-      $this->mapTableName = 'migrate_map_' . Unicode::strtolower($machine_name);
+-      $this->mapTableName = Unicode::substr($this->mapTableName, 0, 63 - $prefix_length);
+-      $this->messageTableName = 'migrate_message_' . Unicode::strtolower($machine_name);
+-      $this->messageTableName = Unicode::substr($this->messageTableName, 0, 63 - $prefix_length);
+       $this->ensureTables();
+     }
+   }
+@@ -696,21 +697,17 @@ public function prepareUpdate() {
+    * {@inheritdoc}
+    */
+   public function processedCount() {
+-    return (int) $this->getDatabase()->select($this->mapTableName())
+-      ->countQuery()
+-      ->execute()
+-      ->fetchField();
++    return $this->countHelper(NULL, $this->mapTableName());
+   }
+ 
+   /**
+    * {@inheritdoc}
+    */
+   public function importedCount() {
+-    return (int) $this->getDatabase()->select($this->mapTableName())
+-      ->condition('source_row_status', [MigrateIdMapInterface::STATUS_IMPORTED, MigrateIdMapInterface::STATUS_NEEDS_UPDATE], 'IN')
+-      ->countQuery()
+-      ->execute()
+-      ->fetchField();
++    return $this->countHelper([
++      MigrateIdMapInterface::STATUS_IMPORTED,
++      MigrateIdMapInterface::STATUS_NEEDS_UPDATE,
++    ]);
+   }
+ 
+   /**
+@@ -737,20 +734,28 @@ public function messageCount() {
+   /**
+    * Counts records in a table.
+    *
+-   * @param int $status
+-   *   An integer for the source_row_status column.
++   * @param int|array $status
++   *   (optional) Status code(s) to filter the source_row_status column.
+    * @param string $table
+    *   (optional) The table to work. Defaults to NULL.
+    *
+    * @return int
+    *   The number of records.
+    */
+-  protected function countHelper($status, $table = NULL) {
+-    $query = $this->getDatabase()->select($table ?: $this->mapTableName());
++  protected function countHelper($status = NULL, $table = NULL) {
++    // Use database directly to avoid creating tables.
++    $query = $this->database->select($table ?: $this->mapTableName());
+     if (isset($status)) {
+-      $query->condition('source_row_status', $status);
++      $query->condition('source_row_status', $status, is_array($status) ? 'IN' : '=');
++    }
++    try {
++      $count = (int) $query->countQuery()->execute()->fetchField();
++    }
++    catch (DatabaseException $e) {
++      // The table does not exist, therefore there are no records.
++      $count = 0;
+     }
+-    return (int) $query->countQuery()->execute()->fetchField();
++    return $count;
+   }
+ 
+   /**
+@@ -977,7 +982,7 @@ function (array $id) {
+     $ids = [0];
+     foreach ($map_tables as $map_table) {
+       if (!$this->getDatabase()->schema()->tableExists($map_table)) {
+-        break;
++        continue;
+       }
+ 
+       $query = $this->getDatabase()->select($map_table, 'map')
+diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
+index 80c55ef..18fc5a4 100644
+--- a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
++++ b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
+@@ -1010,4 +1010,23 @@ private function getIdMapContents() {
+     return $contents;
+   }
+ 
++  public function testMapTableCreation() {
++    $id_map = $this->getIdMap();
++
++    $map_table_name = $id_map->mapTableName();
++    $message_table_name = $id_map->messageTableName();
++    $this->assertEquals('migrate_map_sql_idmap_test', $map_table_name);
++    $this->assertEquals('migrate_message_sql_idmap_test', $message_table_name);
++
++    // Check that tables don't exist.
++    $this->assertFalse($this->database->schema()->tableExists($map_table_name));
++    $this->assertFalse($this->database->schema()->tableExists($message_table_name));
++
++    $id_map->getDatabase();
++
++    // Check that tables do exist.
++    $this->assertTrue($this->database->schema()->tableExists($map_table_name));
++    $this->assertTrue($this->database->schema()->tableExists($message_table_name));
++  }
++
+ }
+diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
+index 860f576..bee030e 100644
+--- a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
++++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
+@@ -60,8 +60,9 @@ public function testMultipleMigrationWithoutIdConflicts() {
+ 
+     // Insert data in the d6_node:page migration mappping table to simulate a
+     // previously migrated node.
+-    $table_name = $this->getMigration('d6_node:page')->getIdMap()->mapTableName();
+-    $this->container->get('database')->insert($table_name)
++    $id_map = $this->getMigration('d6_node:page')->getIdMap();
++    $table_name = $id_map->mapTableName();
++    $id_map->getDatabase()->insert($table_name)
+       ->fields([
+         'source_ids_hash' => 1,
+         'sourceid1' => 1,
+@@ -157,8 +158,9 @@ public function testDraftRevisionIdConflicts() {
+ 
+     // Insert data in the d6_node_revision:page migration mappping table to
+     // simulate a previously migrated node revison.
+-    $table_name = $this->getMigration('d6_node_revision:page')->getIdMap()->mapTableName();
+-    $this->container->get('database')->insert($table_name)
++    $id_map = $this->getMigration('d6_node_revision:page')->getIdMap();
++    $table_name = $id_map->mapTableName();
++    $id_map->getDatabase()->insert($table_name)
+       ->fields([
+         'source_ids_hash' => 1,
+         'sourceid1' => 1,
+diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
+index 351ab43..d12445b 100644
+--- a/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
++++ b/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
+@@ -60,8 +60,9 @@ public function testMultipleMigrationWithoutIdConflicts() {
+ 
+     // Insert data in the d7_node:page migration mappping table to simulate a
+     // previously migrated node.
+-    $table_name = $this->getMigration('d7_node:page')->getIdMap()->mapTableName();
+-    $this->container->get('database')->insert($table_name)
++    $id_map = $this->getMigration('d7_node:page')->getIdMap();
++    $table_name = $id_map->mapTableName();
++    $id_map->getDatabase()->insert($table_name)
+       ->fields([
+         'source_ids_hash' => 1,
+         'sourceid1' => 1,
+@@ -156,8 +157,9 @@ public function testDraftRevisionIdConflicts() {
+ 
+     // Insert data in the d7_node_revision:page migration mappping table to
+     // simulate a previously migrated node revison.
+-    $table_name = $this->getMigration('d7_node_revision:page')->getIdMap()->mapTableName();
+-    $this->container->get('database')->insert($table_name)
++    $id_map = $this->getMigration('d7_node_revision:page')->getIdMap();
++    $table_name = $id_map->mapTableName();
++    $id_map->getDatabase()->insert($table_name)
+       ->fields([
+         'source_ids_hash' => 1,
+         'sourceid1' => 1,
diff --git a/core/composer.json b/core/composer.json
index 7f20fc7..e9196a6 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -18,7 +18,7 @@
         "symfony/process": "~3.4.0",
         "symfony/polyfill-iconv": "^1.0",
         "symfony/yaml": "~3.4.0",
-        "twig/twig": "^1.35.0",
+        "twig/twig": "^1.35.0|^2.4.0",
         "doctrine/common": "^2.5",
         "doctrine/annotations": "^1.2",
         "guzzlehttp/guzzle": "^6.2.1",
diff --git a/core/lib/Drupal/Core/Template/Loader/StringLoader.php b/core/lib/Drupal/Core/Template/Loader/StringLoader.php
index 6325b9e..d0590a2 100644
--- a/core/lib/Drupal/Core/Template/Loader/StringLoader.php
+++ b/core/lib/Drupal/Core/Template/Loader/StringLoader.php
@@ -44,6 +44,14 @@ public function getSource($name) {
   /**
    * {@inheritdoc}
    */
+  public function getSourceContext($name) {
+    $name = (string) $name;
+    return new \Twig_Source($name, $name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getCacheKey($name) {
     return $name;
   }
diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php
index 21755a5..5271155 100644
--- a/core/lib/Drupal/Core/Template/TwigEnvironment.php
+++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php
@@ -25,6 +25,8 @@ class TwigEnvironment extends \Twig_Environment {
 
   protected $twigCachePrefix = '';
 
+  protected $templateClassPrefix = '__TwigTemplate_';
+
   /**
    * Constructs a TwigEnvironment object and stores cache and storage
    * internally.
@@ -42,7 +44,7 @@ class TwigEnvironment extends \Twig_Environment {
    * @param array $options
    *   The options for the Twig environment.
    */
-  public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, StateInterface $state, \Twig_LoaderInterface $loader = NULL, $options = []) {
+  public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, StateInterface $state, \Twig_LoaderInterface $loader = NULL, array $options = []) {
     // Ensure that twig.engine is loaded, given that it is needed to render a
     // template because functions like TwigExtension::escapeFilter() are called.
     require_once $root . '/core/themes/engines/twig/twig.engine';
@@ -57,11 +59,6 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension
     ];
     // Ensure autoescaping is always on.
     $options['autoescape'] = 'html';
-
-    $policy = new TwigSandboxPolicy();
-    $sandbox = new \Twig_Extension_Sandbox($policy, TRUE);
-    $this->addExtension($sandbox);
-
     if ($options['cache'] === TRUE) {
       $current = $state->get('twig_extension_hash_prefix', ['twig_extension_hash' => '']);
       if ($current['twig_extension_hash'] !== $twig_extension_hash || empty($current['twig_cache_prefix'])) {
@@ -78,8 +75,11 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension
       $options['cache'] = new TwigPhpStorageCache($cache, $this->twigCachePrefix);
     }
 
-    $this->loader = $loader;
-    parent::__construct($this->loader, $options);
+    $this->setLoader($loader);
+    parent::__construct($this->getLoader(), $options);
+    $policy = new TwigSandboxPolicy();
+    $sandbox = new \Twig_Extension_Sandbox($policy, TRUE);
+    $this->addExtension($sandbox);
   }
 
   /**
@@ -110,7 +110,7 @@ public function getTemplateClass($name, $index = NULL) {
     // node.html.twig for the output of each node and the same compiled class.
     $cache_index = $name . (NULL === $index ? '' : '_' . $index);
     if (!isset($this->templateClasses[$cache_index])) {
-      $this->templateClasses[$cache_index] = $this->templateClassPrefix . hash('sha256', $this->loader->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index);
+      $this->templateClasses[$cache_index] = $this->templateClassPrefix . hash('sha256', $this->getLoader()->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index);
     }
     return $this->templateClasses[$cache_index];
   }
@@ -140,7 +140,7 @@ public function getTemplateClass($name, $index = NULL) {
   public function renderInline($template_string, array $context = []) {
     // Prefix all inline templates with a special comment.
     $template_string = '{# inline_template_start #}' . $template_string;
-    return Markup::create($this->loadTemplate($template_string, NULL)->render($context));
+    return Markup::create($this->createTemplate($template_string)->render($context));
   }
 
 }
diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php
index 377efe6..7f2738f 100644
--- a/core/lib/Drupal/Core/Template/TwigExtension.php
+++ b/core/lib/Drupal/Core/Template/TwigExtension.php
@@ -65,7 +65,7 @@ class TwigExtension extends \Twig_Extension {
    * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
    *   The date formatter.
    */
-  public function __construct(RendererInterface $renderer, UrlGeneratorInterface $url_generator, ThemeManagerInterface $theme_manager, DateFormatterInterface $date_formatter) {
+  public function __construct(RendererInterface $renderer, UrlGeneratorInterface $url_generator = NULL, ThemeManagerInterface $theme_manager = NULL, DateFormatterInterface $date_formatter = NULL) {
     $this->renderer = $renderer;
     $this->urlGenerator = $url_generator;
     $this->themeManager = $theme_manager;
@@ -177,7 +177,7 @@ public function getFilters() {
       new \Twig_SimpleFilter('safe_join', [$this, 'safeJoin'], ['needs_environment' => TRUE, 'is_safe' => ['html']]),
 
       // Array filters.
-      new \Twig_SimpleFilter('without', 'twig_without'),
+      new \Twig_SimpleFilter('without', [$this, 'withoutFilter']),
 
       // CSS class and ID filters.
       new \Twig_SimpleFilter('clean_class', '\Drupal\Component\Utility\Html::getClass'),
@@ -630,4 +630,35 @@ public function createAttribute(array $attributes = []) {
     return new Attribute($attributes);
   }
 
+  /**
+   * Removes child elements from a copy of the original array.
+   *
+   * Creates a copy of the renderable array and removes child elements by key
+   * specified through filter's arguments. The copy can be printed without these
+   * elements. The original renderable array is still available and can be used
+   * to print child elements in their entirety in the twig template.
+   *
+   * @param array|object $element
+   *   The parent renderable array to exclude the child items.
+   *
+   * @return array
+   *   The filtered renderable array.
+   */
+  public function withoutFilter($element) {
+    if ($element instanceof \ArrayAccess) {
+      $filtered_element = clone $element;
+    }
+    else {
+      $filtered_element = $element;
+    }
+    $args = func_get_args();
+    unset($args[0]);
+    foreach ($args as $arg) {
+      if (isset($filtered_element[$arg])) {
+        unset($filtered_element[$arg]);
+      }
+    }
+    return $filtered_element;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
index c0691db..1d308b0 100644
--- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php
+++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
@@ -17,13 +17,21 @@ class TwigNodeTrans extends \Twig_Node {
   /**
    * {@inheritdoc}
    */
-  public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) {
-    parent::__construct([
-      'count' => $count,
-      'body' => $body,
-      'plural' => $plural,
-      'options' => $options,
-    ], [], $lineno, $tag);
+  public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node $options = NULL, $lineno, $tag = NULL) {
+    $nodes = [];
+    if (NULL !== $body) {
+      $nodes['body'] = $body;
+    }
+    if (NULL !== $count) {
+      $nodes['count'] = $count;
+    }
+    if (NULL !== $plural) {
+      $nodes['plural'] = $plural;
+    }
+    if (NULL !== $options) {
+      $nodes['options'] = $options;
+    }
+    parent::__construct($nodes, [], $lineno, $tag);
   }
 
   /**
@@ -32,29 +40,27 @@ public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_N
   public function compile(\Twig_Compiler $compiler) {
     $compiler->addDebugInfo($this);
 
-    $options = $this->getNode('options');
-
     list($singular, $tokens) = $this->compileString($this->getNode('body'));
     $plural = NULL;
 
-    if (NULL !== $this->getNode('plural')) {
+    if ($this->hasNode('plural')) {
       list($plural, $pluralTokens) = $this->compileString($this->getNode('plural'));
       $tokens = array_merge($tokens, $pluralTokens);
     }
 
     // Start writing with the function to be called.
-    $compiler->write('echo ' . (empty($plural) ? 't' : '\Drupal::translation()->formatPlural') . '(');
+    $compiler->write('echo ' . ($this->hasNode('plural') ? '\Drupal::translation()->formatPlural' : 't') . '(');
 
     // Move the count to the beginning of the parameters list.
-    if (!empty($plural)) {
-      $compiler->raw('abs(')->subcompile($this->getNode('count'))->raw('), ');
+    if ($this->hasNode('plural')) {
+      $compiler->raw('abs(')->subcompile($this->hasNode('count') ? $this->getNode('count') : NULL)->raw('), ');
     }
 
     // Write the singular text parameter.
     $compiler->subcompile($singular);
 
     // Write the plural text parameter, if necessary.
-    if (!empty($plural)) {
+    if ($this->hasNode('plural')) {
       $compiler->raw(', ')->subcompile($plural);
     }
 
@@ -67,8 +73,8 @@ public function compile(\Twig_Compiler $compiler) {
     $compiler->raw(')');
 
     // Write any options passed.
-    if (!empty($options)) {
-      $compiler->raw(', ')->subcompile($options);
+    if ($this->hasNode('options')) {
+      $compiler->raw(', ')->subcompile($this->getNode('options'));
     }
 
     // Write function closure.
@@ -103,7 +109,7 @@ protected function compileString(\Twig_Node $body) {
       $text = '';
 
       foreach ($body as $node) {
-        if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof \Twig_Node_SetTemp) {
+        if (get_class($node) === 'Twig_Node') {
           $node = $node->getNode(1);
         }
 
diff --git a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
index 96f5560..559319c 100644
--- a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
+++ b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
@@ -21,10 +21,10 @@ class TwigTransTokenParser extends \Twig_TokenParser {
   public function parse(\Twig_Token $token) {
     $lineno = $token->getLine();
     $stream = $this->parser->getStream();
-    $body = NULL;
-    $options = NULL;
-    $count = NULL;
     $plural = NULL;
+    $count = NULL;
+    $options = NULL;
+    $body = NULL;
 
     if (!$stream->test(\Twig_Token::BLOCK_END_TYPE) && $stream->test(\Twig_Token::STRING_TYPE)) {
       $body = $this->parser->getExpressionParser()->parseExpression();
@@ -55,14 +55,14 @@ public function parse(\Twig_Token $token) {
   /**
    * Detect a 'plural' switch or the end of a 'trans' tag.
    */
-  public function decideForFork($token) {
+  public function decideForFork(\Twig_Token $token) {
     return $token->test(['plural', 'endtrans']);
   }
 
   /**
    * Detect the end of a 'trans' tag.
    */
-  public function decideForEnd($token) {
+  public function decideForEnd(\Twig_Token $token) {
     return $token->test('endtrans');
   }
 
diff --git a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php
index 93be201..9888caa 100644
--- a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php
+++ b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php
@@ -21,7 +21,7 @@ class TestExtension extends \Twig_Extension {
    */
   public function getFunctions() {
     return [
-      'testfunc' => new \Twig_Function_Function(['Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFunction']),
+      new \Twig_SimpleFunction('testfunc', [$this, 'testFunction']),
     ];
   }
 
@@ -39,7 +39,7 @@ public function getFunctions() {
    */
   public function getFilters() {
     return [
-      'testfilter' => new \Twig_Filter_Function(['Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFilter']),
+      new \Twig_SimpleFilter('testfilter', [$this, 'testFilter']),
     ];
   }
 
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
index 5d6f61f..d93a73d 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
@@ -132,12 +132,17 @@ public function testCacheFilename() {
     $expected = strlen($prefix) + 2 + 2 * TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH;
     $this->assertEquals($expected, strlen($key));
 
-    $original_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig');
+    $template_path = 'core/modules/system/templates/container.html.twig';
+    $cache = $environment->getCache();
+    $class = $environment->getTemplateClass($template_path);
+    $original_filename = $cache->generateKey($template_path, $class);
     \Drupal::getContainer()->set('twig', NULL);
 
     \Drupal::service('module_installer')->install(['twig_extension_test']);
     $environment = \Drupal::service('twig');
-    $new_extension_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig');
+    $cache = $environment->getCache();
+    $class = $environment->getTemplateClass($template_path);
+    $new_extension_filename = $cache->generateKey($template_path, $class);
     \Drupal::getContainer()->set('twig', NULL);
 
     $this->assertNotEqual($new_extension_filename, $original_filename);
diff --git a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php
index c9bccce..e5e7e9a 100644
--- a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php
+++ b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php
@@ -265,10 +265,10 @@ public function testChainAddRemoveClasses() {
    * @group legacy
    */
   public function testTwigAddRemoveClasses($template, $expected, $seed_attributes = []) {
-    $loader = new \Twig_Loader_String();
+    $loader = new \Twig_Loader_Array([]);
     $twig = new \Twig_Environment($loader);
     $data = ['attributes' => new Attribute($seed_attributes)];
-    $result = $twig->render($template, $data);
+    $result = $twig->createTemplate($template)->render($data);
     $this->assertEquals($expected, $result);
   }
 
@@ -281,7 +281,7 @@ public function testTwigAddRemoveClasses($template, $expected, $seed_attributes
    */
   public function providerTestAttributeClassHelpers() {
     return [
-      ["{{ attributes.class }}", ''],
+      ["{{ attributes }}", ''],
       ["{{ attributes.addClass('everest').class }}", 'everest'],
       ["{{ attributes.addClass(['k2', 'kangchenjunga']).class }}", 'k2 kangchenjunga'],
       ["{{ attributes.addClass('lhotse', 'makalu', 'cho-oyu').class }}", 'lhotse makalu cho-oyu'],
diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
index a872e30..4e790d8 100644
--- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
+++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php
@@ -11,6 +11,19 @@
 use Drupal\Core\Url;
 use Drupal\Tests\UnitTestCase;
 
+if (!function_exists(__NAMESPACE__ . 't')) {
+
+  function t($string, array $args = []) {
+    return strtr($string, $args);
+  }
+
+}
+if (!function_exists(__NAMESPACE__ . 'file_create_url')) {
+
+  function file_create_url() {}
+
+}
+
 /**
  * Tests the twig extension.
  *
@@ -78,7 +91,8 @@ public function setUp() {
    * @group legacy
    */
   public function testEscaping($template, $expected) {
-    $twig = new \Twig_Environment(NULL, [
+    $loader = new \Twig_Loader_Filesystem();
+    $twig = new \Twig_Environment($loader, [
       'debug' => TRUE,
       'cache' => FALSE,
       'autoescape' => 'html',
@@ -86,7 +100,8 @@ public function testEscaping($template, $expected) {
     ]);
     $twig->addExtension($this->systemUnderTest);
 
-    $nodes = $twig->parse($twig->tokenize($template));
+    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
+    $nodes = $twig->parse($twig->tokenize(new \Twig_Source($template, $name)));
 
     $this->assertSame($expected, $nodes->getNode('body')
       ->getNode(0)
@@ -140,10 +155,11 @@ public function testActiveTheme() {
       ->method('getActiveTheme')
       ->willReturn($active_theme);
 
-    $loader = new \Twig_Loader_String();
+    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
+    $loader = new \Twig_Loader_Array([$name => '{{ active_theme() }}']);
     $twig = new \Twig_Environment($loader);
     $twig->addExtension($this->systemUnderTest);
-    $result = $twig->render('{{ active_theme() }}');
+    $result = $twig->render($name);
     $this->assertEquals('test_theme', $result);
   }
 
@@ -177,10 +193,11 @@ public function testActiveThemePath() {
       ->method('getActiveTheme')
       ->willReturn($active_theme);
 
-    $loader = new \Twig_Loader_String();
+    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
+    $loader = new \Twig_Loader_Array([$name => '{{ active_theme_path() }}']);
     $twig = new \Twig_Environment($loader);
     $twig->addExtension($this->systemUnderTest);
-    $result = $twig->render('{{ active_theme_path() }}');
+    $result = $twig->render($name);
     $this->assertEquals('foo/bar', $result);
   }
 
@@ -192,7 +209,8 @@ public function testActiveThemePath() {
    * @group legacy
    */
   public function testSafeStringEscaping() {
-    $twig = new \Twig_Environment(NULL, [
+    $loader = new \Twig_Loader_Filesystem();
+    $twig = new \Twig_Environment($loader, [
       'debug' => TRUE,
       'cache' => FALSE,
       'autoescape' => 'html',
@@ -278,7 +296,8 @@ public function providerTestRenderVar() {
    * @group legacy
    */
   public function testEscapeWithGeneratedLink() {
-    $twig = new \Twig_Environment(NULL, [
+    $loader = new \Twig_Loader_Filesystem();
+    $twig = new \Twig_Environment($loader, [
         'debug' => TRUE,
         'cache' => FALSE,
         'autoescape' => 'html',
@@ -336,7 +355,8 @@ public function testRenderVarWithGeneratedLink() {
    * @covers ::createAttribute
    */
   public function testCreateAttribute() {
-    $loader = new StringLoader();
+    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
+    $loader = new \Twig_Loader_Array([$name => "{% for iteration in iterations %}<div{{ create_attribute(iteration) }}></div>{% endfor %}"]);
     $twig = new \Twig_Environment($loader);
     $twig->addExtension($this->systemUnderTest);
 
@@ -345,12 +365,15 @@ public function testCreateAttribute() {
       ['id' => 'puppies', 'data-value' => 'foo', 'data-lang' => 'en'],
       [],
     ];
-    $result = $twig->render("{% for iteration in iterations %}<div{{ create_attribute(iteration) }}></div>{% endfor %}", ['iterations' => $iterations]);
+    $result = $twig->render($name, ['iterations' => $iterations]);
     $expected = '<div class="kittens" data-toggle="modal" data-lang="es"></div><div id="puppies" data-value="foo" data-lang="en"></div><div></div>';
     $this->assertEquals($expected, $result);
 
     // Test default creation of empty attribute object and using its method.
-    $result = $twig->render("<div{{ create_attribute().addClass('meow') }}></div>");
+    $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), TRUE), FALSE));
+    $loader = new \Twig_Loader_Array(array($name => "<div{{ create_attribute().addClass('meow') }}></div>"));
+    $twig->setLoader($loader);
+    $result = $twig->render($name);
     $expected = '<div class="meow"></div>';
     $this->assertEquals($expected, $result);
   }
