Version 2.15.0-51.0.dev

Merge commit '0231a0841a37ab2abfe9c236258f67f9f1bfa4fe' into 'dev'
diff --git a/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart b/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart
index 86ef5bc..11940c4 100644
--- a/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart
+++ b/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart
@@ -6,7 +6,7 @@
 
 import 'dart:isolate';
 
-import 'json_benchmark.dart';
+import 'regexp_benchmark.dart';
 import 'latency.dart';
 
 main() async {
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index 49d514d..59a0623 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -1071,8 +1071,9 @@
           .toList();
       locations.sort();
       for (var location in locations) {
-        table.add(toRow(targetMetrics
-            .map((metrics) => metrics.locationMrrComputers[location]!)));
+        table.add(toRow(targetMetrics.map((metrics) =>
+            metrics.locationMrrComputers[location] ??
+            MeanReciprocalRankComputer(location))));
       }
     }
     rightJustifyColumns(table, range(1, table[0].length));
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 92573bd..9038287 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -1701,9 +1701,9 @@
 
     var functionRewrite = MethodInvocationResolver.getRewriteResult(node);
     if (functionRewrite != null) {
-      nullShortingTermination(node, discardType: true);
       _resolveRewrittenFunctionExpressionInvocation(
           functionRewrite, whyNotPromotedList);
+      nullShortingTermination(node, discardType: true);
     } else {
       nullShortingTermination(node);
     }
diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
index 339e46a..4fe187f 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
@@ -2542,6 +2542,34 @@
     );
   }
 
+  test_hasReceiver_interfaceQ_nullShorting_getter() async {
+    await assertNoErrorsInCode(r'''
+abstract class C {
+  void Function(C) get foo;
+}
+
+void f(C? c) {
+  c?.foo(c); // 1
+}
+''');
+
+    var invocation = findNode.functionExpressionInvocation('foo(c);');
+    assertElementNull(invocation);
+    assertInvokeType(invocation, 'void Function(C)');
+    assertType(invocation, 'void');
+
+    var foo = invocation.function as PropertyAccess;
+    assertType(foo, 'void Function(C)');
+    assertElement(foo.propertyName, findElement.getter('foo'));
+    assertType(foo.propertyName, 'void Function(C)');
+
+    assertSimpleIdentifier(
+      findNode.simple('c); // 1'),
+      element: findElement.parameter('c'),
+      type: 'C',
+    );
+  }
+
   test_hasReceiver_interfaceTypeQ_defined() async {
     await assertErrorsInCode(r'''
 class A {
diff --git a/pkg/vm_snapshot_analysis/test/utils.dart b/pkg/vm_snapshot_analysis/test/utils.dart
index 02e25bd..0d52b80 100644
--- a/pkg/vm_snapshot_analysis/test/utils.dart
+++ b/pkg/vm_snapshot_analysis/test/utils.dart
@@ -10,7 +10,7 @@
 
 final dartCompile = () {
   final sdkBin = path.dirname(Platform.executable);
-  final dartCmd = path.join(sdkBin, Platform.isWindows ? 'dart.bat' : 'dart');
+  final dartCmd = path.join(sdkBin, Platform.isWindows ? 'dart.exe' : 'dart');
 
   if (!File(dartCmd).existsSync()) {
     throw 'Failed to locate `dart` in the SDK';
diff --git a/runtime/observatory/tests/service/regress_46419_test.dart b/runtime/observatory/tests/service/regress_46419_test.dart
new file mode 100644
index 0000000..9e1ef46
--- /dev/null
+++ b/runtime/observatory/tests/service/regress_46419_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2021, 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.
+// VMOptions=--verbose-debug
+
+import 'package:observatory/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+// DO NOT REORDER BEYOND THIS POINT
+bool testing = false;
+void printSync() {
+  print('sync');
+  if (testing) {
+    // We'll never reach this code, but setting a breakpoint here will result in
+    // the breakpoint being resolved below at line 25.
+    print('unreachable'); // line 18, bp1
+  }
+}
+
+printSyncStar() sync* {
+  // We'll end up resolving breakpoint 1 to this location instead of at line 15
+  // if #46419 regresses.
+  print('sync*');
+}
+
+testeeDo() {
+  printSync();
+  final iterator = printSyncStar();
+
+  print('middle'); // Line 32, bp2
+
+  iterator.toList();
+}
+// END DO NOT REORDER SECTION
+
+late Breakpoint bp1;
+late Breakpoint bp2;
+
+final tests = <IsolateTest>[
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    await isolate.rootLibrary.load();
+    final script = isolate.rootLibrary.scripts[0];
+
+    bp1 = await isolate.addBreakpoint(script, 18);
+    print("BP1 - $bp1");
+    expect(bp1, isNotNull);
+    expect(bp1 is Breakpoint, isTrue);
+    bp2 = await isolate.addBreakpoint(script, 32);
+    print("BP2 - $bp2");
+    expect(bp2, isNotNull);
+  },
+  resumeIsolate,
+  (Isolate isolate) async {
+    final stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    await for (ServiceEvent event in stream) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        var bp = event.breakpoint;
+        print('Hit $bp');
+        expect(bp, bp2);
+        await isolate.resume();
+        break;
+      }
+    }
+  }
+];
+
+void main([args = const []]) => runIsolateTests(
+      args,
+      tests,
+      testeeConcurrent: testeeDo,
+      pause_on_start: true,
+    );
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 3baefe9..2059774 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -163,6 +163,7 @@
 regress_28443_test: SkipByDesign # Debugger is disabled in AOT mode.
 regress_28980_test: SkipByDesign # Debugger is disabled in AOT mode.
 regress_34841_test: SkipByDesign # Debugger is disabled in AOT mode.
+regress_46419_test: SkipByDesign # Debugger is disabled in AOT mode.
 reload_sources_test: SkipByDesign # Hot reload is disabled in AOT mode.
 rewind_optimized_out_test: SkipByDesign # Debugger is disabled in AOT mode.
 rewind_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/observatory_2/tests/service_2/regress_46419_test.dart b/runtime/observatory_2/tests/service_2/regress_46419_test.dart
new file mode 100644
index 0000000..9a80e0b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/regress_46419_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2021, 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.
+// VMOptions=--verbose-debug
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+// DO NOT REORDER BEYOND THIS POINT
+bool testing = false;
+void printSync() {
+  print('sync');
+  if (testing) {
+    // We'll never reach this code, but setting a breakpoint here will result in
+    // the breakpoint being resolved below at line 25.
+    print('unreachable'); // line 18, bp1
+  }
+}
+
+printSyncStar() sync* {
+  // We'll end up resolving breakpoint 1 to this location instead of at line 15
+  // if #46419 regresses.
+  print('sync*');
+}
+
+testeeDo() {
+  printSync();
+  final iterator = printSyncStar();
+
+  print('middle'); // Line 32, bp2
+
+  iterator.toList();
+}
+// END DO NOT REORDER SECTION
+
+Breakpoint bp1;
+Breakpoint bp2;
+
+final tests = <IsolateTest>[
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    await isolate.rootLibrary.load();
+    final script = isolate.rootLibrary.scripts[0];
+
+    bp1 = await isolate.addBreakpoint(script, 18);
+    print("BP1 - $bp1");
+    expect(bp1, isNotNull);
+    expect(bp1 is Breakpoint, isTrue);
+    bp2 = await isolate.addBreakpoint(script, 32);
+    print("BP2 - $bp2");
+    expect(bp2, isNotNull);
+  },
+  resumeIsolate,
+  (Isolate isolate) async {
+    final stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    await for (ServiceEvent event in stream) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        var bp = event.breakpoint;
+        print('Hit $bp');
+        expect(bp, bp2);
+        await isolate.resume();
+        break;
+      }
+    }
+  }
+];
+
+void main([args = const []]) => runIsolateTests(
+      args,
+      tests,
+      testeeConcurrent: testeeDo,
+      pause_on_start: true,
+    );
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
index 1fb2444..f926c9a 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -163,6 +163,7 @@
 regress_28443_test: SkipByDesign # Debugger is disabled in AOT mode.
 regress_28980_test: SkipByDesign # Debugger is disabled in AOT mode.
 regress_34841_test: SkipByDesign # Debugger is disabled in AOT mode.
+regress_46419_test: SkipByDesign # Debugger is disabled in AOT mode.
 reload_sources_test: SkipByDesign # Hot reload is disabled in AOT mode.
 rewind_optimized_out_test: SkipByDesign # Debugger is disabled in AOT mode.
 rewind_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 8e2fb4b..c858051 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1381,6 +1381,7 @@
 
 void ClassFinalizer::SortClasses() {
   auto T = Thread::Current();
+  StackZone stack_zone(T);
   auto Z = T->zone();
   auto IG = T->isolate_group();
 
diff --git a/runtime/vm/compiler/aot/dispatch_table_generator.cc b/runtime/vm/compiler/aot/dispatch_table_generator.cc
index 2198ae7..42a8d8a 100644
--- a/runtime/vm/compiler/aot/dispatch_table_generator.cc
+++ b/runtime/vm/compiler/aot/dispatch_table_generator.cc
@@ -98,7 +98,7 @@
       : selector_(selector),
         class_ranges_(zone, 0),
         ranges_(zone, 0),
-        code_(Code::Handle(zone)) {}
+        code_(Code::ZoneHandle(zone)) {}
 
   TableSelector* selector() const { return selector_; }
 
@@ -408,6 +408,7 @@
 void DispatchTableGenerator::Initialize(ClassTable* table) {
   classes_ = table;
 
+  HANDLESCOPE(Thread::Current());
   ReadTableSelectorInfo();
   NumberSelectors();
   SetupSelectorRows();
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 35ca895..5c02e8a 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -718,6 +718,7 @@
 }
 
 void Precompiler::AddRoots() {
+  HANDLESCOPE(T);
   // Note that <rootlibrary>.main is not a root. The appropriate main will be
   // discovered through _getMainClosure.
 
@@ -776,6 +777,7 @@
 }
 
 void Precompiler::CollectCallbackFields() {
+  HANDLESCOPE(T);
   Library& lib = Library::Handle(Z);
   Class& cls = Class::Handle(Z);
   Class& subcls = Class::Handle(Z);
@@ -790,6 +792,7 @@
 
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
     while (it.HasNext()) {
       cls = it.GetNextClass();
@@ -843,6 +846,7 @@
 }
 
 void Precompiler::ProcessFunction(const Function& function) {
+  HANDLESCOPE(T);
   const intptr_t gop_offset =
       FLAG_use_bare_instructions ? global_object_pool_builder()->CurrentLength()
                                  : 0;
@@ -1462,6 +1466,7 @@
 
 // Adds all values annotated with @pragma('vm:entry-point') as roots.
 void Precompiler::AddAnnotatedRoots() {
+  HANDLESCOPE(T);
   auto& lib = Library::Handle(Z);
   auto& cls = Class::Handle(Z);
   auto& members = Array::Handle(Z);
@@ -1480,6 +1485,7 @@
 
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
     while (it.HasNext()) {
       cls = it.GetNextClass();
@@ -1592,6 +1598,7 @@
 }
 
 void Precompiler::CheckForNewDynamicFunctions() {
+  HANDLESCOPE(T);
   Library& lib = Library::Handle(Z);
   Class& cls = Class::Handle(Z);
   Array& functions = Array::Handle(Z);
@@ -1604,6 +1611,7 @@
 
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
     while (it.HasNext()) {
       cls = it.GetNextClass();
@@ -1769,6 +1777,7 @@
   if (!FLAG_collect_dynamic_function_names) {
     return;
   }
+  HANDLESCOPE(T);
   auto& lib = Library::Handle(Z);
   auto& cls = Class::Handle(Z);
   auto& functions = Array::Handle(Z);
@@ -1781,6 +1790,7 @@
   Table table(HashTables::New<Table>(100));
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
     while (it.HasNext()) {
       cls = it.GetNextClass();
@@ -1873,6 +1883,7 @@
 }
 
 void Precompiler::TraceForRetainedFunctions() {
+  HANDLESCOPE(T);
   Library& lib = Library::Handle(Z);
   Class& cls = Class::Handle(Z);
   Array& functions = Array::Handle(Z);
@@ -1884,6 +1895,7 @@
 
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
     while (it.HasNext()) {
       cls = it.GetNextClass();
@@ -1969,6 +1981,7 @@
 void Precompiler::FinalizeDispatchTable() {
   PRECOMPILER_TIMER_SCOPE(this, FinalizeDispatchTable);
   if (!FLAG_use_bare_instructions || !FLAG_use_table_dispatch) return;
+  HANDLESCOPE(T);
   // Build the entries used to serialize the dispatch table before
   // dropping functions, as we may clear references to Code objects.
   const auto& entries =
@@ -2083,6 +2096,7 @@
 }
 
 void Precompiler::DropFunctions() {
+  HANDLESCOPE(T);
   Library& lib = Library::Handle(Z);
   Class& cls = Class::Handle(Z);
   Array& functions = Array::Handle(Z);
@@ -2163,6 +2177,7 @@
   auto& desc = Array::Handle(Z);
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
     while (it.HasNext()) {
       cls = it.GetNextClass();
@@ -2232,6 +2247,7 @@
 }
 
 void Precompiler::DropFields() {
+  HANDLESCOPE(T);
   Library& lib = Library::Handle(Z);
   Class& cls = Class::Handle(Z);
   Array& fields = Array::Handle(Z);
@@ -2242,6 +2258,7 @@
   SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock());
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
     while (it.HasNext()) {
       cls = it.GetNextClass();
@@ -2294,6 +2311,7 @@
 
 void Precompiler::AttachOptimizedTypeTestingStub() {
   PRECOMPILER_TIMER_SCOPE(this, AttachOptimizedTypeTestingStub);
+  HANDLESCOPE(T);
   IsolateGroup::Current()->heap()->CollectAllGarbage();
   GrowableHandlePtrArray<const AbstractType> types(Z, 200);
   {
@@ -2359,6 +2377,7 @@
 }
 
 void Precompiler::TraceTypesFromRetainedClasses() {
+  HANDLESCOPE(T);
   auto& lib = Library::Handle(Z);
   auto& cls = Class::Handle(Z);
   auto& members = Array::Handle(Z);
@@ -2370,6 +2389,7 @@
   SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock());
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
     while (it.HasNext()) {
       cls = it.GetNextClass();
@@ -2445,6 +2465,7 @@
 }
 
 void Precompiler::DropMetadata() {
+  HANDLESCOPE(T);
   SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock());
 
   Library& lib = Library::Handle(Z);
@@ -2455,6 +2476,7 @@
 }
 
 void Precompiler::DropLibraryEntries() {
+  HANDLESCOPE(T);
   Library& lib = Library::Handle(Z);
   Array& dict = Array::Handle(Z);
   Object& entry = Object::Handle(Z);
@@ -2501,6 +2523,7 @@
 }
 
 void Precompiler::DropClasses() {
+  HANDLESCOPE(T);
   Class& cls = Class::Handle(Z);
   Array& constants = Array::Handle(Z);
   GrowableObjectArray& implementors = GrowableObjectArray::Handle(Z);
@@ -2587,6 +2610,7 @@
 }
 
 void Precompiler::DropLibraries() {
+  HANDLESCOPE(T);
   const GrowableObjectArray& retained_libraries =
       GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
   const Library& root_lib =
@@ -2596,6 +2620,7 @@
 
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
+    HANDLESCOPE(T);
     intptr_t entries = 0;
     DictionaryIterator it(lib);
     while (it.HasNext()) {
@@ -2824,6 +2849,7 @@
     return;
   }
 
+  HANDLESCOPE(T);
   DiscardCodeVisitor visitor(Z, functions_to_retain_,
                              functions_called_dynamically_);
   ProgramVisitor::WalkProgram(Z, IG, &visitor);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 5ce5395..58a9649 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -3102,16 +3102,6 @@
 
 void FlowGraphCompiler::FrameStatePush(Definition* defn) {
   Representation rep = defn->representation();
-  if ((rep == kUnboxedDouble) || (rep == kUnboxedFloat64x2) ||
-      (rep == kUnboxedFloat32x4)) {
-    // The LoadField instruction may lie about its representation in unoptimized
-    // code for Dart fields because Definition::representation() can't depend on
-    // the type of compilation but MakeLocationSummary and EmitNativeCode can.
-    ASSERT(defn->IsLoadField() &&
-           defn->AsLoadField()->IsUnboxedDartFieldLoad());
-    ASSERT(defn->locs()->out(0).IsRegister());
-    rep = kTagged;
-  }
   ASSERT(!is_optimizing());
   ASSERT((rep == kTagged) || (rep == kUntagged) ||
          RepresentationUtils::IsUnboxedInteger(rep));
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 1a6db80..41ef3cb 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -903,7 +903,7 @@
 }
 
 Representation LoadFieldInstr::representation() const {
-  if (IsUnboxedDartFieldLoad()) {
+  if (IsUnboxedDartFieldLoad() && CompilerState::Current().is_optimizing()) {
     return FlowGraph::UnboxedFieldRepresentationOf(slot().field());
   }
   return slot().representation();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 99362e7..2054bbb 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -5351,7 +5351,11 @@
           function.set_is_inlinable(!FLAG_lazy_async_stacks);
         }
 
-        function.set_end_token_pos(function_node_helper.end_position_);
+        // If the start token position is synthetic, the end token position
+        // should be as well.
+        function.set_end_token_pos(
+            position.IsReal() ? function_node_helper.end_position_ : position);
+
         LocalScope* scope = scopes()->function_scopes[i].scope;
         const ContextScope& context_scope = ContextScope::Handle(
             Z, scope->PreserveOuterScope(flow_graph_builder_->context_depth_));
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 71d09e7..c02e4fb 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -132,7 +132,8 @@
   ASSERT(script_url.Equals(func_url));
 #endif  // defined(DEBUG)
   ASSERT(!IsLatent());
-  ASSERT(token_pos.IsWithin(func.token_pos(), func.end_token_pos()));
+  ASSERT(func.is_generated_body() ||
+         token_pos.IsWithin(func.token_pos(), func.end_token_pos()));
   ASSERT(func.is_debuggable());
   token_pos_.store(token_pos);
   end_token_pos_.store(token_pos);
@@ -3249,12 +3250,7 @@
   }
 
   // There is no local function within function that contains the
-  // breakpoint token position. Resolve the breakpoint if necessary
-  // and set the code breakpoints.
-  if (!location->EnsureIsResolved(function, token_pos)) {
-    // Failed to resolve breakpoint location for some reason
-    return false;
-  }
+  // breakpoint token position.
   return true;
 }
 
@@ -3262,6 +3258,27 @@
   if (!function.is_debuggable()) {
     return;
   }
+  Function& resolved_function = Function::Handle(function.ptr());
+  // Synchronous generators have the form:
+  //
+  // user_func sync* {
+  //   :sync_op_gen() {
+  //     :sync_op(..) yielding {
+  //       // ...
+  //     }
+  //   }
+  // }
+  //
+  // Setting a breakpoint in a sync* function should result in a code
+  // breakpoint being inserted into the synthetic :sync_op(..) function node.
+  // In order to ensure we're setting the breakpoint in the right function,
+  // we need to be able to check against the token positions of the
+  // non-synthetic function. Note, we only check for the innermost synthetic
+  // method as this is where the user's code will execute.
+  if (function.IsSyncGenClosure()) {
+    resolved_function ^= function.parent_function();
+    resolved_function ^= resolved_function.parent_function();
+  }
   auto thread = Thread::Current();
   auto zone = thread->zone();
 
@@ -3275,7 +3292,9 @@
   RELEASE_ASSERT(thread->IsInStoppedMutatorsScope());
   for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
     BreakpointLocation* location = breakpoint_locations_.At(i);
-    if (EnsureLocationIsInFunction(zone, function, location)) {
+    if (EnsureLocationIsInFunction(zone, resolved_function, location)) {
+      // Ensure the location is resolved for the original function.
+      location->EnsureIsResolved(function, location->token_pos());
       if (FLAG_verbose_debug) {
         Breakpoint* bpt = location->breakpoints();
         while (bpt != NULL) {
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index 34022eb..0a07b1e 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -346,7 +346,7 @@
   DirectChainedHashMap<S> canonical_objects_;
 };
 
-void ProgramVisitor::BindStaticCalls(Zone* zone, IsolateGroup* isolate_group) {
+void ProgramVisitor::BindStaticCalls(Thread* thread) {
   class BindStaticCallsVisitor : public CodeVisitor {
    public:
     explicit BindStaticCallsVisitor(Zone* zone)
@@ -422,17 +422,19 @@
     Code& target_code_;
   };
 
-  BindStaticCallsVisitor visitor(zone);
-  WalkProgram(zone, isolate_group, &visitor);
+  StackZone stack_zone(thread);
+  BindStaticCallsVisitor visitor(thread->zone());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 
 DECLARE_FLAG(charp, trace_precompiler_to);
 DECLARE_FLAG(charp, write_v8_snapshot_profile_to);
 
-void ProgramVisitor::ShareMegamorphicBuckets(Zone* zone,
-                                             IsolateGroup* isolate_group) {
+void ProgramVisitor::ShareMegamorphicBuckets(Thread* thread) {
+  StackZone stack_zone(thread);
+  Zone* zone = thread->zone();
   const GrowableObjectArray& table = GrowableObjectArray::Handle(
-      zone, isolate_group->object_store()->megamorphic_cache_table());
+      zone, thread->isolate_group()->object_store()->megamorphic_cache_table());
   if (table.IsNull()) return;
   MegamorphicCache& cache = MegamorphicCache::Handle(zone);
 
@@ -562,9 +564,7 @@
 
 typedef DirectChainedHashMap<StackMapEntryKeyIntValueTrait> StackMapEntryIntMap;
 
-void ProgramVisitor::NormalizeAndDedupCompressedStackMaps(
-    Zone* zone,
-    IsolateGroup* isolate_group) {
+void ProgramVisitor::NormalizeAndDedupCompressedStackMaps(Thread* thread) {
   // Walks all the CSMs in Code objects and collects their entry information
   // for consolidation.
   class CollectStackMapEntriesVisitor : public CodeVisitor {
@@ -724,9 +724,10 @@
     CompressedStackMaps& maps_;
   };
 
-  NormalizeAndDedupCompressedStackMapsVisitor dedup_visitor(zone,
-                                                            isolate_group);
-  WalkProgram(zone, isolate_group, &dedup_visitor);
+  StackZone stack_zone(thread);
+  NormalizeAndDedupCompressedStackMapsVisitor visitor(thread->zone(),
+                                                      thread->isolate_group());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 
 class PcDescriptorsKeyValueTrait {
@@ -747,8 +748,7 @@
   }
 };
 
-void ProgramVisitor::DedupPcDescriptors(Zone* zone,
-                                        IsolateGroup* isolate_group) {
+void ProgramVisitor::DedupPcDescriptors(Thread* thread) {
   class DedupPcDescriptorsVisitor
       : public CodeVisitor,
         public Dedupper<PcDescriptors, PcDescriptorsKeyValueTrait> {
@@ -771,8 +771,9 @@
     PcDescriptors& pc_descriptor_;
   };
 
-  DedupPcDescriptorsVisitor visitor(zone);
-  WalkProgram(zone, isolate_group, &visitor);
+  StackZone stack_zone(thread);
+  DedupPcDescriptorsVisitor visitor(thread->zone());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 
 class TypedDataKeyValueTrait {
@@ -801,8 +802,7 @@
   bool IsCorrectType(const Object& obj) const { return obj.IsTypedData(); }
 };
 
-void ProgramVisitor::DedupDeoptEntries(Zone* zone,
-                                       IsolateGroup* isolate_group) {
+void ProgramVisitor::DedupDeoptEntries(Thread* thread) {
   class DedupDeoptEntriesVisitor : public CodeVisitor,
                                    public TypedDataDedupper {
    public:
@@ -836,13 +836,14 @@
   };
 
   if (FLAG_precompiled_mode) return;
-  DedupDeoptEntriesVisitor visitor(zone);
-  WalkProgram(zone, isolate_group, &visitor);
+
+  StackZone stack_zone(thread);
+  DedupDeoptEntriesVisitor visitor(thread->zone());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 
 #if defined(DART_PRECOMPILER)
-void ProgramVisitor::DedupCatchEntryMovesMaps(Zone* zone,
-                                              IsolateGroup* isolate_group) {
+void ProgramVisitor::DedupCatchEntryMovesMaps(Thread* thread) {
   class DedupCatchEntryMovesMapsVisitor : public CodeVisitor,
                                           public TypedDataDedupper {
    public:
@@ -861,8 +862,10 @@
   };
 
   if (!FLAG_precompiled_mode) return;
-  DedupCatchEntryMovesMapsVisitor visitor(zone);
-  WalkProgram(zone, isolate_group, &visitor);
+
+  StackZone stack_zone(thread);
+  DedupCatchEntryMovesMapsVisitor visitor(thread->zone());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 
 class UnlinkedCallKeyValueTrait {
@@ -883,8 +886,7 @@
   }
 };
 
-void ProgramVisitor::DedupUnlinkedCalls(Zone* zone,
-                                        IsolateGroup* isolate_group) {
+void ProgramVisitor::DedupUnlinkedCalls(Thread* thread) {
   class DedupUnlinkedCallsVisitor
       : public CodeVisitor,
         public Dedupper<UnlinkedCall, UnlinkedCallKeyValueTrait> {
@@ -924,7 +926,8 @@
 
   if (!FLAG_precompiled_mode) return;
 
-  DedupUnlinkedCallsVisitor deduper(zone, isolate_group);
+  StackZone stack_zone(thread);
+  DedupUnlinkedCallsVisitor visitor(thread->zone(), thread->isolate_group());
 
   // Note: in bare instructions mode we can still have object pools attached
   // to code objects and these pools need to be deduplicated.
@@ -935,11 +938,11 @@
   if (!FLAG_use_bare_instructions ||
       FLAG_write_v8_snapshot_profile_to != nullptr ||
       FLAG_trace_precompiler_to != nullptr) {
-    WalkProgram(zone, isolate_group, &deduper);
+    WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
   }
 }
 
-void ProgramVisitor::PruneSubclasses(Zone* zone, IsolateGroup* isolate_group) {
+void ProgramVisitor::PruneSubclasses(Thread* thread) {
   class PruneSubclassesVisitor : public ClassVisitor {
    public:
     explicit PruneSubclassesVisitor(Zone* zone)
@@ -996,9 +999,10 @@
     GrowableObjectArray& null_list_;
   };
 
-  PruneSubclassesVisitor visitor(zone);
-  SafepointWriteRwLocker ml(Thread::Current(), isolate_group->program_lock());
-  WalkProgram(zone, isolate_group, &visitor);
+  StackZone stack_zone(thread);
+  PruneSubclassesVisitor visitor(thread->zone());
+  SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 #endif  // defined(DART_PRECOMPILER)
 
@@ -1024,8 +1028,7 @@
   }
 };
 
-void ProgramVisitor::DedupCodeSourceMaps(Zone* zone,
-                                         IsolateGroup* isolate_group) {
+void ProgramVisitor::DedupCodeSourceMaps(Thread* thread) {
   class DedupCodeSourceMapsVisitor
       : public CodeVisitor,
         public Dedupper<CodeSourceMap, CodeSourceMapKeyValueTrait> {
@@ -1048,8 +1051,9 @@
     CodeSourceMap& code_source_map_;
   };
 
-  DedupCodeSourceMapsVisitor visitor(zone);
-  WalkProgram(zone, isolate_group, &visitor);
+  StackZone stack_zone(thread);
+  DedupCodeSourceMapsVisitor visitor(thread->zone());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 
 class ArrayKeyValueTrait {
@@ -1078,7 +1082,7 @@
   }
 };
 
-void ProgramVisitor::DedupLists(Zone* zone, IsolateGroup* isolate_group) {
+void ProgramVisitor::DedupLists(Thread* thread) {
   class DedupListsVisitor : public CodeVisitor,
                             public Dedupper<Array, ArrayKeyValueTrait> {
    public:
@@ -1122,8 +1126,9 @@
     Field& field_;
   };
 
-  DedupListsVisitor visitor(zone);
-  WalkProgram(zone, isolate_group, &visitor);
+  StackZone stack_zone(thread);
+  DedupListsVisitor visitor(thread->zone());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 
 // Traits for comparing two [Instructions] objects for equality, which is
@@ -1208,8 +1213,7 @@
 };
 #endif
 
-void ProgramVisitor::DedupInstructions(Zone* zone,
-                                       IsolateGroup* isolate_group) {
+void ProgramVisitor::DedupInstructions(Thread* thread) {
   class DedupInstructionsVisitor
       : public CodeVisitor,
         public Dedupper<Instructions, InstructionsKeyValueTrait>,
@@ -1297,33 +1301,31 @@
   };
 
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    DedupInstructionsWithSameMetadataVisitor visitor(zone);
-    return WalkProgram(zone, isolate_group, &visitor);
+    StackZone stack_zone(thread);
+    DedupInstructionsWithSameMetadataVisitor visitor(thread->zone());
+    WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
+    return;
   }
 #endif  // defined(DART_PRECOMPILER)
 
-  DedupInstructionsVisitor visitor(zone);
-  WalkProgram(zone, isolate_group, &visitor);
+  StackZone stack_zone(thread);
+  DedupInstructionsVisitor visitor(thread->zone());
+  WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
 
 void ProgramVisitor::Dedup(Thread* thread) {
-  auto const isolate_group = thread->isolate_group();
-  StackZone stack_zone(thread);
-  HANDLESCOPE(thread);
-  auto const zone = thread->zone();
-
-  BindStaticCalls(zone, isolate_group);
-  ShareMegamorphicBuckets(zone, isolate_group);
-  NormalizeAndDedupCompressedStackMaps(zone, isolate_group);
-  DedupPcDescriptors(zone, isolate_group);
-  DedupDeoptEntries(zone, isolate_group);
+  BindStaticCalls(thread);
+  ShareMegamorphicBuckets(thread);
+  NormalizeAndDedupCompressedStackMaps(thread);
+  DedupPcDescriptors(thread);
+  DedupDeoptEntries(thread);
 #if defined(DART_PRECOMPILER)
-  DedupCatchEntryMovesMaps(zone, isolate_group);
-  DedupUnlinkedCalls(zone, isolate_group);
-  PruneSubclasses(zone, isolate_group);
+  DedupCatchEntryMovesMaps(thread);
+  DedupUnlinkedCalls(thread);
+  PruneSubclasses(thread);
 #endif
-  DedupCodeSourceMaps(zone, isolate_group);
-  DedupLists(zone, isolate_group);
+  DedupCodeSourceMaps(thread);
+  DedupLists(thread);
 
   // Reduces binary size but obfuscates profiler results.
   if (FLAG_dedup_instructions) {
@@ -1346,7 +1348,7 @@
     if (FLAG_precompiled_mode && !FLAG_use_bare_instructions) return;
 #endif
 
-    DedupInstructions(zone, isolate_group);
+    DedupInstructions(thread);
   }
 }
 
@@ -1419,8 +1421,7 @@
 
 void ProgramVisitor::AssignUnits(Thread* thread) {
   StackZone stack_zone(thread);
-  HANDLESCOPE(thread);
-  Zone* zone = thread->zone();
+  Zone* zone = stack_zone.GetZone();
 
   // VM stubs.
   Instructions& inst = Instructions::Handle(zone);
diff --git a/runtime/vm/program_visitor.h b/runtime/vm/program_visitor.h
index fa57326..b743e0a 100644
--- a/runtime/vm/program_visitor.h
+++ b/runtime/vm/program_visitor.h
@@ -106,20 +106,19 @@
 #endif
 
  private:
-  static void BindStaticCalls(Zone* zone, IsolateGroup* isolate_group);
-  static void ShareMegamorphicBuckets(Zone* zone, IsolateGroup* isolate_group);
-  static void NormalizeAndDedupCompressedStackMaps(Zone* zone,
-                                                   IsolateGroup* isolate_group);
-  static void DedupPcDescriptors(Zone* zone, IsolateGroup* isolate_group);
-  static void DedupDeoptEntries(Zone* zone, IsolateGroup* isolate_group);
+  static void BindStaticCalls(Thread* thread);
+  static void ShareMegamorphicBuckets(Thread* thread);
+  static void NormalizeAndDedupCompressedStackMaps(Thread* thread);
+  static void DedupPcDescriptors(Thread* thread);
+  static void DedupDeoptEntries(Thread* thread);
 #if defined(DART_PRECOMPILER)
-  static void DedupCatchEntryMovesMaps(Zone* zone, IsolateGroup* isolate_group);
-  static void DedupUnlinkedCalls(Zone* zone, IsolateGroup* isolate_group);
-  static void PruneSubclasses(Zone* zone, IsolateGroup* isolate_group);
+  static void DedupCatchEntryMovesMaps(Thread* thread);
+  static void DedupUnlinkedCalls(Thread* thread);
+  static void PruneSubclasses(Thread* thread);
 #endif
-  static void DedupCodeSourceMaps(Zone* zone, IsolateGroup* isolate_group);
-  static void DedupLists(Zone* zone, IsolateGroup* isolate_group);
-  static void DedupInstructions(Zone* zone, IsolateGroup* isolate_group);
+  static void DedupCodeSourceMaps(Thread* thread);
+  static void DedupLists(Thread* thread);
+  static void DedupInstructions(Thread* thread);
 };
 
 }  // namespace dart
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index b2545cf..7a966ac 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -403,7 +403,8 @@
       desc.info.declaration_pos = context_scope.DeclarationTokenIndexAt(i);
       desc.info.begin_pos = begin_token_pos();
       desc.info.end_pos = end_token_pos();
-      ASSERT(desc.info.begin_pos <= desc.info.end_pos);
+      ASSERT((desc.info.begin_pos.IsReal() != desc.info.end_pos.IsReal()) ||
+             (desc.info.begin_pos <= desc.info.end_pos));
       desc.info.set_index(context_scope.ContextIndexAt(i));
       vars.Add(desc);
     }
diff --git a/tools/VERSION b/tools/VERSION
index 259aa5b..ac3e141 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 50
+PRERELEASE 51
 PRERELEASE_PATCH 0
\ No newline at end of file