Version 2.10.0-44.0.dev

Merge commit '1cec77f0a60b0c8e3df18085893e6ecfdbceb5b4' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 84ad8a2..37a656d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -587,7 +587,7 @@
   /// The [path] must be absolute and normalized.
   FileResult getFileSync(String path) {
     _throwIfNotAbsolutePath(path);
-    FileState file = _fileTracker.verifyApiSignature(path);
+    FileState file = _fileTracker.getFile(path);
     return FileResultImpl(
         _currentSession, path, file.uri, file.lineInfo, file.isPart);
   }
@@ -819,7 +819,7 @@
   Future<SourceKind> getSourceKind(String path) async {
     _throwIfNotAbsolutePath(path);
     if (AnalysisEngine.isDartFileName(path)) {
-      FileState file = _fileTracker.verifyApiSignature(path);
+      FileState file = _fileTracker.getFile(path);
       return file.isPart ? SourceKind.PART : SourceKind.LIBRARY;
     }
     return null;
@@ -907,7 +907,7 @@
   /// resolved unit).
   ParsedUnitResult parseFileSync(String path) {
     _throwIfNotAbsolutePath(path);
-    FileState file = _fileTracker.verifyApiSignature(path);
+    FileState file = _fileTracker.getFile(path);
     RecordingErrorListener listener = RecordingErrorListener();
     CompilationUnit unit = file.parse(listener);
     return ParsedUnitResultImpl(currentSession, file.path, file.uri,
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_tracker.dart b/pkg/analyzer/lib/src/dart/analysis/file_tracker.dart
index abd28a5..29fe48e 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_tracker.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_tracker.dart
@@ -139,6 +139,18 @@
     _pendingFiles.remove(path);
   }
 
+  /// Return the [FileState] suitable only when the file is used by itself.
+  /// If the file is in the set of changed files, it is refreshed first.
+  /// If the file is not in the set of changed files, it is not refreshed.
+  FileState getFile(String path) {
+    // Read any files that we were told were changed.
+    // But don't read the requested file explicitly.
+    // Read it only if it is in this set of changed files.
+    while (verifyChangedFilesIfNeeded()) {}
+
+    return _fsState.getFileForPath(path);
+  }
+
   /// Returns a boolean indicating whether the given [path] points to a file
   /// that requires analysis.
   bool isFilePending(String path) {
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 2f46907..2522b77 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -1275,6 +1275,43 @@
     expect(files, isNot(contains(c)));
   }
 
+  test_getFileSync_changedFile() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
+
+    newFile(a, content: '');
+    newFile(b, content: r'''
+import 'a.dart';
+
+void f(A a) {}
+''');
+
+    // Ensure that [a.dart] library cycle is loaded.
+    // So, `a.dart` is in the library context.
+    await driver.getResult(a);
+
+    // Update the file, changing its API signature.
+    // Note that we don't call `changeFile`.
+    newFile(a, content: 'class A {}\n');
+
+    // Get the file.
+    // We have not called `changeFile(a)`, so we should not read the file.
+    // Moreover, doing this will create a new library cycle [a.dart].
+    // Library cycles are compared by their identity, so we would try to
+    // reload linked summary for [a.dart], and crash.
+    expect(driver.getFileSync(a).lineInfo.lineCount, 1);
+
+    // We have not read `a.dart`, so `A` is still not declared.
+    expect((await driver.getResult(b)).errors, isNotEmpty);
+
+    // Notify the driver that the file was changed.
+    driver.changeFile(a);
+
+    // So, `class A {}` is declared now.
+    expect(driver.getFileSync(a).lineInfo.lineCount, 2);
+    expect((await driver.getResult(b)).errors, isEmpty);
+  }
+
   test_getFileSync_library() async {
     var path = convertPath('/test/lib/a.dart');
     newFile(path);
@@ -1825,6 +1862,43 @@
     expect(result1.unit, isNotNull);
   }
 
+  test_getSourceKind_changedFile() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
+
+    newFile(a, content: 'part of lib;');
+    newFile(b, content: r'''
+import 'a.dart';
+
+void f(A a) {}
+''');
+
+    // Ensure that [a.dart] library cycle is loaded.
+    // So, `a.dart` is in the library context.
+    await driver.getResult(a);
+
+    // Update the file, changing its API signature.
+    // Note that we don't call `changeFile`.
+    newFile(a, content: 'class A {}');
+
+    // Get the kind of the file.
+    // We have not called `changeFile(a)`, so we should not read the file.
+    // Moreover, doing this will create a new library cycle [a.dart].
+    // Library cycles are compared by their identity, so we would try to
+    // reload linked summary for [a.dart], and crash.
+    expect(await driver.getSourceKind(a), SourceKind.PART);
+
+    // We have not read `a.dart`, so `A` is still not declared.
+    expect((await driver.getResult(b)).errors, isNotEmpty);
+
+    // Notify the driver that the file was changed.
+    driver.changeFile(a);
+
+    // So, `class A {}` is declared now.
+    expect(await driver.getSourceKind(a), SourceKind.LIBRARY);
+    expect((await driver.getResult(b)).errors, isEmpty);
+  }
+
   test_getSourceKind_changeFile() async {
     var path = convertPath('/test/lib/test.dart');
     expect(await driver.getSourceKind(path), SourceKind.LIBRARY);
@@ -2164,6 +2238,55 @@
     expect(error.errorCode, CompileTimeErrorCode.MISSING_DART_LIBRARY);
   }
 
+  test_parseFile_changedFile() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
+
+    newFile(a, content: '');
+    newFile(b, content: r'''
+import 'a.dart';
+
+void f(A a) {}
+''');
+
+    // Ensure that [a.dart] library cycle is loaded.
+    // So, `a.dart` is in the library context.
+    await driver.getResult(a);
+
+    // Update the file, changing its API signature.
+    // Note that we don't call `changeFile`.
+    newFile(a, content: 'class A {}');
+
+    // Parse the file.
+    // We have not called `changeFile(a)`, so we should not read the file.
+    // Moreover, doing this will create a new library cycle [a.dart].
+    // Library cycles are compared by their identity, so we would try to
+    // reload linked summary for [a.dart], and crash.
+    {
+      var parseResult = await driver.parseFile(a);
+      expect(parseResult.unit.declarations, isEmpty);
+    }
+
+    // We have not read `a.dart`, so `A` is still not declared.
+    {
+      var bResult = await driver.getResult(b);
+      expect(bResult.errors, isNotEmpty);
+    }
+
+    // Notify the driver that the file was changed.
+    driver.changeFile(a);
+
+    // So, `class A {}` is declared now.
+    {
+      var parseResult = driver.parseFileSync(a);
+      expect(parseResult.unit.declarations, hasLength(1));
+    }
+    {
+      var bResult = await driver.getResult(b);
+      expect(bResult.errors, isEmpty);
+    }
+  }
+
   test_parseFile_notAbsolutePath() async {
     expect(() async {
       await driver.parseFile('not_absolute.dart');
@@ -2179,21 +2302,53 @@
     expect(driver.knownFiles, contains(p));
   }
 
-  test_parseFile_shouldRefresh() async {
-    var p = convertPath('/test/bin/a.dart');
+  test_parseFileSync_changedFile() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
 
-    newFile(p, content: 'class A {}');
-    driver.addFile(p);
+    newFile(a, content: '');
+    newFile(b, content: r'''
+import 'a.dart';
 
-    // Get the result, so force the file reading.
-    await driver.getResult(p);
+void f(A a) {}
+''');
 
-    // Update the file.
-    newFile(p, content: 'class A2 {}');
+    // Ensure that [a.dart] library cycle is loaded.
+    // So, `a.dart` is in the library context.
+    await driver.getResult(a);
 
-    ParsedUnitResult parseResult = await driver.parseFile(p);
-    var clazz = parseResult.unit.declarations[0] as ClassDeclaration;
-    expect(clazz.name.name, 'A2');
+    // Update the file, changing its API signature.
+    // Note that we don't call `changeFile`.
+    newFile(a, content: 'class A {}');
+
+    // Parse the file.
+    // We have not called `changeFile(a)`, so we should not read the file.
+    // Moreover, doing this will create a new library cycle [a.dart].
+    // Library cycles are compared by their identity, so we would try to
+    // reload linked summary for [a.dart], and crash.
+    {
+      var parseResult = driver.parseFileSync(a);
+      expect(parseResult.unit.declarations, isEmpty);
+    }
+
+    // We have not read `a.dart`, so `A` is still not declared.
+    {
+      var bResult = await driver.getResult(b);
+      expect(bResult.errors, isNotEmpty);
+    }
+
+    // Notify the driver that the file was changed.
+    driver.changeFile(a);
+
+    // So, `class A {}` is declared now.
+    {
+      var parseResult = driver.parseFileSync(a);
+      expect(parseResult.unit.declarations, hasLength(1));
+    }
+    {
+      var bResult = await driver.getResult(b);
+      expect(bResult.errors, isEmpty);
+    }
   }
 
   test_parseFileSync_languageVersion() async {
@@ -2236,23 +2391,6 @@
     expect(driver.knownFiles, contains(p));
   }
 
-  test_parseFileSync_shouldRefresh() async {
-    var p = convertPath('/test/bin/a.dart');
-
-    newFile(p, content: 'class A {}');
-    driver.addFile(p);
-
-    // Get the result, so force the file reading.
-    await driver.getResult(p);
-
-    // Update the file.
-    newFile(p, content: 'class A2 {}');
-
-    ParsedUnitResult parseResult = driver.parseFileSync(p);
-    var clazz = parseResult.unit.declarations[0] as ClassDeclaration;
-    expect(clazz.name.name, 'A2');
-  }
-
   test_part_getErrors_afterLibrary() async {
     var a = convertPath('/test/lib/a.dart');
     var b = convertPath('/test/lib/b.dart');
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
index 086d55aa..7afc77e 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
@@ -1201,18 +1201,23 @@
       type = type.withoutNullability;
       if (type is InterfaceType) {
         return [type.element];
-      } else if (type is DynamicType) {
-        return [commonElements.objectClass];
+      } else if (type is NeverType ||
+          type is DynamicType ||
+          type is VoidType ||
+          type is AnyType ||
+          type is ErasedType) {
+        // No classes implied.
+        return const [];
       } else if (type is FunctionType) {
         // TODO(johnniwinther): Include only potential function type subtypes.
         return [commonElements.functionClass];
-      } else if (type is VoidType) {
-        // No classes implied.
       } else if (type is FunctionTypeVariable) {
         return impliedClasses(type.bound);
       } else if (type is FutureOrType) {
-        return [commonElements.futureClass]
-          ..addAll(impliedClasses(type.typeArgument));
+        return [
+          commonElements.futureClass,
+          ...impliedClasses(type.typeArgument),
+        ];
       } else if (type is TypeVariableType) {
         // TODO(johnniwinther): Can we do better?
         return impliedClasses(
diff --git a/runtime/tests/vm/dart/incompatible_loading_unit_test.dart b/runtime/tests/vm/dart/incompatible_loading_unit_test.dart
index 9adf6e8..73a7a6d 100644
--- a/runtime/tests/vm/dart/incompatible_loading_unit_test.dart
+++ b/runtime/tests/vm/dart/incompatible_loading_unit_test.dart
@@ -36,7 +36,7 @@
       var uris = <String>[];
       for (var uri in unit['libraries']) {
         if (uri.startsWith("dart:")) continue;
-        uris.add(Uri.file(uri).pathSegments.last);
+        uris.add(Uri.parse(uri).pathSegments.last);
       }
       uris.sort((a, b) => a.compareTo(b));
       units.add(uris);
diff --git a/runtime/tests/vm/dart_2/incompatible_loading_unit_test.dart b/runtime/tests/vm/dart_2/incompatible_loading_unit_test.dart
index 9adf6e8..73a7a6d 100644
--- a/runtime/tests/vm/dart_2/incompatible_loading_unit_test.dart
+++ b/runtime/tests/vm/dart_2/incompatible_loading_unit_test.dart
@@ -36,7 +36,7 @@
       var uris = <String>[];
       for (var uri in unit['libraries']) {
         if (uri.startsWith("dart:")) continue;
-        uris.add(Uri.file(uri).pathSegments.last);
+        uris.add(Uri.parse(uri).pathSegments.last);
       }
       uris.sort((a, b) => a.compareTo(b));
       units.add(uris);
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 6e327e2..95ae9bb 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1426,20 +1426,18 @@
       group->RunWithLockedGroup([&]() {
         // Ensure no other old space GC tasks are running and "occupy" the old
         // space.
+        SafepointOperationScope safepoint_scope(thread);
         {
           auto old_space = group->heap()->old_space();
           MonitorLocker ml(old_space->tasks_lock());
           while (old_space->tasks() > 0) {
-            ml.WaitWithSafepointCheck(thread);
+            ml.Wait();
           }
           old_space->set_tasks(1);
         }
 
         // Merge the heap from [spawning_group] to [group].
-        {
-          SafepointOperationScope safepoint_scope(thread);
-          group->heap()->MergeFrom(isolate->group()->heap());
-        }
+        group->heap()->MergeFrom(isolate->group()->heap());
 
         spawning_group->UnregisterIsolate(isolate);
         const bool shutdown_group =
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 7e81415..170f52c 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -65,8 +65,6 @@
       barrier_(),
       barrier_done_(),
       read_only_(false),
-      gc_new_space_in_progress_(false),
-      gc_old_space_in_progress_(false),
       last_gc_was_old_space_(false),
       assume_scavenge_will_fail_(false),
       gc_on_nth_allocation_(kNoForcedGarbageCollection) {
@@ -237,6 +235,8 @@
       heap_(isolate()->heap()),
       old_space_(heap_->old_space()),
       writable_(writable) {
+  isolate()->safepoint_handler()->SafepointThreads(thread);
+
   {
     // It's not safe to iterate over old space when concurrent marking or
     // sweeping is in progress, or another thread is iterating the heap, so wait
@@ -255,7 +255,7 @@
         ml.Enter();
       }
       while (old_space_->tasks() > 0) {
-        ml.WaitWithSafepointCheck(thread);
+        ml.Wait();
       }
     }
 #if defined(DEBUG)
@@ -265,8 +265,6 @@
     old_space_->set_tasks(1);
   }
 
-  isolate()->safepoint_handler()->SafepointThreads(thread);
-
   if (writable_) {
     heap_->WriteProtectCode(false);
   }
@@ -277,16 +275,18 @@
     heap_->WriteProtectCode(true);
   }
 
-  isolate()->safepoint_handler()->ResumeThreads(thread());
-
-  MonitorLocker ml(old_space_->tasks_lock());
+  {
+    MonitorLocker ml(old_space_->tasks_lock());
 #if defined(DEBUG)
-  ASSERT(old_space_->iterating_thread_ == thread());
-  old_space_->iterating_thread_ = NULL;
+    ASSERT(old_space_->iterating_thread_ == thread());
+    old_space_->iterating_thread_ = NULL;
 #endif
-  ASSERT(old_space_->tasks() == 1);
-  old_space_->set_tasks(0);
-  ml.NotifyAll();
+    ASSERT(old_space_->tasks() == 1);
+    old_space_->set_tasks(0);
+    ml.NotifyAll();
+  }
+
+  isolate()->safepoint_handler()->ResumeThreads(thread());
 }
 
 void HeapIterationScope::IterateObjects(ObjectVisitor* visitor) const {
@@ -360,57 +360,14 @@
   return raw_obj;
 }
 
-bool Heap::BeginNewSpaceGC(Thread* thread) {
-  MonitorLocker ml(&gc_in_progress_monitor_);
-  bool start_gc_on_thread = true;
-  while (gc_new_space_in_progress_ || gc_old_space_in_progress_) {
-    start_gc_on_thread = !gc_new_space_in_progress_;
-    ml.WaitWithSafepointCheck(thread);
-  }
-  if (start_gc_on_thread) {
-    gc_new_space_in_progress_ = true;
-    return true;
-  }
-  return false;
-}
-
-void Heap::EndNewSpaceGC() {
-  MonitorLocker ml(&gc_in_progress_monitor_);
-  ASSERT(gc_new_space_in_progress_);
-  gc_new_space_in_progress_ = false;
-  last_gc_was_old_space_ = false;
-  ml.NotifyAll();
-}
-
-bool Heap::BeginOldSpaceGC(Thread* thread) {
-  MonitorLocker ml(&gc_in_progress_monitor_);
-  bool start_gc_on_thread = true;
-  while (gc_new_space_in_progress_ || gc_old_space_in_progress_) {
-    start_gc_on_thread = !gc_old_space_in_progress_;
-    ml.WaitWithSafepointCheck(thread);
-  }
-  if (start_gc_on_thread) {
-    gc_old_space_in_progress_ = true;
-    return true;
-  }
-  return false;
-}
-
-void Heap::EndOldSpaceGC() {
-  MonitorLocker ml(&gc_in_progress_monitor_);
-  ASSERT(gc_old_space_in_progress_);
-  gc_old_space_in_progress_ = false;
-  last_gc_was_old_space_ = true;
-  assume_scavenge_will_fail_ = false;
-  ml.NotifyAll();
-}
-
 void Heap::HintFreed(intptr_t size) {
   old_space_.HintFreed(size);
 }
 
 void Heap::NotifyIdle(int64_t deadline) {
   Thread* thread = Thread::Current();
+  SafepointOperationScope safepoint_operation(thread);
+
   // Check if we want to collect new-space first, because if we want to collect
   // both new-space and old-space, the new-space collection should run first
   // to shrink the root set (make old-space GC faster) and avoid
@@ -475,7 +432,8 @@
     // visiting pointers.
     return;
   }
-  if (BeginNewSpaceGC(thread)) {
+  {
+    SafepointOperationScope safepoint_operation(thread);
     RecordBeforeGC(kScavenge, reason);
     VMTagScope tagScope(thread, reason == kIdle ? VMTag::kGCIdleTagId
                                                 : VMTag::kGCNewSpaceTagId);
@@ -484,7 +442,7 @@
     RecordAfterGC(kScavenge);
     PrintStats();
     NOT_IN_PRODUCT(PrintStatsToTimeline(&tbes, reason));
-    EndNewSpaceGC();
+    last_gc_was_old_space_ = false;
   }
 }
 
@@ -498,7 +456,8 @@
     // visiting pointers.
     return;
   }
-  if (BeginNewSpaceGC(thread)) {
+  {
+    SafepointOperationScope safepoint_operation(thread);
     RecordBeforeGC(kScavenge, reason);
     {
       VMTagScope tagScope(thread, reason == kIdle ? VMTag::kGCIdleTagId
@@ -508,7 +467,7 @@
       RecordAfterGC(kScavenge);
       PrintStats();
       NOT_IN_PRODUCT(PrintStatsToTimeline(&tbes, reason));
-      EndNewSpaceGC();
+      last_gc_was_old_space_ = false;
     }
     if (reason == kNewSpace) {
       if (old_space_.ReachedHardThreshold()) {
@@ -537,11 +496,14 @@
     // visiting pointers.
     return;
   }
-  if (BeginOldSpaceGC(thread)) {
-    thread->isolate_group()->ForEachIsolate([&](Isolate* isolate) {
-      // Discard regexp backtracking stacks to further reduce memory usage.
-      isolate->CacheRegexpBacktrackStack(nullptr);
-    });
+  {
+    SafepointOperationScope safepoint_operation(thread);
+    thread->isolate_group()->ForEachIsolate(
+        [&](Isolate* isolate) {
+          // Discard regexp backtracking stacks to further reduce memory usage.
+          isolate->CacheRegexpBacktrackStack(nullptr);
+        },
+        /*at_safepoint=*/true);
 
     RecordBeforeGC(type, reason);
     VMTagScope tagScope(thread, reason == kIdle ? VMTag::kGCIdleTagId
@@ -553,11 +515,14 @@
     NOT_IN_PRODUCT(PrintStatsToTimeline(&tbes, reason));
 
     // Some Code objects may have been collected so invalidate handler cache.
-    thread->isolate_group()->ForEachIsolate([&](Isolate* isolate) {
-      isolate->handler_info_cache()->Clear();
-      isolate->catch_entry_moves_cache()->Clear();
-    });
-    EndOldSpaceGC();
+    thread->isolate_group()->ForEachIsolate(
+        [&](Isolate* isolate) {
+          isolate->handler_info_cache()->Clear();
+          isolate->catch_entry_moves_cache()->Clear();
+        },
+        /*at_safepoint=*/true);
+    last_gc_was_old_space_ = true;
+    assume_scavenge_will_fail_ = false;
   }
 }
 
@@ -637,11 +602,8 @@
 }
 
 void Heap::StartConcurrentMarking(Thread* thread) {
-  if (BeginOldSpaceGC(thread)) {
-    TIMELINE_FUNCTION_GC_DURATION_BASIC(thread, "StartConcurrentMarking");
-    old_space_.CollectGarbage(/*compact=*/false, /*finalize=*/false);
-    EndOldSpaceGC();
-  }
+  TIMELINE_FUNCTION_GC_DURATION_BASIC(thread, "StartConcurrentMarking");
+  old_space_.CollectGarbage(/*compact=*/false, /*finalize=*/false);
 }
 
 void Heap::CheckFinishConcurrentMarking(Thread* thread) {
@@ -751,8 +713,6 @@
 }
 
 void Heap::MergeFrom(Heap* donor) {
-  ASSERT(!donor->gc_new_space_in_progress_);
-  ASSERT(!donor->gc_old_space_in_progress_);
   ASSERT(!donor->read_only_);
   ASSERT(donor->old_space()->tasks() == 0);
 
@@ -1021,9 +981,6 @@
 #endif  // PRODUCT
 
 void Heap::RecordBeforeGC(GCType type, GCReason reason) {
-  ASSERT((type == kScavenge && gc_new_space_in_progress_) ||
-         (type == kMarkSweep && gc_old_space_in_progress_) ||
-         (type == kMarkCompact && gc_old_space_in_progress_));
   stats_.num_++;
   stats_.type_ = type;
   stats_.reason_ = reason;
@@ -1048,9 +1005,6 @@
   }
   stats_.after_.new_ = new_space_.GetCurrentUsage();
   stats_.after_.old_ = old_space_.GetCurrentUsage();
-  ASSERT((type == kScavenge && gc_new_space_in_progress_) ||
-         (type == kMarkSweep && gc_old_space_in_progress_) ||
-         (type == kMarkCompact && gc_old_space_in_progress_));
 #ifndef PRODUCT
   // For now we'll emit the same GC events on all isolates.
   if (Service::gc_stream.enabled()) {
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index fa9d733..8982ba3 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -383,12 +383,6 @@
   void PrintStats();
   void PrintStatsToTimeline(TimelineEventScope* event, GCReason reason);
 
-  // Updates gc in progress flags.
-  bool BeginNewSpaceGC(Thread* thread);
-  void EndNewSpaceGC();
-  bool BeginOldSpaceGC(Thread* thread);
-  void EndOldSpaceGC();
-
   void AddRegionsToObjectSet(ObjectSet* set) const;
 
   // Trigger major GC if 'gc_on_nth_allocation_' is set.
@@ -412,10 +406,6 @@
   // This heap is in read-only mode: No allocation is allowed.
   bool read_only_;
 
-  // GC on the heap is in progress.
-  Monitor gc_in_progress_monitor_;
-  bool gc_new_space_in_progress_;
-  bool gc_old_space_in_progress_;
   bool last_gc_was_old_space_;
   bool assume_scavenge_will_fail_;
 
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index c0a2597..e0f10cb 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1030,9 +1030,10 @@
   }
 
   Thread* thread = Thread::Current();
+  const int64_t pre_safe_point = OS::GetCurrentMonotonicMicros();
+  SafepointOperationScope safepoint_scope(thread);
 
   const int64_t pre_wait_for_sweepers = OS::GetCurrentMonotonicMicros();
-
   // Wait for pending tasks to complete and then account for the driver task.
   Phase waited_for;
   {
@@ -1045,15 +1046,15 @@
     }
 
     while (tasks() > 0) {
-      locker.WaitWithSafepointCheck(thread);
+      locker.Wait();
     }
     ASSERT(phase() == kAwaitingFinalization || phase() == kDone);
     set_tasks(1);
   }
 
-  const int64_t pre_safe_point = OS::GetCurrentMonotonicMicros();
   if (FLAG_verbose_gc) {
-    const int64_t wait = pre_safe_point - pre_wait_for_sweepers;
+    const int64_t wait =
+        OS::GetCurrentMonotonicMicros() - pre_wait_for_sweepers;
     if (waited_for == kMarking) {
       THR_Print("Waited %" Pd64 " us for concurrent marking to finish.\n",
                 wait);
@@ -1068,9 +1069,8 @@
   // to ensure that if two threads are racing to collect at the same time the
   // loser skips collection and goes straight to allocation.
   {
-    SafepointOperationScope safepoint_scope(thread);
-    CollectGarbageAtSafepoint(compact, finalize, pre_wait_for_sweepers,
-                              pre_safe_point);
+    CollectGarbageHelper(compact, finalize, pre_wait_for_sweepers,
+                         pre_safe_point);
   }
 
   // Done, reset the task count.
@@ -1081,10 +1081,10 @@
   }
 }
 
-void PageSpace::CollectGarbageAtSafepoint(bool compact,
-                                          bool finalize,
-                                          int64_t pre_wait_for_sweepers,
-                                          int64_t pre_safe_point) {
+void PageSpace::CollectGarbageHelper(bool compact,
+                                     bool finalize,
+                                     int64_t pre_wait_for_sweepers,
+                                     int64_t pre_safe_point) {
   Thread* thread = Thread::Current();
   ASSERT(thread->IsAtSafepoint());
   auto isolate_group = heap_->isolate_group();
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index ef5badf..83c5488 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -538,10 +538,10 @@
   void FreeLargePage(OldPage* page, OldPage* previous_page);
   void FreePages(OldPage* pages);
 
-  void CollectGarbageAtSafepoint(bool compact,
-                                 bool finalize,
-                                 int64_t pre_wait_for_sweepers,
-                                 int64_t pre_safe_point);
+  void CollectGarbageHelper(bool compact,
+                            bool finalize,
+                            int64_t pre_wait_for_sweepers,
+                            int64_t pre_safe_point);
   void SweepLarge();
   void Sweep();
   void ConcurrentSweep(IsolateGroup* isolate_group);
diff --git a/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart b/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart
index 8eaad60..4f182d0 100644
--- a/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart
@@ -208,7 +208,7 @@
     return out;
   }
 
-  String namedGroup(String name) {
+  String? namedGroup(String name) {
     var groups = JS('Object|Null', '#.groups', _match);
     if (groups != null) {
       var result = JS('String|Null', '#[#]', groups, name);
diff --git a/tests/dart2js/29130_test.dart b/tests/dart2js/29130_test.dart
index 4426f26..6766e0c 100644
--- a/tests/dart2js/29130_test.dart
+++ b/tests/dart2js/29130_test.dart
@@ -20,7 +20,11 @@
 
 // interface scenario: we shouldn't trace B
 abstract class B implements A {
-  factory B() => null as dynamic;
+  factory B() => DummyB();
+}
+
+class DummyB implements B {
+  call() {}
 }
 
 // mixin scenario: we should trace C, but we should trace _C
diff --git a/tools/VERSION b/tools/VERSION
index 339772b..bec7b97 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 43
+PRERELEASE 44
 PRERELEASE_PATCH 0
\ No newline at end of file