[vm] Support multiple local variables with the same name in the same scope

When lowering patterns, front-end can add multiple distinct local
variables with the same name into the same local scope.
Previously, VM identified local variables in a local scope by name.
However, this no longer works with patterns.

With this change, local variables are now identified by pair (name,
kernel offset). Name is still taken into account as compiler can add
extra variables which do not correspond to kernel variables,
such as 'this'.

TEST=runtime/tests/vm/dart/regress_51091_test.dart
Fixes https://github.com/dart-lang/sdk/issues/51091
Issue https://github.com/dart-lang/sdk/issues/49755

Change-Id: I0263769cb31f3f8d9652f5d6534800510ac882fb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279650
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/tests/vm/dart/regress_51091_test.dart b/runtime/tests/vm/dart/regress_51091_test.dart
new file mode 100644
index 0000000..4d7c522
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_51091_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/51091.
+// Verifies that compiler doesn't crash if there are two local
+// variables with the same name in the same local scope.
+
+// SharedOptions=--enable-experiment=patterns
+
+import 'package:expect/expect.dart';
+
+void test1() {
+  if (0 case var x) {
+    Expect.equals(0, x);
+  }
+  var x = 1;
+  Expect.equals(1, x);
+}
+
+void test2(int arg) {
+  switch(arg) {
+    case == 0 && var x:
+      Expect.equals(0, x);
+    case == 1 && var x:
+      Expect.equals(1, x);
+  }
+  var x = 1;
+  Expect.equals(1, x);
+}
+
+void main() {
+  test1();
+  test2(0);
+  test2(1);
+}
\ No newline at end of file
diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h
index 4381a0a..2945b4a 100644
--- a/runtime/vm/compiler/backend/il_test_helper.h
+++ b/runtime/vm/compiler/backend/il_test_helper.h
@@ -291,10 +291,10 @@
   void AddVariable(const char* name,
                    const AbstractType& static_type,
                    CompileType* param_type = nullptr) {
-    LocalVariable* v =
-        new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
-                          String::Handle(Symbols::New(Thread::Current(), name)),
-                          static_type, param_type);
+    LocalVariable* v = new LocalVariable(
+        TokenPosition::kNoSource, TokenPosition::kNoSource,
+        String::Handle(Symbols::New(Thread::Current(), name)), static_type,
+        LocalVariable::kNoKernelOffset, param_type);
     v->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
     flow_graph()->parsed_function().scope()->AddVariable(v);
   }
diff --git a/runtime/vm/compiler/compiler_state.cc b/runtime/vm/compiler/compiler_state.cc
index c50e82d..a04df41 100644
--- a/runtime/vm/compiler/compiler_state.cc
+++ b/runtime/vm/compiler/compiler_state.cc
@@ -48,7 +48,8 @@
             Z, Symbols::NewFormatted(thread(), ":context_var%" Pd, index));
         LocalVariable* var = new (Z)
             LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
-                          name, dynamic_type, /*param_type=*/nullptr);
+                          name, dynamic_type, LocalVariable::kNoKernelOffset,
+                          /*param_type=*/nullptr);
         var->set_is_captured();
         var->set_index(VariableIndex(index));
         return var;
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index c84d6bd..80d2756 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -771,6 +771,7 @@
 LocalVariable* FlowGraphBuilder::LookupVariable(intptr_t kernel_offset) {
   LocalVariable* local = scopes_->locals.Lookup(kernel_offset);
   ASSERT(local != NULL);
+  ASSERT(local->kernel_offset() == kernel_offset);
   return local;
 }
 
@@ -1740,9 +1741,9 @@
   // and not the signature type.
   Type& klass_type = Type::ZoneHandle(Z, klass.DeclarationType());
 
-  LocalVariable* receiver_variable = new (Z)
-      LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
-                    Symbols::This(), klass_type, /*param_type=*/nullptr);
+  LocalVariable* receiver_variable = new (Z) LocalVariable(
+      TokenPosition::kNoSource, TokenPosition::kNoSource, Symbols::This(),
+      klass_type, LocalVariable::kNoKernelOffset, /*param_type=*/nullptr);
 
   receiver_variable->set_is_captured();
   //  receiver_variable->set_is_final();
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 9e73f48..811947f 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -278,7 +278,7 @@
               TokenPosition::kNoSource, TokenPosition::kNoSource,
               Symbols::Value(),
               AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos)),
-              &parameter_type);
+              LocalVariable::kNoKernelOffset, &parameter_type);
         } else {
           result_->setter_value = MakeVariable(
               TokenPosition::kNoSource, TokenPosition::kNoSource,
@@ -577,12 +577,12 @@
   // Mark known chained futures such as _Future::timeout()'s _future.
   if (function.recognized_kind() == MethodRecognizer::kFutureTimeout &&
       depth_.function_ == 1) {
-    LocalVariable* future = scope_->LookupVariable(Symbols::_future(), true);
+    LocalVariable* future = scope_->LookupVariableByName(Symbols::_future());
     ASSERT(future != nullptr);
     future->set_is_chained_future();
   } else if (function.recognized_kind() == MethodRecognizer::kFutureWait &&
              depth_.function_ == 1) {
-    LocalVariable* future = scope_->LookupVariable(Symbols::_future(), true);
+    LocalVariable* future = scope_->LookupVariableByName(Symbols::_future());
     ASSERT(future != nullptr);
     future->set_is_chained_future();
   }
@@ -1311,7 +1311,8 @@
 void ScopeBuilder::VisitVariableDeclaration() {
   PositionScope scope(&helper_.reader_);
 
-  intptr_t kernel_offset_no_tag = helper_.ReaderOffset();
+  const intptr_t kernel_offset =
+      helper_.data_program_offset_ + helper_.ReaderOffset();
   VariableDeclarationHelper helper(&helper_);
   helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
   AbstractType& type = BuildAndVisitVariableType();
@@ -1336,7 +1337,7 @@
     end_position = end_position.Next();
   }
   LocalVariable* variable =
-      MakeVariable(helper.position_, end_position, name, type);
+      MakeVariable(helper.position_, end_position, name, type, kernel_offset);
   if (helper.IsFinal()) {
     variable->set_is_final();
   }
@@ -1346,8 +1347,7 @@
   }
 
   scope_->AddVariable(variable);
-  result_->locals.Insert(helper_.data_program_offset_ + kernel_offset_no_tag,
-                         variable);
+  result_->locals.Insert(kernel_offset, variable);
 }
 
 AbstractType& ScopeBuilder::BuildAndVisitVariableType() {
@@ -1486,7 +1486,8 @@
       // The type argument vector is passed as the very first argument to the
       // factory constructor function.
       HandleSpecialLoad(&result_->type_arguments_variable,
-                        Symbols::TypeArgumentsParameter());
+                        Symbols::TypeArgumentsParameter(),
+                        LocalVariable::kNoKernelOffset);
     } else {
       // If the type parameter is a parameter to this or an enclosing function,
       // we can read it directly from the function type arguments vector later.
@@ -1608,9 +1609,12 @@
     intptr_t pos,
     ParameterTypeCheckMode type_check_mode,
     const ProcedureAttributesMetadata& attrs) {
-  intptr_t kernel_offset = helper_.ReaderOffset();  // no tag.
+  // Convert kernel offset of variable declaration to absolute.
+  const intptr_t kernel_offset =
+      helper_.data_program_offset_ + helper_.ReaderOffset();
+  // MetadataHelper expects relative offsets and adjusts them internally
   const InferredTypeMetadata parameter_type =
-      inferred_type_metadata_helper_.GetInferredType(kernel_offset);
+      inferred_type_metadata_helper_.GetInferredType(helper_.ReaderOffset());
   VariableDeclarationHelper helper(&helper_);
   helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
   String& name = H.DartSymbolObfuscate(helper.name_index_);
@@ -1619,8 +1623,9 @@
   helper.SetJustRead(VariableDeclarationHelper::kType);
   helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
 
-  LocalVariable* variable = MakeVariable(helper.position_, helper.position_,
-                                         name, type, &parameter_type);
+  LocalVariable* variable =
+      MakeVariable(helper.position_, helper.position_, name, type,
+                   kernel_offset, &parameter_type);
   if (helper.IsFinal()) {
     variable->set_is_final();
   }
@@ -1681,8 +1686,7 @@
   }
 
   scope_->InsertParameterAt(pos, variable);
-  result_->locals.Insert(helper_.data_program_offset_ + kernel_offset,
-                         variable);
+  result_->locals.Insert(kernel_offset, variable);
 
   // The default value may contain 'let' bindings for which the constant
   // evaluator needs scope bindings.
@@ -1697,6 +1701,7 @@
     TokenPosition token_pos,
     const String& name,
     const AbstractType& type,
+    intptr_t kernel_offset,
     const InferredTypeMetadata* param_type_md /* = NULL */) {
   CompileType* param_type = nullptr;
   const Object* param_value = nullptr;
@@ -1707,7 +1712,7 @@
     }
   }
   return new (Z) LocalVariable(declaration_pos, token_pos, name, type,
-                               param_type, param_value);
+                               kernel_offset, param_type, param_value);
 }
 
 void ScopeBuilder::AddExceptionVariable(
@@ -1823,7 +1828,7 @@
 LocalVariable* ScopeBuilder::LookupVariable(
     intptr_t declaration_binary_offset) {
   LocalVariable* variable = result_->locals.Lookup(declaration_binary_offset);
-  if (variable == NULL) {
+  if (variable == nullptr) {
     // We have not seen a declaration of the variable, so it must be the
     // case that we are compiling a nested function and the variable is
     // declared in an outer scope.  In that case, look it up in the scope by
@@ -1834,11 +1839,13 @@
         parsed_function_->function());
 
     const String& name = H.DartSymbolObfuscate(var_name);
-    variable = current_function_scope_->parent()->LookupVariable(name, true);
-    ASSERT(variable != NULL);
+    variable = current_function_scope_->parent()->LookupVariable(
+        name, declaration_binary_offset, true);
+    ASSERT(variable != nullptr);
     result_->locals.Insert(declaration_binary_offset, variable);
   }
 
+  ASSERT(variable->owner() != nullptr);
   if (variable->owner()->function_level() < scope_->function_level()) {
     // We call `LocalScope->CaptureVariable(variable)` in two scenarios for two
     // different reasons:
@@ -1885,8 +1892,8 @@
       current_function_scope_->parent() != nullptr) {
     // Lazily populate receiver variable using the parent function scope.
     parsed_function_->set_receiver_var(
-        current_function_scope_->parent()->LookupVariable(Symbols::This(),
-                                                          true));
+        current_function_scope_->parent()->LookupVariable(
+            Symbols::This(), LocalVariable::kNoKernelOffset, true));
   }
 
   if ((current_function_scope_->parent() != nullptr) ||
@@ -1899,13 +1906,14 @@
 }
 
 void ScopeBuilder::HandleSpecialLoad(LocalVariable** variable,
-                                     const String& symbol) {
+                                     const String& symbol,
+                                     intptr_t kernel_offset) {
   if (current_function_scope_->parent() != NULL) {
     // We are building the scope tree of a closure function and saw [node]. We
     // lazily populate the variable using the parent function scope.
     if (*variable == NULL) {
-      *variable =
-          current_function_scope_->parent()->LookupVariable(symbol, true);
+      *variable = current_function_scope_->parent()->LookupVariable(
+          symbol, kernel_offset, true);
       ASSERT(*variable != NULL);
     }
   }
@@ -1919,14 +1927,5 @@
   }
 }
 
-void ScopeBuilder::LookupCapturedVariableByName(LocalVariable** variable,
-                                                const String& name) {
-  if (*variable == NULL) {
-    *variable = scope_->LookupVariable(name, true);
-    ASSERT(*variable != NULL);
-    scope_->CaptureVariable(*variable);
-  }
-}
-
 }  // namespace kernel
 }  // namespace dart
diff --git a/runtime/vm/compiler/frontend/scope_builder.h b/runtime/vm/compiler/frontend/scope_builder.h
index bd962b3..d6ba189 100644
--- a/runtime/vm/compiler/frontend/scope_builder.h
+++ b/runtime/vm/compiler/frontend/scope_builder.h
@@ -98,11 +98,13 @@
       ParameterTypeCheckMode type_check_mode,
       const ProcedureAttributesMetadata& attrs);
 
-  LocalVariable* MakeVariable(TokenPosition declaration_pos,
-                              TokenPosition token_pos,
-                              const String& name,
-                              const AbstractType& type,
-                              const InferredTypeMetadata* param_type_md = NULL);
+  LocalVariable* MakeVariable(
+      TokenPosition declaration_pos,
+      TokenPosition token_pos,
+      const String& name,
+      const AbstractType& type,
+      intptr_t kernel_offset = LocalVariable::kNoKernelOffset,
+      const InferredTypeMetadata* param_type_md = NULL);
 
   void AddExceptionVariable(GrowableArray<LocalVariable*>* variables,
                             const char* prefix,
@@ -130,9 +132,9 @@
   const String& GenerateName(const char* prefix, intptr_t suffix);
 
   void HandleLoadReceiver();
-  void HandleSpecialLoad(LocalVariable** variable, const String& symbol);
-  void LookupCapturedVariableByName(LocalVariable** variable,
-                                    const String& name);
+  void HandleSpecialLoad(LocalVariable** variable,
+                         const String& symbol,
+                         intptr_t kernel_offset);
 
   struct DepthState {
     explicit DepthState(intptr_t function)
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index 57ab1ff..bde95d9 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -38,7 +38,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 12;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -714,7 +714,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 8;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 64;
+static constexpr dart::compiler::target::word ContextScope_element_size = 72;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -1401,7 +1401,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 12;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -2077,7 +2077,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 8;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 64;
+static constexpr dart::compiler::target::word ContextScope_element_size = 72;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -2765,7 +2765,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 16;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -3450,7 +3450,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 16;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -4136,7 +4136,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 12;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -4814,7 +4814,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 8;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 64;
+static constexpr dart::compiler::target::word ContextScope_element_size = 72;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -5501,7 +5501,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 12;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -6169,7 +6169,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 8;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 64;
+static constexpr dart::compiler::target::word ContextScope_element_size = 72;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -6848,7 +6848,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 12;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -7516,7 +7516,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 8;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 64;
+static constexpr dart::compiler::target::word ContextScope_element_size = 72;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -8196,7 +8196,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 16;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -8873,7 +8873,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 16;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -9551,7 +9551,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 4;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 12;
-static constexpr dart::compiler::target::word ContextScope_element_size = 32;
+static constexpr dart::compiler::target::word ContextScope_element_size = 36;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -10221,7 +10221,7 @@
 static constexpr dart::compiler::target::word Context_element_size = 8;
 static constexpr dart::compiler::target::word
     ContextScope_elements_start_offset = 16;
-static constexpr dart::compiler::target::word ContextScope_element_size = 64;
+static constexpr dart::compiler::target::word ContextScope_element_size = 72;
 static constexpr dart::compiler::target::word
     ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word ExceptionHandlers_element_size =
@@ -10905,7 +10905,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 12;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    32;
+    36;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word
@@ -11657,7 +11657,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    64;
+    72;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word
@@ -12417,7 +12417,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    64;
+    72;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word
@@ -13175,7 +13175,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    32;
+    36;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 16;
 static constexpr dart::compiler::target::word
@@ -13933,7 +13933,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    32;
+    36;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 16;
 static constexpr dart::compiler::target::word
@@ -14692,7 +14692,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 12;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    32;
+    36;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word
@@ -15446,7 +15446,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    64;
+    72;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word
@@ -16203,7 +16203,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 12;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    32;
+    36;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word
@@ -16946,7 +16946,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    64;
+    72;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word
@@ -17697,7 +17697,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    64;
+    72;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word
@@ -18446,7 +18446,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    32;
+    36;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 16;
 static constexpr dart::compiler::target::word
@@ -19195,7 +19195,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    32;
+    36;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 16;
 static constexpr dart::compiler::target::word
@@ -19945,7 +19945,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 12;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    32;
+    36;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 12;
 static constexpr dart::compiler::target::word
@@ -20690,7 +20690,7 @@
 static constexpr dart::compiler::target::word
     AOT_ContextScope_elements_start_offset = 16;
 static constexpr dart::compiler::target::word AOT_ContextScope_element_size =
-    64;
+    72;
 static constexpr dart::compiler::target::word
     AOT_ExceptionHandlers_elements_start_offset = 24;
 static constexpr dart::compiler::target::word
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index bfe8700..04173cc 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -18626,6 +18626,15 @@
   untag()->set_context_level_at(scope_index, Smi::New(context_level));
 }
 
+intptr_t ContextScope::KernelOffsetAt(intptr_t scope_index) const {
+  return Smi::Value(untag()->kernel_offset_at(scope_index));
+}
+
+void ContextScope::SetKernelOffsetAt(intptr_t scope_index,
+                                     intptr_t kernel_offset) const {
+  untag()->set_kernel_offset_at(scope_index, Smi::New(kernel_offset));
+}
+
 const char* ContextScope::ToCString() const {
   const char* prev_cstr = "ContextScope:";
   String& name = String::Handle();
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 324efaf..5498175 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -7220,6 +7220,9 @@
   intptr_t ContextLevelAt(intptr_t scope_index) const;
   void SetContextLevelAt(intptr_t scope_index, intptr_t context_level) const;
 
+  intptr_t KernelOffsetAt(intptr_t scope_index) const;
+  void SetKernelOffsetAt(intptr_t scope_index, intptr_t kernel_offset) const;
+
   static const intptr_t kBytesPerElement =
       sizeof(UntaggedContextScope::VariableDesc);
   static const intptr_t kMaxElements = kSmiMax / kBytesPerElement;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index f54e584..0bb6fde 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -2579,30 +2579,39 @@
   parent_scope->AddVariable(var_c);
 
   bool test_only = false;  // Please, insert alias.
-  var_ta = local_scope->LookupVariable(ta, test_only);
+  var_ta = local_scope->LookupVariable(ta, LocalVariable::kNoKernelOffset,
+                                       test_only);
   EXPECT(var_ta->is_captured());
   EXPECT_EQ(parent_scope_function_level, var_ta->owner()->function_level());
-  EXPECT(local_scope->LocalLookupVariable(ta) == var_ta);  // Alias.
+  EXPECT(local_scope->LocalLookupVariable(ta, LocalVariable::kNoKernelOffset) ==
+         var_ta);  // Alias.
 
-  var_a = local_scope->LookupVariable(a, test_only);
+  var_a =
+      local_scope->LookupVariable(a, LocalVariable::kNoKernelOffset, test_only);
   EXPECT(var_a->is_captured());
   EXPECT_EQ(parent_scope_function_level, var_a->owner()->function_level());
-  EXPECT(local_scope->LocalLookupVariable(a) == var_a);  // Alias.
+  EXPECT(local_scope->LocalLookupVariable(a, LocalVariable::kNoKernelOffset) ==
+         var_a);  // Alias.
 
-  var_b = local_scope->LookupVariable(b, test_only);
+  var_b =
+      local_scope->LookupVariable(b, LocalVariable::kNoKernelOffset, test_only);
   EXPECT(!var_b->is_captured());
   EXPECT_EQ(local_scope_function_level, var_b->owner()->function_level());
-  EXPECT(local_scope->LocalLookupVariable(b) == var_b);
+  EXPECT(local_scope->LocalLookupVariable(b, LocalVariable::kNoKernelOffset) ==
+         var_b);
 
   test_only = true;  // Please, do not insert alias.
-  var_c = local_scope->LookupVariable(c, test_only);
+  var_c =
+      local_scope->LookupVariable(c, LocalVariable::kNoKernelOffset, test_only);
   EXPECT(!var_c->is_captured());
   EXPECT_EQ(parent_scope_function_level, var_c->owner()->function_level());
   // c is not in local_scope.
-  EXPECT(local_scope->LocalLookupVariable(c) == NULL);
+  EXPECT(local_scope->LocalLookupVariable(c, LocalVariable::kNoKernelOffset) ==
+         NULL);
 
   test_only = false;  // Please, insert alias.
-  var_c = local_scope->LookupVariable(c, test_only);
+  var_c =
+      local_scope->LookupVariable(c, LocalVariable::kNoKernelOffset, test_only);
   EXPECT(var_c->is_captured());
 
   EXPECT_EQ(4, local_scope->num_variables());         // ta, a, b, c.
@@ -2629,22 +2638,23 @@
   LocalScope* outer_scope = LocalScope::RestoreOuterScope(context_scope);
   EXPECT_EQ(3, outer_scope->num_variables());
 
-  var_ta = outer_scope->LocalLookupVariable(ta);
+  var_ta = outer_scope->LocalLookupVariable(ta, LocalVariable::kNoKernelOffset);
   EXPECT(var_ta->is_captured());
   EXPECT_EQ(0, var_ta->index().value());  // First index.
   EXPECT_EQ(parent_scope_context_level - local_scope_context_level,
             var_ta->owner()->context_level());  // Adjusted context level.
 
-  var_a = outer_scope->LocalLookupVariable(a);
+  var_a = outer_scope->LocalLookupVariable(a, LocalVariable::kNoKernelOffset);
   EXPECT(var_a->is_captured());
   EXPECT_EQ(1, var_a->index().value());  // First index.
   EXPECT_EQ(parent_scope_context_level - local_scope_context_level,
             var_a->owner()->context_level());  // Adjusted context level.
 
   // var b was not captured.
-  EXPECT(outer_scope->LocalLookupVariable(b) == NULL);
+  EXPECT(outer_scope->LocalLookupVariable(b, LocalVariable::kNoKernelOffset) ==
+         NULL);
 
-  var_c = outer_scope->LocalLookupVariable(c);
+  var_c = outer_scope->LocalLookupVariable(c, LocalVariable::kNoKernelOffset);
   EXPECT(var_c->is_captured());
   EXPECT_EQ(2, var_c->index().value());
   EXPECT_EQ(parent_scope_context_level - local_scope_context_level,
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index d645daf..d507be9 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -199,11 +199,12 @@
       String& tmp = String::ZoneHandle(Z);
       tmp = Symbols::FromConcat(T, Symbols::OriginalParam(), variable->name());
 
-      RELEASE_ASSERT(scope->LocalLookupVariable(tmp) == NULL);
+      RELEASE_ASSERT(scope->LocalLookupVariable(
+                         tmp, variable->kernel_offset()) == nullptr);
       raw_parameter = new LocalVariable(
           variable->declaration_token_pos(), variable->token_pos(), tmp,
-          variable->type(), variable->parameter_type(),
-          variable->parameter_value());
+          variable->type(), variable->kernel_offset(),
+          variable->parameter_type(), variable->parameter_value());
       if (variable->is_explicit_covariant_parameter()) {
         raw_parameter->set_is_explicit_covariant_parameter();
       }
@@ -247,11 +248,13 @@
       tmp = Symbols::FromConcat(T, Symbols::OriginalParam(),
                                 function_type_arguments_->name());
 
-      ASSERT(scope->LocalLookupVariable(tmp) == NULL);
+      ASSERT(scope->LocalLookupVariable(
+                 tmp, function_type_arguments_->kernel_offset()) == nullptr);
       raw_type_args_parameter =
-          new LocalVariable(raw_type_args_parameter->declaration_token_pos(),
-                            raw_type_args_parameter->token_pos(), tmp,
-                            raw_type_args_parameter->type());
+          new LocalVariable(function_type_arguments_->declaration_token_pos(),
+                            function_type_arguments_->token_pos(), tmp,
+                            function_type_arguments_->type(),
+                            function_type_arguments_->kernel_offset());
       bool ok = scope->AddVariable(raw_type_args_parameter);
       ASSERT(ok);
     }
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 5dcd450..ade9305 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -2346,6 +2346,7 @@
     };
     CompressedSmiPtr context_index;
     CompressedSmiPtr context_level;
+    CompressedSmiPtr kernel_offset;
   };
 
   int32_t num_variables_;
@@ -2385,6 +2386,7 @@
   DEFINE_ACCESSOR(InstancePtr, value)
   DEFINE_ACCESSOR(SmiPtr, context_index)
   DEFINE_ACCESSOR(SmiPtr, context_level)
+  DEFINE_ACCESSOR(SmiPtr, kernel_offset)
 #undef DEFINE_ACCESSOR
 
   CompressedObjectPtr* to(intptr_t num_vars) {
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index 4dacf03..d1ea4dc 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -56,7 +56,8 @@
 
 bool LocalScope::AddVariable(LocalVariable* variable) {
   ASSERT(variable != NULL);
-  if (LocalLookupVariable(variable->name()) != NULL) {
+  if (LocalLookupVariable(variable->name(), variable->kernel_offset()) !=
+      nullptr) {
     return false;
   }
   variables_.Add(variable);
@@ -70,7 +71,8 @@
 
 bool LocalScope::InsertParameterAt(intptr_t pos, LocalVariable* parameter) {
   ASSERT(parameter != NULL);
-  if (LocalLookupVariable(parameter->name()) != NULL) {
+  if (LocalLookupVariable(parameter->name(), parameter->kernel_offset()) !=
+      nullptr) {
     return false;
   }
   variables_.InsertAt(pos, parameter);
@@ -354,22 +356,27 @@
   }
 }
 
-LocalVariable* LocalScope::LocalLookupVariable(const String& name) const {
+LocalVariable* LocalScope::LocalLookupVariable(const String& name,
+                                               intptr_t kernel_offset) const {
   ASSERT(name.IsSymbol());
   for (intptr_t i = 0; i < variables_.length(); i++) {
     LocalVariable* var = variables_[i];
     ASSERT(var->name().IsSymbol());
-    if (var->name().ptr() == name.ptr()) {
+    if ((var->name().ptr() == name.ptr()) &&
+        (var->kernel_offset() == kernel_offset)) {
       return var;
     }
   }
   return NULL;
 }
 
-LocalVariable* LocalScope::LookupVariable(const String& name, bool test_only) {
+LocalVariable* LocalScope::LookupVariable(const String& name,
+                                          intptr_t kernel_offset,
+                                          bool test_only) {
   LocalScope* current_scope = this;
   while (current_scope != NULL) {
-    LocalVariable* var = current_scope->LocalLookupVariable(name);
+    LocalVariable* var =
+        current_scope->LocalLookupVariable(name, kernel_offset);
     // If testing only, return the variable even if invisible.
     if ((var != NULL) && (!var->is_invisible_ || test_only)) {
       if (!test_only && (var->owner()->function_level() != function_level())) {
@@ -382,6 +389,20 @@
   return NULL;
 }
 
+LocalVariable* LocalScope::LookupVariableByName(const String& name) {
+  ASSERT(name.IsSymbol());
+  for (LocalScope* scope = this; scope != nullptr; scope = scope->parent()) {
+    for (intptr_t i = 0, n = scope->variables_.length(); i < n; ++i) {
+      LocalVariable* var = scope->variables_[i];
+      ASSERT(var->name().IsSymbol());
+      if (var->name().ptr() == name.ptr()) {
+        return var;
+      }
+    }
+  }
+  return nullptr;
+}
+
 void LocalScope::CaptureVariable(LocalVariable* variable) {
   ASSERT(variable != NULL);
 
@@ -476,6 +497,7 @@
       int adjusted_context_level =
           variable->owner()->context_level() - current_context_level;
       context_scope.SetContextLevelAt(captured_idx, adjusted_context_level);
+      context_scope.SetKernelOffsetAt(captured_idx, variable->kernel_offset());
       captured_idx++;
     }
   }
@@ -494,7 +516,8 @@
       variable = new LocalVariable(context_scope.DeclarationTokenIndexAt(i),
                                    context_scope.TokenIndexAt(i),
                                    String::ZoneHandle(context_scope.NameAt(i)),
-                                   Object::dynamic_type());
+                                   Object::dynamic_type(),
+                                   context_scope.KernelOffsetAt(i));
       variable->SetConstValue(
           Instance::ZoneHandle(context_scope.ConstValueAt(i)));
     } else {
@@ -502,7 +525,8 @@
           new LocalVariable(context_scope.DeclarationTokenIndexAt(i),
                             context_scope.TokenIndexAt(i),
                             String::ZoneHandle(context_scope.NameAt(i)),
-                            AbstractType::ZoneHandle(context_scope.TypeAt(i)));
+                            AbstractType::ZoneHandle(context_scope.TypeAt(i)),
+                            context_scope.KernelOffsetAt(i));
     }
     variable->set_is_captured();
     variable->set_index(VariableIndex(context_scope.ContextIndexAt(i)));
@@ -565,6 +589,7 @@
   context_scope.SetTypeAt(0, type);
   context_scope.SetContextIndexAt(0, 0);
   context_scope.SetContextLevelAt(0, 0);
+  context_scope.SetKernelOffsetAt(0, LocalVariable::kNoKernelOffset);
   ASSERT(context_scope.num_variables() == kNumCapturedVars);  // Verify count.
   return context_scope.ptr();
 }
diff --git a/runtime/vm/scopes.h b/runtime/vm/scopes.h
index 28cfaa3..b6e9a2d 100644
--- a/runtime/vm/scopes.h
+++ b/runtime/vm/scopes.h
@@ -72,15 +72,19 @@
 
 class LocalVariable : public ZoneAllocated {
  public:
+  static constexpr intptr_t kNoKernelOffset = -1;
+
   LocalVariable(TokenPosition declaration_pos,
                 TokenPosition token_pos,
                 const String& name,
                 const AbstractType& type,
+                intptr_t kernel_offset = kNoKernelOffset,
                 CompileType* parameter_type = nullptr,
                 const Object* parameter_value = nullptr)
       : declaration_pos_(declaration_pos),
         token_pos_(token_pos),
         name_(name),
+        kernel_offset_(kernel_offset),
         owner_(NULL),
         type_(type),
         parameter_type_(parameter_type),
@@ -105,6 +109,7 @@
   TokenPosition token_pos() const { return token_pos_; }
   TokenPosition declaration_token_pos() const { return declaration_pos_; }
   const String& name() const { return name_; }
+  intptr_t kernel_offset() const { return kernel_offset_; }
   LocalScope* owner() const { return owner_; }
   void set_owner(LocalScope* owner) {
     ASSERT(owner_ == NULL);
@@ -220,6 +225,7 @@
   const TokenPosition declaration_pos_;
   const TokenPosition token_pos_;
   const String& name_;
+  const intptr_t kernel_offset_;
   LocalScope* owner_;  // Local scope declaring this variable.
 
   const AbstractType& type_;  // Declaration type of local variable.
@@ -325,7 +331,7 @@
   int num_context_variables() const { return context_variables().length(); }
 
   // Add a variable to the scope. Returns false if a variable with the
-  // same name is already present.
+  // same name and kernel offset is already present.
   bool AddVariable(LocalVariable* variable);
 
   // Add a variable to the scope as a context allocated variable and assigns
@@ -339,14 +345,20 @@
   bool InsertParameterAt(intptr_t pos, LocalVariable* parameter);
 
   // Lookup a variable in this scope only.
-  LocalVariable* LocalLookupVariable(const String& name) const;
+  LocalVariable* LocalLookupVariable(const String& name,
+                                     intptr_t kernel_offset) const;
 
   // Lookup a variable in this scope and its parents. If the variable
   // is found in a parent scope and 'test_only' is not true, we insert
   // aliases of the variable in the current and intermediate scopes up to
   // the declaration scope in order to detect "used before declared" errors.
   // We mark a variable as 'captured' when applicable.
-  LocalVariable* LookupVariable(const String& name, bool test_only);
+  LocalVariable* LookupVariable(const String& name,
+                                intptr_t kernel_offset,
+                                bool test_only);
+
+  // Lookup a variable in this scope and its parents by name.
+  LocalVariable* LookupVariableByName(const String& name);
 
   // Mark this variable as captured by this scope.
   void CaptureVariable(LocalVariable* variable);
diff --git a/runtime/vm/scopes_test.cc b/runtime/vm/scopes_test.cc
index e54d49e..03b5b62 100644
--- a/runtime/vm/scopes_test.cc
+++ b/runtime/vm/scopes_test.cc
@@ -51,10 +51,14 @@
   EXPECT(!outer_scope->AddVariable(var_a));
 
   // Check the simple layout above.
-  EXPECT_EQ(var_a, outer_scope->LocalLookupVariable(a));
-  EXPECT_EQ(var_a, inner_scope1->LookupVariable(a, true));
-  EXPECT(outer_scope->LocalLookupVariable(b) == NULL);
-  EXPECT(inner_scope1->LocalLookupVariable(c) == NULL);
+  EXPECT_EQ(var_a, outer_scope->LocalLookupVariable(
+                       a, LocalVariable::kNoKernelOffset));
+  EXPECT_EQ(var_a, inner_scope1->LookupVariable(
+                       a, LocalVariable::kNoKernelOffset, true));
+  EXPECT(outer_scope->LocalLookupVariable(b, LocalVariable::kNoKernelOffset) ==
+         NULL);
+  EXPECT(inner_scope1->LocalLookupVariable(c, LocalVariable::kNoKernelOffset) ==
+         NULL);
 
   // Modify the local scopes to contain shadowing:
   // {  // outer_scope
@@ -69,8 +73,10 @@
   //   }
   // }
   EXPECT(inner_scope1->AddVariable(inner_var_a));
-  EXPECT_EQ(inner_var_a, inner_scope1->LookupVariable(a, true));
-  EXPECT(inner_scope1->LookupVariable(a, true) != var_a);
+  EXPECT_EQ(inner_var_a, inner_scope1->LookupVariable(
+                             a, LocalVariable::kNoKernelOffset, true));
+  EXPECT(inner_scope1->LookupVariable(a, LocalVariable::kNoKernelOffset,
+                                      true) != var_a);
 
   // Modify the local scopes with access of an outer scope variable:
   // {  // outer_scope
@@ -84,9 +90,11 @@
   //     L: ...
   //   }
   // }
-  EXPECT(inner_scope2->LocalLookupVariable(a) == NULL);
+  EXPECT(inner_scope2->LocalLookupVariable(a, LocalVariable::kNoKernelOffset) ==
+         NULL);
   EXPECT(inner_scope2->AddVariable(var_a));
-  EXPECT_EQ(var_a, inner_scope2->LocalLookupVariable(a));
+  EXPECT_EQ(var_a, inner_scope2->LocalLookupVariable(
+                       a, LocalVariable::kNoKernelOffset));
 
   EXPECT_EQ(1, outer_scope->num_variables());
   EXPECT_EQ(2, inner_scope1->num_variables());