Version 2.12.0-38.0.dev

Merge commit '54a91f26c47d8bf860ebd78bb8d836b1442bdb21' into 'dev'
diff --git a/DEPS b/DEPS
index 73717bf..cd20336 100644
--- a/DEPS
+++ b/DEPS
@@ -146,7 +146,7 @@
   "source_maps-0.9.4_rev": "38524",
   "source_maps_rev": "53eb92ccfe6e64924054f83038a534b959b12b3e",
   "source_span_rev": "49ff31eabebed0da0ae6634124f8ba5c6fbf57f1",
-  "sse_tag": "e5cf68975e8e87171a3dc297577aa073454a91dc",
+  "sse_tag": "814924bac4d3cd56f40d05b3427e69f3e966d139",
   "stack_trace_tag": "6788afc61875079b71b3d1c3e65aeaa6a25cbc2f",
   "stagehand_tag": "v3.3.11",
   "stream_channel_tag": "d7251e61253ec389ee6e045ee1042311bced8f1d",
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index adbfc57..fb9d078 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -15,7 +15,7 @@
     hide AnalysisOptions;
 import 'package:analysis_server/src/analysis_server_abstract.dart';
 import 'package:analysis_server/src/channel/channel.dart';
-import 'package:analysis_server/src/computer/computer_highlights2.dart';
+import 'package:analysis_server/src/computer/computer_highlights.dart';
 import 'package:analysis_server/src/computer/new_notifications.dart';
 import 'package:analysis_server/src/context_manager.dart';
 import 'package:analysis_server/src/domain_analysis.dart';
@@ -600,7 +600,6 @@
 
 /// Various IDE options.
 class AnalysisServerOptions {
-  String fileReadMode = 'as-is';
   String newAnalysisDriverLog;
 
   String clientId;
@@ -826,7 +825,7 @@
   }
 
   List<HighlightRegion> _computeHighlightRegions(CompilationUnit unit) {
-    return DartUnitHighlightsComputer2(unit).compute();
+    return DartUnitHighlightsComputer(unit).compute();
   }
 
   server.AnalysisNavigationParams _computeNavigationParams(
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
similarity index 98%
rename from pkg/analysis_server/lib/src/computer/computer_highlights2.dart
rename to pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 70489fe..df56229 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -10,16 +10,16 @@
 import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
 
 /// A computer for [HighlightRegion]s in a Dart [CompilationUnit].
-class DartUnitHighlightsComputer2 {
+class DartUnitHighlightsComputer {
   final CompilationUnit _unit;
 
   final List<HighlightRegion> _regions = <HighlightRegion>[];
 
-  DartUnitHighlightsComputer2(this._unit);
+  DartUnitHighlightsComputer(this._unit);
 
   /// Returns the computed highlight regions, not `null`.
   List<HighlightRegion> compute() {
-    _unit.accept(_DartUnitHighlightsComputerVisitor2(this));
+    _unit.accept(_DartUnitHighlightsComputerVisitor(this));
     _addCommentRanges();
     return _regions;
   }
@@ -429,11 +429,11 @@
   }
 }
 
-/// An AST visitor for [DartUnitHighlightsComputer2].
-class _DartUnitHighlightsComputerVisitor2 extends RecursiveAstVisitor<void> {
-  final DartUnitHighlightsComputer2 computer;
+/// An AST visitor for [DartUnitHighlightsComputer].
+class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<void> {
+  final DartUnitHighlightsComputer computer;
 
-  _DartUnitHighlightsComputerVisitor2(this.computer);
+  _DartUnitHighlightsComputerVisitor(this.computer);
 
   @override
   void visitAnnotation(Annotation node) {
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
index ae00fb8..832f432 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
@@ -64,18 +64,8 @@
       return;
     }
 
-    PhysicalResourceProvider resourceProvider;
-    if (analysisServerOptions.fileReadMode == 'as-is') {
-      resourceProvider = PhysicalResourceProvider(null,
-          stateLocation: analysisServerOptions.cacheFolder);
-    } else if (analysisServerOptions.fileReadMode == 'normalize-eol-always') {
-      resourceProvider = PhysicalResourceProvider(
-          PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS,
-          stateLocation: analysisServerOptions.cacheFolder);
-    } else {
-      throw Exception(
-          'File read mode was set to the unknown mode: $analysisServerOptions.fileReadMode');
-    }
+    var resourceProvider = PhysicalResourceProvider(
+        stateLocation: analysisServerOptions.cacheFolder);
 
     analysisServer = LspAnalysisServer(
         serverChannel,
diff --git a/pkg/analysis_server/lib/src/operation/operation_analysis.dart b/pkg/analysis_server/lib/src/operation/operation_analysis.dart
index 1f2391e..a53a0cf 100644
--- a/pkg/analysis_server/lib/src/operation/operation_analysis.dart
+++ b/pkg/analysis_server/lib/src/operation/operation_analysis.dart
@@ -5,7 +5,7 @@
 import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/computer/computer_closingLabels.dart';
 import 'package:analysis_server/src/computer/computer_folding.dart';
-import 'package:analysis_server/src/computer/computer_highlights2.dart';
+import 'package:analysis_server/src/computer/computer_highlights.dart';
 import 'package:analysis_server/src/computer/computer_outline.dart';
 import 'package:analysis_server/src/computer/computer_overrides.dart';
 import 'package:analysis_server/src/domains/analysis/implemented_dart.dart';
@@ -99,7 +99,7 @@
 void sendAnalysisNotificationHighlights(
     AnalysisServer server, String file, CompilationUnit dartUnit) {
   _sendNotification(server, () {
-    var regions = DartUnitHighlightsComputer2(dartUnit).compute();
+    var regions = DartUnitHighlightsComputer(dartUnit).compute();
     var params = protocol.AnalysisHighlightsParams(file, regions);
     server.sendNotification(params.toNotification());
   });
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 36cd808..754fb57 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -112,7 +112,8 @@
       List<String> allowed,
       Map<String, String> allowedHelp,
       String defaultsTo,
-      void Function(Object) callback}) {
+      void Function(Object) callback,
+      bool hide = false}) {
     _knownFlags.add(name);
     _parser.addOption(name,
         abbr: abbr,
@@ -121,7 +122,8 @@
         allowed: allowed,
         allowedHelp: allowedHelp,
         defaultsTo: defaultsTo,
-        callback: callback);
+        callback: callback,
+        hide: hide);
   }
 
   /// Generates a string displaying usage information for the defined options.
@@ -229,9 +231,6 @@
   static const String DISABLE_SERVER_FEATURE_SEARCH =
       'disable-server-feature-search';
 
-  /// The name of the option used to set the file read mode.
-  static const String FILE_READ_MODE = 'file-read-mode';
-
   /// The name of the option used to print usage information.
   static const String HELP_OPTION = 'help';
 
@@ -305,7 +304,6 @@
     var results = parser.parse(arguments, <String, String>{});
 
     var analysisServerOptions = AnalysisServerOptions();
-    analysisServerOptions.fileReadMode = results[FILE_READ_MODE];
     analysisServerOptions.newAnalysisDriverLog =
         results[NEW_ANALYSIS_DRIVER_LOG];
     analysisServerOptions.clientId = results[CLIENT_ID];
@@ -763,18 +761,6 @@
             ' status and performance information');
     parser.addOption(SDK_OPTION,
         valueHelp: 'path', help: 'Path to the Dart sdk');
-    parser.addOption(FILE_READ_MODE,
-        help: 'an option for reading files (some clients normalize eol '
-            'characters, which make the file offset and range information '
-            'incorrect)',
-        valueHelp: 'mode',
-        allowed: ['as-is', 'normalize-eol-always'],
-        allowedHelp: {
-          'as-is': 'file contents are read as-is',
-          'normalize-eol-always':
-              r"eol characters normalized to the single character new line ('\n')"
-        },
-        defaultsTo: 'as-is');
     parser.addOption(CACHE_FOLDER,
         valueHelp: 'path', help: 'Path to the location to write cache data');
     parser.addFlag(USE_LSP,
@@ -808,9 +794,11 @@
     parser.addMultiOption('enable-experiment', hide: true);
     // Removed 9/23/2020.
     parser.addFlag('enable-instrumentation', hide: true);
+    // Removed 11/12/2020.
+    parser.addOption('file-read-mode', hide: true);
     // Removed 11/8/2020.
     parser.addFlag('preview-dart-2', hide: true);
-    // Removed 11/11/2020.
+    // Removed 11/12/2020.
     parser.addFlag('useAnalysisHighlight2', hide: true);
     // Removed 9/23/2020.
     parser.addFlag('use-fasta-parser', hide: true);
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index cd9e6b9..cb8540a 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -16,6 +16,7 @@
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/value_generator.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/variable_scope.dart';
 import 'package:analysis_server/src/utilities/extensions/yaml.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:meta/meta.dart';
@@ -117,8 +118,13 @@
   /// The error reporter to which diagnostics will be reported.
   final ErrorReporter errorReporter;
 
+  /// The name of the package from which the data file being translated was
+  /// found.
   final String packageName;
 
+  /// The variable scope defined for the current transform.
+  VariableScope transformVariableScope = VariableScope.empty;
+
   /// The parameter modifications associated with the current transform, or
   /// `null` if the current transform does not yet have any such modifications.
   List<ParameterModification> _parameterModifications;
@@ -141,8 +147,8 @@
 
   /// Convert the given [template] into a list of components. Variable
   /// references in the template are looked up in the map of [generators].
-  List<TemplateComponent> _extractTemplateComponents(String template,
-      Map<String, ValueGenerator> generators, int templateOffset) {
+  List<TemplateComponent> _extractTemplateComponents(
+      String template, VariableScope variableScope, int templateOffset) {
     var components = <TemplateComponent>[];
     var textStart = 0;
     var variableStart = template.indexOf(_openComponent);
@@ -163,7 +169,7 @@
         return components;
       } else {
         var name = template.substring(variableStart + 2, endIndex).trim();
-        var generator = generators[name];
+        var generator = variableScope.lookup(name);
         if (generator == null) {
           errorReporter.reportErrorForOffset(
               TransformSetErrorCode.undefinedVariable,
@@ -490,10 +496,11 @@
         }
       }
       // TODO(brianwilkerson) We should report unreferenced variables.
-      var generators = _translateTemplateVariables(node.valueAt(_variablesKey),
+      var variableScope = _translateTemplateVariables(
+          node.valueAt(_variablesKey),
           ErrorContext(key: _variablesKey, parentNode: node));
       var components =
-          _extractTemplateComponents(template, generators, templateOffset);
+          _extractTemplateComponents(template, variableScope, templateOffset);
       return CodeTemplate(kind, components);
     } else if (node == null) {
       if (required) {
@@ -766,11 +773,10 @@
     }
   }
 
-  /// Translate the [node] into a list of template components. Return the
-  /// resulting list, or `null` if the [node] does not represent a valid
-  /// variables map. If the [node] is not valid, use the [context] to report the
-  /// error.
-  Map<String, ValueGenerator> _translateTemplateVariables(
+  /// Translate the [node] into a variable scope. Return the resulting scope, or
+  /// the enclosing scope if the [node] does not represent a valid variables
+  /// map. If the [node] is not valid, use the [context] to report the error.
+  VariableScope _translateTemplateVariables(
       YamlNode node, ErrorContext context) {
     if (node is YamlMap) {
       var generators = <String, ValueGenerator>{};
@@ -784,11 +790,12 @@
           }
         }
       }
-      return generators;
+      return VariableScope(transformVariableScope, generators);
     } else if (node == null) {
-      return const {};
+      return transformVariableScope;
     } else {
-      return _reportInvalidValue(node, context, 'Map');
+      _reportInvalidValue(node, context, 'Map');
+      return transformVariableScope;
     }
   }
 
@@ -798,8 +805,14 @@
   Transform _translateTransform(YamlNode node, ErrorContext context) {
     assert(node != null);
     if (node is YamlMap) {
-      _reportUnsupportedKeys(node,
-          const {_bulkApplyKey, _changesKey, _dateKey, _elementKey, _titleKey});
+      _reportUnsupportedKeys(node, const {
+        _bulkApplyKey,
+        _changesKey,
+        _dateKey,
+        _elementKey,
+        _titleKey,
+        _variablesKey
+      });
       var title = _translateString(node.valueAt(_titleKey),
           ErrorContext(key: _titleKey, parentNode: node));
       var date = _translateDate(node.valueAt(_dateKey),
@@ -810,8 +823,12 @@
           true;
       var element = _translateElement(node.valueAt(_elementKey),
           ErrorContext(key: _elementKey, parentNode: node));
+      transformVariableScope = _translateTemplateVariables(
+          node.valueAt(_variablesKey),
+          ErrorContext(key: _variablesKey, parentNode: node));
       var changes = _translateList(node.valueAt(_changesKey),
           ErrorContext(key: _changesKey, parentNode: node), _translateChange);
+      transformVariableScope = VariableScope.empty;
       if (title == null || date == null || element == null || changes == null) {
         // The error has already been reported.
         return null;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart
index 4e5bd68..0ae0c27 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart
@@ -6,6 +6,9 @@
 
 /// A scope in which the generators associated with variables can be looked up.
 class VariableScope {
+  /// An empty variable scope.
+  static final empty = VariableScope(null, {});
+
   /// The outer scope in which this scope is nested.
   final VariableScope outerScope;
 
diff --git a/pkg/analysis_server/lib/src/socket_server.dart b/pkg/analysis_server/lib/src/socket_server.dart
index 25ad0f77..b86d18d 100644
--- a/pkg/analysis_server/lib/src/socket_server.dart
+++ b/pkg/analysis_server/lib/src/socket_server.dart
@@ -17,7 +17,9 @@
 
 abstract class AbstractSocketServer {
   AbstractAnalysisServer get analysisServer;
+
   AnalysisServerOptions get analysisServerOptions;
+
   DiagnosticServer get diagnosticServer;
 }
 
@@ -66,18 +68,8 @@
       return;
     }
 
-    PhysicalResourceProvider resourceProvider;
-    if (analysisServerOptions.fileReadMode == 'as-is') {
-      resourceProvider = PhysicalResourceProvider(null,
-          stateLocation: analysisServerOptions.cacheFolder);
-    } else if (analysisServerOptions.fileReadMode == 'normalize-eol-always') {
-      resourceProvider = PhysicalResourceProvider(
-          PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS,
-          stateLocation: analysisServerOptions.cacheFolder);
-    } else {
-      throw Exception(
-          'File read mode was set to the unknown mode: $analysisServerOptions.fileReadMode');
-    }
+    var resourceProvider = PhysicalResourceProvider(
+        stateLocation: analysisServerOptions.cacheFolder);
 
     analysisServer = AnalysisServer(
       serverChannel,
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index a6b4251..8b50d8f 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -55,14 +55,14 @@
 class AbstractContextManagerTest extends ContextManagerTest {
   void test_contextsInAnalysisRoot_nestedContext() {
     var subProjPath = join(projPath, 'subproj');
-    var subProjFolder = resourceProvider.newFolder(subProjPath);
-    resourceProvider.newFile(join(subProjPath, 'pubspec.yaml'), 'contents');
+    var subProjFolder = newFolder(subProjPath);
+    newFile(join(subProjPath, 'pubspec.yaml'), content: 'contents');
     var subProjFilePath = join(subProjPath, 'file.dart');
-    resourceProvider.newFile(subProjFilePath, 'contents');
+    newFile(subProjFilePath, content: 'contents');
     manager.setRoots(<String>[projPath], <String>[]);
     // Make sure that there really are contexts for both the main project and
     // the subproject.
-    var projectFolder = resourceProvider.getFolder(projPath);
+    var projectFolder = getFolder(projPath);
     var projContextInfo = manager.getContextInfoFor(projectFolder);
     expect(projContextInfo, isNotNull);
     expect(projContextInfo.folder, projectFolder);
@@ -106,7 +106,7 @@
   "dart:typed_data": "../embedder/src/part"
   ''');
 
-    var projectFolder = resourceProvider.newFolder(projPath);
+    var projectFolder = newFolder(projPath);
 
     // NOTE that this is Not in our package path yet.
 
@@ -159,8 +159,7 @@
     manager.setRoots(<String>[projPath], <String>[]);
     await pumpEventQueue();
     // Confirm that one context was created.
-    var count = manager
-        .numberOfContextsInAnalysisRoot(resourceProvider.newFolder(projPath));
+    var count = manager.numberOfContextsInAnalysisRoot(newFolder(projPath));
     expect(count, equals(1));
     var source = sourceFactory.forUri('dart:foobar');
     expect(source, isNotNull);
@@ -178,8 +177,8 @@
     var project = convertPath('/project');
     var excludedFolder = convertPath('$project/excluded');
     // set roots
-    resourceProvider.newFolder(project);
-    resourceProvider.newFolder(excludedFolder);
+    newFolder(project);
+    newFolder(excludedFolder);
     manager.setRoots(<String>[project], <String>[excludedFolder]);
     // verify
     expect(manager.isInAnalysisRoot(convertPath('$excludedFolder/test.dart')),
@@ -188,10 +187,10 @@
 
   void test_isInAnalysisRoot_inNestedContext() {
     var subProjPath = join(projPath, 'subproj');
-    var subProjFolder = resourceProvider.newFolder(subProjPath);
-    resourceProvider.newFile(join(subProjPath, 'pubspec.yaml'), 'contents');
+    var subProjFolder = newFolder(subProjPath);
+    newFile(join(subProjPath, 'pubspec.yaml'), content: 'contents');
     var subProjFilePath = join(subProjPath, 'file.dart');
-    resourceProvider.newFile(subProjFilePath, 'contents');
+    newFile(subProjFilePath, content: 'contents');
     manager.setRoots(<String>[projPath], <String>[]);
     // Make sure that there really is a context for the subproject.
     var subProjContextInfo = manager.getContextInfoFor(subProjFolder);
@@ -214,14 +213,14 @@
   Future<void> test_packagesFolder_areAnalyzed() {
     // create a context with a pubspec.yaml file
     var pubspecPath = join(projPath, 'pubspec.yaml');
-    resourceProvider.newFile(pubspecPath, 'pubspec');
+    newFile(pubspecPath, content: 'pubspec');
     // create a file in the "packages" folder
     var filePath1 = join(projPath, 'packages', 'file1.dart');
-    var file1 = resourceProvider.newFile(filePath1, 'contents');
+    var file1 = newFile(filePath1, content: 'contents');
     manager.setRoots(<String>[projPath], <String>[]);
     expect(callbacks.currentFilePaths, unorderedEquals([file1.path]));
     var filePath2 = join(projPath, 'packages', 'file2.dart');
-    var file2 = resourceProvider.newFile(filePath2, 'contents');
+    var file2 = newFile(filePath2, content: 'contents');
     return pumpEventQueue().then((_) {
       expect(callbacks.currentFilePaths,
           unorderedEquals([file1.path, file2.path]));
@@ -230,7 +229,7 @@
 
   Future<void> test_path_filter() async {
     // Setup context.
-    var root = resourceProvider.newFolder(projPath);
+    var root = newFolder(projPath);
     manager.setRoots(<String>[projPath], <String>[]);
     expect(callbacks.currentFilePaths, isEmpty);
     // Set ignore patterns for context.
@@ -257,7 +256,7 @@
   Future<void> test_refresh_folder_with_packagespec() {
     // create a context with a .packages file
     var packagespecFile = join(projPath, '.packages');
-    resourceProvider.newFile(packagespecFile, '');
+    newFile(packagespecFile, content: '');
     manager.setRoots(<String>[projPath], <String>[]);
     return pumpEventQueue().then((_) {
       expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
@@ -280,8 +279,8 @@
     var subdir2Path = join(projPath, 'subdir2');
     var packagespec1Path = join(subdir1Path, '.packages');
     var packagespec2Path = join(subdir2Path, '.packages');
-    resourceProvider.newFile(packagespec1Path, '');
-    resourceProvider.newFile(packagespec2Path, '');
+    newFile(packagespec1Path, content: '');
+    newFile(packagespec2Path, content: '');
     manager.setRoots(<String>[projPath], <String>[]);
     return pumpEventQueue().then((_) {
       expect(callbacks.currentContextRoots,
@@ -301,7 +300,7 @@
   Future<void> test_refresh_folder_with_pubspec() {
     // create a context with a pubspec.yaml file
     var pubspecPath = join(projPath, 'pubspec.yaml');
-    resourceProvider.newFile(pubspecPath, 'pubspec');
+    newFile(pubspecPath, content: 'pubspec');
     manager.setRoots(<String>[projPath], <String>[]);
     return pumpEventQueue().then((_) {
       expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
@@ -321,8 +320,8 @@
     var subdir2Path = join(projPath, 'subdir2');
     var pubspec1Path = join(subdir1Path, 'pubspec.yaml');
     var pubspec2Path = join(subdir2Path, 'pubspec.yaml');
-    resourceProvider.newFile(pubspec1Path, 'pubspec');
-    resourceProvider.newFile(pubspec2Path, 'pubspec');
+    newFile(pubspec1Path, content: 'pubspec');
+    newFile(pubspec2Path, content: 'pubspec');
     manager.setRoots(<String>[projPath], <String>[]);
     return pumpEventQueue().then((_) {
       expect(callbacks.currentContextRoots,
@@ -342,12 +341,12 @@
   Future<void> test_refresh_oneContext() {
     // create two contexts with pubspec.yaml files
     var pubspecPath = join(projPath, 'pubspec.yaml');
-    resourceProvider.newFile(pubspecPath, 'pubspec1');
+    newFile(pubspecPath, content: 'pubspec1');
 
     var proj2Path = convertPath('/my/proj2');
-    resourceProvider.newFolder(proj2Path);
+    newFolder(proj2Path);
     var pubspec2Path = join(proj2Path, 'pubspec.yaml');
-    resourceProvider.newFile(pubspec2Path, 'pubspec2');
+    newFile(pubspec2Path, content: 'pubspec2');
 
     var roots = <String>[projPath, proj2Path];
     manager.setRoots(roots, <String>[]);
@@ -355,7 +354,7 @@
       expect(callbacks.currentContextRoots, unorderedEquals(roots));
       var then = callbacks.now;
       callbacks.now++;
-      manager.refresh([resourceProvider.getResource(proj2Path)]);
+      manager.refresh([getFolder(proj2Path)]);
       return pumpEventQueue().then((_) {
         expect(callbacks.currentContextRoots, unorderedEquals(roots));
         expect(callbacks.currentContextTimestamps[projPath], then);
@@ -365,15 +364,14 @@
   }
 
   void test_setRoots_addFolderWithDartFile() {
-    var filePath = resourceProvider.pathContext.join(projPath, 'foo.dart');
-    resourceProvider.newFile(filePath, 'contents');
+    var filePath = join(projPath, 'foo.dart');
+    newFile(filePath, content: 'contents');
     manager.setRoots(<String>[projPath], <String>[]);
     // verify
     var filePaths = callbacks.currentFilePaths;
     expect(filePaths, hasLength(1));
     expect(filePaths, contains(filePath));
-    var drivers =
-        manager.getDriversInAnalysisRoot(resourceProvider.newFolder(projPath));
+    var drivers = manager.getDriversInAnalysisRoot(newFolder(projPath));
     expect(drivers, hasLength(1));
     expect(drivers[0], isNotNull);
     var result = sourceFactory.forUri('dart:async');
@@ -382,7 +380,7 @@
 
   void test_setRoots_addFolderWithDartFileInSubfolder() {
     var filePath = join(projPath, 'foo', 'bar.dart');
-    resourceProvider.newFile(filePath, 'contents');
+    newFile(filePath, content: 'contents');
     manager.setRoots(<String>[projPath], <String>[]);
     // verify
     var filePaths = callbacks.currentFilePaths;
@@ -415,14 +413,12 @@
     expect(callbacks.currentContextRoots, contains(projPath));
     var projSources = callbacks.currentFileSources(projPath);
     expect(projSources, hasLength(1));
-    expect(projSources.first.uri.toString(),
-        (Uri.file(join(libPath, 'main.dart')).toString()));
+    expect(projSources.first.uri, toUri('$libPath/main.dart'));
 
     expect(callbacks.currentContextRoots, contains(examplePath));
     var exampleSources = callbacks.currentFileSources(examplePath);
     expect(exampleSources, hasLength(1));
-    expect(exampleSources.first.uri.toString(),
-        (Uri.file(join(examplePath, 'example.dart')).toString()));
+    expect(exampleSources.first.uri, toUri('$examplePath/example.dart'));
   }
 
   void test_setRoots_addFolderWithNestedPubspec() {
@@ -449,8 +445,7 @@
     expect(callbacks.currentContextRoots, contains(examplePath));
     var exampleSources = callbacks.currentFileSources(examplePath);
     expect(exampleSources, hasLength(1));
-    expect(exampleSources.first.uri.toString(),
-        (Uri.file('$examplePath/example.dart').toString()));
+    expect(exampleSources.first.uri, toUri('$examplePath/example.dart'));
   }
 
   void test_setRoots_addFolderWithoutPubspec() {
@@ -463,8 +458,7 @@
   void test_setRoots_addFolderWithPackagespec() {
     var packagespecPath = join(projPath, '.packages');
     var testLib = convertPath('/home/somebody/.pub/cache/unittest-0.9.9/lib');
-    var testLibUri = resourceProvider.pathContext.toUri(testLib);
-    resourceProvider.newFile(packagespecPath, 'unittest:$testLibUri');
+    newFile(packagespecPath, content: 'unittest:${toUriStr(testLib)}');
     var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
     var mainFile = newFile('$libPath/main.dart');
     var source = mainFile.createSource();
@@ -484,7 +478,7 @@
 
   void test_setRoots_addFolderWithPubspec() {
     var pubspecPath = join(projPath, 'pubspec.yaml');
-    resourceProvider.newFile(pubspecPath, 'pubspec');
+    newFile(pubspecPath, content: 'pubspec');
     manager.setRoots(<String>[projPath], <String>[]);
     // verify
     expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
@@ -494,8 +488,8 @@
   void test_setRoots_addFolderWithPubspec_andPackagespec() {
     var pubspecPath = join(projPath, 'pubspec.yaml');
     var packagespecPath = join(projPath, '.packages');
-    resourceProvider.newFile(pubspecPath, 'pubspec');
-    resourceProvider.newFile(packagespecPath, '');
+    newFile(pubspecPath, content: 'pubspec');
+    newFile(packagespecPath, content: '');
     manager.setRoots(<String>[projPath], <String>[]);
     // verify
     callbacks.assertContextPaths([projPath]);
@@ -521,10 +515,10 @@
     expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
     expect(sources, hasLength(4));
     var uris = sources.map((Source source) => source.uri.toString()).toList();
-    expect(uris, contains((Uri.file(appPath)).toString()));
+    expect(uris, contains(toUriStr(appPath)));
     expect(uris, contains('package:proj/main.dart'));
     expect(uris, contains('package:proj/src/internal.dart'));
-    expect(uris, contains((Uri.file(testFilePath)).toString()));
+    expect(uris, contains(toUriStr(testFilePath)));
   }
 
   void test_setRoots_addFolderWithPubspecAndPackagespecFolders() {
@@ -571,9 +565,9 @@
     newFile('$projectB/${ContextManagerImpl.PUBSPEC_NAME}');
     newFile('$projectB/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
         content: 'bar:lib/');
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subProjectA_file, 'library a;');
-    resourceProvider.newFile(subProjectB_file, 'library b;');
+    newFile(rootFile, content: 'library root;');
+    newFile(subProjectA_file, content: 'library a;');
+    newFile(subProjectB_file, content: 'library b;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root, projectA, projectB]);
@@ -586,12 +580,12 @@
     expect(
         _packageMap(projectA),
         equals({
-          'foo': [resourceProvider.getFolder(projectALib)]
+          'foo': [getFolder(projectALib)]
         }));
     expect(
         _packageMap(projectB),
         equals({
-          'bar': [resourceProvider.getFolder(projectBLib)]
+          'bar': [getFolder(projectBLib)]
         }));
   }
 
@@ -601,8 +595,8 @@
     var file1 = convertPath('$project/file1.dart');
     var file2 = convertPath('$project/file2.dart');
     // create files
-    resourceProvider.newFile(file1, '// 1');
-    resourceProvider.newFile(file2, '// 2');
+    newFile(file1, content: '// 1');
+    newFile(file2, content: '// 2');
     // set roots
     manager.setRoots(<String>[project], <String>[file1]);
     callbacks.assertContextPaths([project]);
@@ -617,8 +611,8 @@
     var fileA = convertPath('$folderA/a.dart');
     var fileB = convertPath('$folderB/b.dart');
     // create files
-    resourceProvider.newFile(fileA, 'library a;');
-    resourceProvider.newFile(fileB, 'library b;');
+    newFile(fileA, content: 'library a;');
+    newFile(fileB, content: 'library b;');
     // set roots
     manager.setRoots(<String>[project], <String>[folderB]);
     callbacks.assertContextPaths([project]);
@@ -631,8 +625,8 @@
     var file1 = convertPath('$project/file1.dart');
     var file2 = convertPath('$project/file2.dart');
     // create files
-    resourceProvider.newFile(file1, '// 1');
-    resourceProvider.newFile(file2, '// 2');
+    newFile(file1, content: '// 1');
+    newFile(file2, content: '// 2');
     // set roots
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
@@ -651,8 +645,8 @@
     var fileA = convertPath('$folderA/a.dart');
     var fileB = convertPath('$folderB/b.dart');
     // create files
-    resourceProvider.newFile(fileA, 'library a;');
-    resourceProvider.newFile(fileB, 'library b;');
+    newFile(fileA, content: 'library a;');
+    newFile(fileB, content: 'library b;');
     // initially both "aaa/a" and "bbb/b" are included
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
@@ -669,8 +663,8 @@
     var file1 = convertPath('$project/file1.dart');
     var file2 = convertPath('$project/file2.dart');
     // create files
-    resourceProvider.newFile(file1, '// 1');
-    resourceProvider.newFile(file2, '// 2');
+    newFile(file1, content: '// 1');
+    newFile(file2, content: '// 2');
     // set roots
     manager.setRoots(<String>[project], <String>[file2]);
     callbacks.assertContextPaths([project]);
@@ -687,8 +681,8 @@
     var file1 = convertPath('$project/bin/file1.dart');
     var file2 = convertPath('$project/bin/file2.dart');
     // create files
-    resourceProvider.newFile(file1, '// 1');
-    resourceProvider.newFile(file2, '// 2');
+    newFile(file1, content: '// 1');
+    newFile(file2, content: '// 2');
     // set roots
     manager.setRoots(<String>[project], <String>[file2]);
     callbacks.assertContextPaths([project]);
@@ -707,8 +701,8 @@
     var fileA = convertPath('$folderA/a.dart');
     var fileB = convertPath('$folderB/b.dart');
     // create files
-    resourceProvider.newFile(fileA, 'library a;');
-    resourceProvider.newFile(fileB, 'library b;');
+    newFile(fileA, content: 'library a;');
+    newFile(fileB, content: 'library b;');
     // exclude "bbb/"
     manager.setRoots(<String>[project], <String>[folderB]);
     callbacks.assertContextPaths([project]);
@@ -724,9 +718,9 @@
     var fileA = convertPath('$project/foo.dart');
     var fileB = convertPath('$project/lib/doc/bar.dart');
     var fileC = convertPath('$project/doc/bar.dart');
-    resourceProvider.newFile(fileA, '');
-    resourceProvider.newFile(fileB, '');
-    resourceProvider.newFile(fileC, '');
+    newFile(fileA, content: '');
+    newFile(fileB, content: '');
+    newFile(fileC, content: '');
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA, fileB]);
@@ -738,8 +732,8 @@
     var example = convertPath('$project/example');
     var examplePubspec = convertPath('$example/pubspec.yaml');
     // create files
-    resourceProvider.newFile(projectPubspec, 'name: project');
-    resourceProvider.newFile(examplePubspec, 'name: example');
+    newFile(projectPubspec, content: 'name: project');
+    newFile(examplePubspec, content: 'name: example');
     manager.setRoots(<String>[example, project], <String>[]);
     // verify
     {
@@ -764,8 +758,8 @@
     var projectPubspec = convertPath('$project/pubspec.yaml');
     var example = convertPath('$project/example');
     // create files
-    resourceProvider.newFile(projectPubspec, 'name: project');
-    resourceProvider.newFolder(example);
+    newFile(projectPubspec, content: 'name: project');
+    newFolder(example);
     manager.setRoots(<String>[project, example], <String>[]);
     // verify
     {
@@ -786,8 +780,8 @@
     var example = convertPath('$project/example');
     var examplePubspec = convertPath('$example/pubspec.yaml');
     // create files
-    resourceProvider.newFile(projectPubspec, 'name: project');
-    resourceProvider.newFile(examplePubspec, 'name: example');
+    newFile(projectPubspec, content: 'name: project');
+    newFile(examplePubspec, content: 'name: example');
     manager.setRoots(<String>[project, example], <String>[]);
     // verify
     {
@@ -811,7 +805,7 @@
     var packagePath = convertPath('/package/foo');
     newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
         content: 'foo:${toUriStr('/package/foo')}');
-    var packageFolder = resourceProvider.newFolder(packagePath);
+    var packageFolder = newFolder(packagePath);
     manager.setRoots(<String>[projPath], <String>[]);
     expect(
         _currentPackageMap,
@@ -826,7 +820,7 @@
     var excludedFolder = convertPath('$project/excluded');
     var excludedPubspec = convertPath('$excludedFolder/pubspec.yaml');
     // create files
-    resourceProvider.newFile(excludedPubspec, 'name: ignore-me');
+    newFile(excludedPubspec, content: 'name: ignore-me');
     // set "/project", and exclude "/project/excluded"
     manager.setRoots(<String>[project], <String>[excludedFolder]);
     callbacks.assertContextPaths([project]);
@@ -834,7 +828,7 @@
 
   void test_setRoots_noContext_inDotFolder() {
     var pubspecPath = join(projPath, '.pub', 'pubspec.yaml');
-    resourceProvider.newFile(pubspecPath, 'name: test');
+    newFile(pubspecPath, content: 'name: test');
     manager.setRoots(<String>[projPath], <String>[]);
     // verify
     expect(callbacks.currentContextRoots, hasLength(1));
@@ -846,11 +840,10 @@
     var filePath = join(projPath, 'lib', 'foo.dart');
     newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
         content: 'foo:lib/');
-    resourceProvider.newFile(filePath, 'contents');
+    newFile(filePath, content: 'contents');
     manager.setRoots(<String>[projPath], <String>[]);
 
-    var drivers =
-        manager.getDriversInAnalysisRoot(resourceProvider.newFolder(projPath));
+    var drivers = manager.getDriversInAnalysisRoot(newFolder(projPath));
     expect(drivers, hasLength(1));
     expect(drivers[0], isNotNull);
     var result = sourceFactory.forUri('package:foo/foo.dart');
@@ -859,7 +852,7 @@
 
   void test_setRoots_packagesFolder_hasContext() {
     var pubspecPath = join(projPath, 'packages', 'pubspec.yaml');
-    resourceProvider.newFile(pubspecPath, 'name: test');
+    newFile(pubspecPath, content: 'name: test');
     manager.setRoots(<String>[projPath], <String>[]);
     // verify
     expect(callbacks.currentContextRoots, hasLength(2));
@@ -873,8 +866,8 @@
     var project = convertPath('/project');
     var fileA = convertPath('$project/foo.dart');
     var fileB = convertPath('$project/.pub/bar.dart');
-    resourceProvider.newFile(fileA, '');
-    resourceProvider.newFile(fileB, '');
+    newFile(fileA, content: '');
+    newFile(fileB, content: '');
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA]);
@@ -893,7 +886,7 @@
   void test_setRoots_removeFolderWithPackagespec() {
     // create a pubspec
     var pubspecPath = join(projPath, '.packages');
-    resourceProvider.newFile(pubspecPath, '');
+    newFile(pubspecPath, content: '');
     // add one root - there is a context
     manager.setRoots(<String>[projPath], <String>[]);
     expect(manager.changeSubscriptions, hasLength(1));
@@ -918,12 +911,12 @@
     var subProjectA_file = convertPath('$subProjectA/bin/sub_a.dart');
     var subProjectB_file = convertPath('$subProjectB/bin/sub_b.dart');
     // create files
-    resourceProvider.newFile(projectA_file, '// a');
-    resourceProvider.newFile(projectB_file, '// b');
-    resourceProvider.newFile(subProjectA_pubspec, '');
-    resourceProvider.newFile(subProjectB_pubspec, '');
-    resourceProvider.newFile(subProjectA_file, '// sub-a');
-    resourceProvider.newFile(subProjectB_file, '// sub-b');
+    newFile(projectA_file, content: '// a');
+    newFile(projectB_file, content: '// b');
+    newFile(subProjectA_pubspec, content: '');
+    newFile(subProjectB_pubspec, content: '');
+    newFile(subProjectA_file, content: '// sub-a');
+    newFile(subProjectB_file, content: '// sub-b');
     // set roots
     manager.setRoots(<String>[projectA, projectB], <String>[]);
     callbacks
@@ -942,7 +935,7 @@
   void test_setRoots_removeFolderWithPubspec() {
     // create a pubspec
     var pubspecPath = join(projPath, 'pubspec.yaml');
-    resourceProvider.newFile(pubspecPath, 'pubspec');
+    newFile(pubspecPath, content: 'pubspec');
     // add one root - there is a context
     manager.setRoots(<String>[projPath], <String>[]);
     expect(callbacks.currentContextRoots, hasLength(1));
@@ -965,12 +958,12 @@
     var subProjectA_file = convertPath('$subProjectA/bin/sub_a.dart');
     var subProjectB_file = convertPath('$subProjectB/bin/sub_b.dart');
     // create files
-    resourceProvider.newFile(projectA_file, '// a');
-    resourceProvider.newFile(projectB_file, '// b');
-    resourceProvider.newFile(subProjectA_pubspec, 'pubspec');
-    resourceProvider.newFile(subProjectB_pubspec, 'pubspec');
-    resourceProvider.newFile(subProjectA_file, '// sub-a');
-    resourceProvider.newFile(subProjectB_file, '// sub-b');
+    newFile(projectA_file, content: '// a');
+    newFile(projectB_file, content: '// b');
+    newFile(subProjectA_pubspec, content: 'pubspec');
+    newFile(subProjectB_pubspec, content: 'pubspec');
+    newFile(subProjectA_file, content: '// sub-a');
+    newFile(subProjectB_file, content: '// sub-b');
     // set roots
     manager.setRoots(<String>[projectA, projectB], <String>[]);
     callbacks
@@ -992,7 +985,7 @@
     // context to be ignored.
     var project = convertPath('/.pub/project');
     var fileA = convertPath('$project/foo.dart');
-    resourceProvider.newFile(fileA, '');
+    newFile(fileA, content: '');
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA]);
@@ -1017,7 +1010,7 @@
     expect(callbacks.currentFilePaths, hasLength(0));
     // add file
     var filePath = join(projPath, 'foo.dart');
-    resourceProvider.newFile(filePath, 'contents');
+    newFile(filePath, content: 'contents');
     // the file was added
     return pumpEventQueue().then((_) {
       var filePaths = callbacks.currentFilePaths;
@@ -1034,13 +1027,13 @@
     var fileA = convertPath('$folderA/a.dart');
     var fileB = convertPath('$folderB/b.dart');
     // create files
-    resourceProvider.newFile(fileA, 'library a;');
+    newFile(fileA, content: 'library a;');
     // set roots
     manager.setRoots(<String>[project], <String>[folderB]);
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA]);
     // add a file, ignored as excluded
-    resourceProvider.newFile(fileB, 'library b;');
+    newFile(fileB, content: 'library b;');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([project]);
       callbacks.assertContextFiles(project, [fileA]);
@@ -1053,13 +1046,13 @@
     var fileA = convertPath('$project/a.dart');
     var fileB = convertPath('$project/lib/doc/b.dart');
     // create files
-    resourceProvider.newFile(fileA, '');
+    newFile(fileA, content: '');
     // set roots
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA]);
     // add a "lib/doc" file, it is not ignored
-    resourceProvider.newFile(fileB, '');
+    newFile(fileB, content: '');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([project]);
       callbacks.assertContextFiles(project, [fileA, fileB]);
@@ -1072,13 +1065,13 @@
     var fileA = convertPath('$project/a.dart');
     var fileB = convertPath('$project/doc/b.dart');
     // create files
-    resourceProvider.newFile(fileA, '');
+    newFile(fileA, content: '');
     // set roots
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA]);
     // add a "doc" file, it is ignored
-    resourceProvider.newFile(fileB, '');
+    newFile(fileB, content: '');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([project]);
       callbacks.assertContextFiles(project, [fileA]);
@@ -1091,11 +1084,11 @@
     var project = convertPath('/project');
     var fileA = convertPath('$project/foo.dart');
     var fileB = convertPath('$project/.pub/bar.dart');
-    resourceProvider.newFile(fileA, '');
+    newFile(fileA, content: '');
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA]);
-    resourceProvider.newFile(fileB, '');
+    newFile(fileB, content: '');
     await pumpEventQueue();
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA]);
@@ -1107,11 +1100,11 @@
     var project = convertPath('/.pub/project');
     var fileA = convertPath('$project/foo.dart');
     var fileB = convertPath('$project/bar/baz.dart');
-    resourceProvider.newFile(fileA, '');
+    newFile(fileA, content: '');
     manager.setRoots(<String>[project], <String>[]);
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA]);
-    resourceProvider.newFile(fileB, '');
+    newFile(fileB, content: '');
     await pumpEventQueue();
     callbacks.assertContextPaths([project]);
     callbacks.assertContextFiles(project, [fileA, fileB]);
@@ -1123,7 +1116,7 @@
     expect(callbacks.currentFilePaths, hasLength(0));
     // add file in subfolder
     var filePath = join(projPath, 'foo', 'bar.dart');
-    resourceProvider.newFile(filePath, 'contents');
+    newFile(filePath, content: 'contents');
     // the file was added
     return pumpEventQueue().then((_) {
       var filePaths = callbacks.currentFilePaths;
@@ -1138,14 +1131,14 @@
     var rootFile = convertPath('$root/root.dart');
     var rootPackagespec = convertPath('$root/.packages');
     // create files
-    resourceProvider.newFile(rootFile, 'library root;');
+    newFile(rootFile, content: 'library root;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root]);
     // verify files
     callbacks.assertContextFiles(root, [rootFile]);
     // add packagespec - still just one root
-    resourceProvider.newFile(rootPackagespec, '');
+    newFile(rootPackagespec, content: '');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root]);
       callbacks.assertContextFiles(root, [rootFile]);
@@ -1162,15 +1155,15 @@
     var subPubspec = convertPath('$subProject/.packages');
     var subFile = convertPath('$subProject/bin/a.dart');
     // create files
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subFile, 'library a;');
+    newFile(rootFile, content: 'library root;');
+    newFile(subFile, content: 'library a;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root]);
     // verify files
     callbacks.assertContextFiles(root, [rootFile, subFile]);
     // add .packages
-    resourceProvider.newFile(subPubspec, '');
+    newFile(subPubspec, content: '');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root, subProject]);
       callbacks.assertContextFiles(root, [rootFile]);
@@ -1187,16 +1180,16 @@
     var subFile = convertPath('$subProject/bin/sub.dart');
     var subSubPubspec = convertPath('$subProject/subsub/.packages');
     // create files
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subPubspec, '');
-    resourceProvider.newFile(subFile, 'library sub;');
+    newFile(rootFile, content: 'library root;');
+    newFile(subPubspec, content: '');
+    newFile(subFile, content: 'library sub;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root, subProject]);
     callbacks.assertContextFiles(root, [rootFile]);
     callbacks.assertContextFiles(subProject, [subFile]);
     // add pubspec - ignore, because is already in a packagespec-based context
-    resourceProvider.newFile(subSubPubspec, '');
+    newFile(subSubPubspec, content: '');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root, subProject]);
       callbacks.assertContextFiles(root, [rootFile]);
@@ -1213,9 +1206,9 @@
     var subPubspec = convertPath('$subProject/pubspec.yaml');
     var subFile = convertPath('$subProject/bin/a.dart');
     // create files
-    resourceProvider.newFile(subPubspec, 'pubspec');
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subFile, 'library a;');
+    newFile(subPubspec, content: 'pubspec');
+    newFile(rootFile, content: 'library root;');
+    newFile(subFile, content: 'library a;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root, subProject]);
@@ -1224,7 +1217,7 @@
     callbacks.assertContextFiles(subProject, [subFile]);
 
     // add .packages
-    resourceProvider.newFile(subPackagespec, '');
+    newFile(subPackagespec, content: '');
     return pumpEventQueue().then((_) {
       // Should NOT create another context.
       callbacks.assertContextPaths([root, subProject]);
@@ -1239,14 +1232,14 @@
     var rootFile = convertPath('$root/root.dart');
     var rootPubspec = convertPath('$root/pubspec.yaml');
     // create files
-    resourceProvider.newFile(rootFile, 'library root;');
+    newFile(rootFile, content: 'library root;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root]);
     // verify files
     callbacks.assertContextFiles(root, [rootFile]);
     // add pubspec - still just one root
-    resourceProvider.newFile(rootPubspec, 'pubspec');
+    newFile(rootPubspec, content: 'pubspec');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root]);
       callbacks.assertContextFiles(root, [rootFile]);
@@ -1261,15 +1254,15 @@
     var subPubspec = convertPath('$subProject/pubspec.yaml');
     var subFile = convertPath('$subProject/bin/a.dart');
     // create files
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subFile, 'library a;');
+    newFile(rootFile, content: 'library root;');
+    newFile(subFile, content: 'library a;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root]);
     // verify files
     callbacks.assertContextFiles(root, [rootFile, subFile]);
     // add pubspec
-    resourceProvider.newFile(subPubspec, 'pubspec');
+    newFile(subPubspec, content: 'pubspec');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root, subProject]);
       callbacks.assertContextFiles(root, [rootFile]);
@@ -1286,16 +1279,16 @@
     var subFile = convertPath('$subProject/bin/sub.dart');
     var subSubPubspec = convertPath('$subProject/subsub/pubspec.yaml');
     // create files
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subPubspec, 'pubspec');
-    resourceProvider.newFile(subFile, 'library sub;');
+    newFile(rootFile, content: 'library root;');
+    newFile(subPubspec, content: 'pubspec');
+    newFile(subFile, content: 'library sub;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root, subProject]);
     callbacks.assertContextFiles(root, [rootFile]);
     callbacks.assertContextFiles(subProject, [subFile]);
     // add pubspec - ignore, because is already in a pubspec-based context
-    resourceProvider.newFile(subSubPubspec, 'pubspec');
+    newFile(subSubPubspec, content: 'pubspec');
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root, subProject]);
       callbacks.assertContextFiles(root, [rootFile]);
@@ -1306,7 +1299,7 @@
   Future<void> test_watch_deleteFile() {
     var filePath = join(projPath, 'foo.dart');
     // add root with a file
-    var file = resourceProvider.newFile(filePath, 'contents');
+    var file = newFile(filePath, content: 'contents');
     var projFolder = file.parent;
     manager.setRoots(<String>[projPath], <String>[]);
     // the file was added
@@ -1316,7 +1309,7 @@
     expect(file.exists, isTrue);
     expect(projFolder.exists, isTrue);
     // delete the file
-    resourceProvider.deleteFile(filePath);
+    deleteFile(filePath);
     return pumpEventQueue().then((_) {
       expect(file.exists, isFalse);
       expect(projFolder.exists, isTrue);
@@ -1327,7 +1320,7 @@
   Future<void> test_watch_deleteFolder() {
     var filePath = join(projPath, 'foo.dart');
     // add root with a file
-    var file = resourceProvider.newFile(filePath, 'contents');
+    var file = newFile(filePath, content: 'contents');
     var projFolder = file.parent;
     manager.setRoots(<String>[projPath], <String>[]);
     // the file was added
@@ -1337,7 +1330,7 @@
     expect(file.exists, isTrue);
     expect(projFolder.exists, isTrue);
     // delete the folder
-    resourceProvider.deleteFolder(projPath);
+    deleteFolder(projPath);
     return pumpEventQueue().then((_) {
       expect(file.exists, isFalse);
       expect(projFolder.exists, isFalse);
@@ -1351,14 +1344,14 @@
     var rootPubspec = convertPath('$root/.packages');
     var rootFile = convertPath('$root/root.dart');
     // create files
-    resourceProvider.newFile(rootPubspec, '');
-    resourceProvider.newFile(rootFile, 'library root;');
+    newFile(rootPubspec, content: '');
+    newFile(rootFile, content: 'library root;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root]);
     callbacks.assertContextFiles(root, [rootFile]);
     // delete the pubspec
-    resourceProvider.deleteFile(rootPubspec);
+    deleteFile(rootPubspec);
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root]);
       callbacks.assertContextFiles(root, [rootFile]);
@@ -1373,9 +1366,9 @@
     var subPubspec = convertPath('$subProject/.packages');
     var subFile = convertPath('$subProject/bin/a.dart');
     // create files
-    resourceProvider.newFile(subPubspec, '');
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subFile, 'library a;');
+    newFile(subPubspec, content: '');
+    newFile(rootFile, content: 'library root;');
+    newFile(subFile, content: 'library a;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root, subProject]);
@@ -1383,7 +1376,7 @@
     callbacks.assertContextFiles(root, [rootFile]);
     callbacks.assertContextFiles(subProject, [subFile]);
     // delete the pubspec
-    resourceProvider.deleteFile(subPubspec);
+    deleteFile(subPubspec);
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root]);
       callbacks.assertContextFiles(root, [rootFile, subFile]);
@@ -1409,10 +1402,10 @@
     var subPubspec = convertPath('$subProject/pubspec.yaml');
     var subFile = convertPath('$subProject/bin/a.dart');
     // create files
-    resourceProvider.newFile(subPackagespec, '');
-    resourceProvider.newFile(subPubspec, 'pubspec');
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subFile, 'library a;');
+    newFile(subPackagespec, content: '');
+    newFile(subPubspec, content: 'pubspec');
+    newFile(rootFile, content: 'library root;');
+    newFile(subFile, content: 'library a;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root, subProject]);
@@ -1420,7 +1413,7 @@
     callbacks.assertContextFiles(root, [rootFile]);
     callbacks.assertContextFiles(subProject, [subFile]);
     // delete the packagespec
-    resourceProvider.deleteFile(subPackagespec);
+    deleteFile(subPackagespec);
     return pumpEventQueue().then((_) {
       // Should NOT merge
       callbacks.assertContextPaths([root, subProject]);
@@ -1434,14 +1427,14 @@
     var rootPubspec = convertPath('$root/pubspec.yaml');
     var rootFile = convertPath('$root/root.dart');
     // create files
-    resourceProvider.newFile(rootPubspec, 'pubspec');
-    resourceProvider.newFile(rootFile, 'library root;');
+    newFile(rootPubspec, content: 'pubspec');
+    newFile(rootFile, content: 'library root;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root]);
     callbacks.assertContextFiles(root, [rootFile]);
     // delete the pubspec
-    resourceProvider.deleteFile(rootPubspec);
+    deleteFile(rootPubspec);
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root]);
       callbacks.assertContextFiles(root, [rootFile]);
@@ -1456,9 +1449,9 @@
     var subPubspec = convertPath('$subProject/pubspec.yaml');
     var subFile = convertPath('$subProject/bin/a.dart');
     // create files
-    resourceProvider.newFile(subPubspec, 'pubspec');
-    resourceProvider.newFile(rootFile, 'library root;');
-    resourceProvider.newFile(subFile, 'library a;');
+    newFile(subPubspec, content: 'pubspec');
+    newFile(rootFile, content: 'library root;');
+    newFile(subFile, content: 'library a;');
     // set roots
     manager.setRoots(<String>[root], <String>[]);
     callbacks.assertContextPaths([root, subProject]);
@@ -1466,7 +1459,7 @@
     callbacks.assertContextFiles(root, [rootFile]);
     callbacks.assertContextFiles(subProject, [subFile]);
     // delete the pubspec
-    resourceProvider.deleteFile(subPubspec);
+    deleteFile(subPubspec);
     return pumpEventQueue().then((_) {
       callbacks.assertContextPaths([root]);
       callbacks.assertContextFiles(root, [rootFile, subFile]);
@@ -1476,7 +1469,7 @@
   Future<void> test_watch_modifyFile() {
     var filePath = join(projPath, 'foo.dart');
     // add root with a file
-    resourceProvider.newFile(filePath, 'contents');
+    newFile(filePath, content: 'contents');
     manager.setRoots(<String>[projPath], <String>[]);
     // the file was added
     var filePaths = callbacks.currentFilePaths;
@@ -1485,7 +1478,7 @@
     // TODO(brianwilkerson) Test when the file was modified
     // update the file
     callbacks.now++;
-    resourceProvider.modifyFile(filePath, 'new contents');
+    modifyFile(filePath, 'new contents');
     return pumpEventQueue().then((_) {
       // TODO(brianwilkerson) Test when the file was modified
     });
@@ -1495,8 +1488,8 @@
     var packageConfigPath = '$projPath/.dart_tool/package_config.json';
     var filePath = convertPath('$projPath/bin/main.dart');
 
-    resourceProvider.newFile(packageConfigPath, '');
-    resourceProvider.newFile(filePath, 'library main;');
+    newFile(packageConfigPath, content: '');
+    newFile(filePath, content: 'library main;');
 
     manager.setRoots(<String>[projPath], <String>[]);
 
@@ -1507,7 +1500,7 @@
 
     // update .dart_tool/package_config.json
     callbacks.now++;
-    resourceProvider.modifyFile(
+    modifyFile(
       packageConfigPath,
       (PackageConfigFileBuilder()..add(name: 'my', rootPath: '../'))
           .toContent(toUriStr: toUriStr),
@@ -1523,8 +1516,8 @@
     var packagesPath = convertPath('$projPath/.packages');
     var filePath = convertPath('$projPath/bin/main.dart');
 
-    resourceProvider.newFile(packagesPath, '');
-    resourceProvider.newFile(filePath, 'library main;');
+    newFile(packagesPath, content: '');
+    newFile(filePath, content: 'library main;');
 
     manager.setRoots(<String>[projPath], <String>[]);
 
@@ -1535,7 +1528,7 @@
 
     // update .packages
     callbacks.now++;
-    resourceProvider.modifyFile(packagesPath, 'main:./lib/');
+    modifyFile(packagesPath, 'main:./lib/');
     return pumpEventQueue().then((_) {
       // verify new package info
       expect(_currentPackageMap.keys, unorderedEquals(['main']));
@@ -1615,7 +1608,7 @@
   void setUp() {
     processRequiredPlugins();
     projPath = convertPath('/my/proj');
-    resourceProvider.newFolder(projPath);
+    newFolder(projPath);
     // Create an SDK in the mock file system.
     MockSdk(resourceProvider: resourceProvider);
     var sdkManager = DartSdkManager(convertPath(sdkRoot));
@@ -1633,7 +1626,7 @@
   }
 
   Map<String, List<Folder>> _packageMap(String contextPath) {
-    var folder = resourceProvider.getFolder(contextPath);
+    var folder = getFolder(contextPath);
     var info = manager.getContextInfoFor(folder);
     return info.analysisDriver.sourceFactory?.packageMap;
   }
@@ -1821,7 +1814,7 @@
 
     // Remove the root, with the analysis options file.
     // No exceptions.
-    resourceProvider.deleteFolder(projPath);
+    deleteFolder(projPath);
     await pumpEventQueue();
   }
 
@@ -1867,8 +1860,7 @@
     await pumpEventQueue();
 
     // Confirm that one context was created.
-    var count = manager
-        .numberOfContextsInAnalysisRoot(resourceProvider.newFolder(projPath));
+    var count = manager.numberOfContextsInAnalysisRoot(newFolder(projPath));
     expect(count, equals(1));
 
     // Verify options.
@@ -1973,7 +1965,7 @@
   Future<void> test_non_analyzable_files_not_considered() async {
     // Set up project and get a reference to the driver.
     manager.setRoots(<String>[projPath], <String>[]);
-    var projectFolder = resourceProvider.newFolder(projPath);
+    var projectFolder = newFolder(projPath);
     var drivers = manager.getDriversInAnalysisRoot(projectFolder);
     expect(drivers, hasLength(1));
 
@@ -1999,14 +1991,14 @@
     // After a few other changes, the test now times out on my machine, so I'm
     // disabling it in order to prevent it from being flaky.
     _fail('Test times out');
-    var file = resourceProvider.newFile('$projPath/bin/test.dart', r'''
+    var file = newFile('$projPath/bin/test.dart', content: r'''
 main() {
   var paths = <int>[];
   var names = <String>[];
   paths.addAll(names.map((s) => s.length));
 }
 ''');
-    resourceProvider.newFile('$projPath/$optionsFileName', r'''
+    newFile('$projPath/$optionsFileName', content: r'''
 analyzer:
   strong-mode: false
 ''');
@@ -2025,7 +2017,7 @@
     expect(result.errors, isEmpty);
 
     // Update the options file - turn on 'strong-mode'.
-    resourceProvider.updateFile('$projPath/$optionsFileName', r'''
+    modifyFile('$projPath/$optionsFileName', r'''
 analyzer:
   strong-mode: true
 ''');
@@ -2067,7 +2059,7 @@
     manager.setRoots(<String>[projPath], <String>[]);
 
     // Verify that analysis options was parsed and the ignore patterns applied.
-    var projectFolder = resourceProvider.newFolder(projPath);
+    var projectFolder = newFolder(projPath);
     var drivers = manager.getDriversInAnalysisRoot(projectFolder);
     expect(drivers, hasLength(1));
     var driver = drivers[0];
@@ -2100,7 +2092,7 @@
     manager.setRoots(<String>[projPath], <String>[]);
     // Verify that the context in other_lib wasn't created and that the
     // context in lib was created.
-    var projectFolder = resourceProvider.newFolder(projPath);
+    var projectFolder = newFolder(projPath);
     var drivers = manager.getDriversInAnalysisRoot(projectFolder);
     expect(drivers, hasLength(2));
     expect(drivers[0].name, equals(convertPath('/my/proj')));
@@ -2132,7 +2124,7 @@
 
     // Verify that the context in other_lib wasn't created and that the
     // context in lib was created.
-    var projectFolder = resourceProvider.newFolder(projPath);
+    var projectFolder = newFolder(projPath);
     var drivers = manager.getDriversInAnalysisRoot(projectFolder);
     expect(drivers, hasLength(2));
     expect(drivers[0].name, equals(convertPath('/my/proj')));
@@ -2161,7 +2153,7 @@
     // Setup context / driver.
     manager.setRoots(<String>[projPath], <String>[]);
 
-    var projectFolder = resourceProvider.newFolder(projPath);
+    var projectFolder = newFolder(projPath);
     var drivers = manager.getDriversInAnalysisRoot(projectFolder);
     expect(drivers, hasLength(2));
     expect(drivers[0].name, equals(convertPath('/my/proj')));
@@ -2174,8 +2166,8 @@
     var example = convertPath('$project/example');
     var examplePubspec = convertPath('$example/pubspec.yaml');
     // create files
-    resourceProvider.newFile(projectPubspec, 'name: project');
-    resourceProvider.newFile(examplePubspec, 'name: example');
+    newFile(projectPubspec, content: 'name: project');
+    newFile(examplePubspec, content: 'name: example');
     newFile('$project/$optionsFileName', content: r'''
 analyzer:
   exclude:
@@ -2207,8 +2199,8 @@
     var aPubspec = convertPath('$a/pubspec.yaml');
     var cPubspec = convertPath('$c/pubspec.yaml');
     // create files
-    resourceProvider.newFile(aPubspec, 'name: aaa');
-    resourceProvider.newFile(cPubspec, 'name: ccc');
+    newFile(aPubspec, content: 'name: aaa');
+    newFile(cPubspec, content: 'name: ccc');
     newFile('$a/$optionsFileName', content: r'''
 analyzer:
   exclude:
diff --git a/pkg/analysis_server/test/edit/bulk_fixes_test.dart b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
index 0b79983..a845792 100644
--- a/pkg/analysis_server/test/edit/bulk_fixes_test.dart
+++ b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
@@ -2,6 +2,8 @@
 // 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.
 
+import 'dart:io';
+
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/edit/edit_domain.dart';
 import 'package:analyzer/src/generated/engine.dart';
@@ -153,6 +155,9 @@
     // The test case currently drops the 'new' but does not convert the code to
     // use a set literal. The code is no longer mangled, but we need to run the
     // BulkFixProcessor iteratively to solve the second case.
+    if (Platform.isWindows) {
+      fail('Should not be passing on Windows, but it does');
+    }
     addAnalysisOptionsFile('''
 linter:
   rules:
diff --git a/pkg/analysis_server/test/src/computer/highlights2_computer_test.dart b/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
similarity index 97%
rename from pkg/analysis_server/test/src/computer/highlights2_computer_test.dart
rename to pkg/analysis_server/test/src/computer/highlights_computer_test.dart
index 33aa117..d251985 100644
--- a/pkg/analysis_server/test/src/computer/highlights2_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
@@ -2,7 +2,7 @@
 // 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.
 
-import 'package:analysis_server/src/computer/computer_highlights2.dart';
+import 'package:analysis_server/src/computer/computer_highlights.dart';
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -103,7 +103,7 @@
       expect(result.errors, isEmpty);
     }
 
-    var computer = DartUnitHighlightsComputer2(result.unit);
+    var computer = DartUnitHighlightsComputer(result.unit);
     highlights = computer.compute();
   }
 }
diff --git a/pkg/analysis_server/test/src/computer/test_all.dart b/pkg/analysis_server/test/src/computer/test_all.dart
index b35ad77..1a04173 100644
--- a/pkg/analysis_server/test/src/computer/test_all.dart
+++ b/pkg/analysis_server/test/src/computer/test_all.dart
@@ -6,7 +6,7 @@
 
 import 'closing_labels_computer_test.dart' as closing_labels_computer;
 import 'folding_computer_test.dart' as folding_computer;
-import 'highlights2_computer_test.dart' as highlights2_computer;
+import 'highlights_computer_test.dart' as highlights_computer;
 import 'import_elements_computer_test.dart' as import_elements_computer;
 import 'imported_elements_computer_test.dart' as imported_elements_computer;
 import 'outline_computer_test.dart' as outline_computer;
@@ -15,7 +15,7 @@
   defineReflectiveSuite(() {
     closing_labels_computer.main();
     folding_computer.main();
-    highlights2_computer.main();
+    highlights_computer.main();
     import_elements_computer.main();
     imported_elements_computer.main();
     outline_computer.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
index ac2dccb..b4c29ff 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
@@ -348,6 +348,49 @@
     expect(parameter.index, 2);
   }
 
+  void test_addTypeParameter_fromPositionalArgument_variableInOuterScope() {
+    parse('''
+version: 1
+transforms:
+- title: 'Add'
+  date: 2020-09-03
+  element:
+    uris:
+      - 'test.dart'
+    class: 'A'
+  changes:
+    - kind: 'addTypeParameter'
+      index: 0
+      name: 'T'
+      extends:
+        expression: 'Object'
+      argumentValue:
+        expression: '{% t %}'
+  variables:
+    t:
+      kind: 'fragment'
+      value: 'arguments[2]'
+''');
+    var transforms = _transforms('A');
+    expect(transforms, hasLength(1));
+    var transform = transforms[0];
+    expect(transform.title, 'Add');
+    expect(transform.changes, hasLength(1));
+    var change = transform.changes[0] as AddTypeParameter;
+    expect(change.index, 0);
+    expect(change.name, 'T');
+
+    var extendsComponents = change.extendedType.components;
+    expect(extendsComponents, hasLength(1));
+    expect((extendsComponents[0] as TemplateText).text, 'Object');
+
+    var argumentComponents = change.argumentValue.components;
+    expect(argumentComponents, hasLength(1));
+    var value = _accessor(argumentComponents[0]) as ArgumentAccessor;
+    var parameter = value.parameter as PositionalParameterReference;
+    expect(parameter.index, 2);
+  }
+
   void test_bulkApply() {
     parse('''
 version: 1
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 3fdeadb..2e2dce1 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -10,6 +10,10 @@
 * The value of`FunctionType.element` for types created from a `typedef`
   is now `FunctionTypeAliasElement`, not its function element.
 * Removed deprecated `GenericTypeAliasElement`.
+* Removed `PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS`.
+* Changed the default `PhysicalResourceProvider` constructor to no longer take a
+  required positional parameter (removed the existing `fileReadMode` positional
+  parameter).
 
 ## 0.40.6
 * The non_nullable feature is released in 2.12 language version.
diff --git a/pkg/analyzer/lib/file_system/physical_file_system.dart b/pkg/analyzer/lib/file_system/physical_file_system.dart
index 0f0ee0d..9561ff6 100644
--- a/pkg/analyzer/lib/file_system/physical_file_system.dart
+++ b/pkg/analyzer/lib/file_system/physical_file_system.dart
@@ -55,22 +55,13 @@
 
 /// A `dart:io` based implementation of [ResourceProvider].
 class PhysicalResourceProvider implements ResourceProvider {
-  static final String Function(String) NORMALIZE_EOL_ALWAYS =
-      (String string) => string.replaceAll(RegExp('\r\n?'), '\n');
-
-  static final PhysicalResourceProvider INSTANCE =
-      PhysicalResourceProvider(null);
+  static final PhysicalResourceProvider INSTANCE = PhysicalResourceProvider();
 
   /// The path to the base folder where state is stored.
   final String _stateLocation;
 
-  PhysicalResourceProvider(String Function(String) fileReadMode,
-      {String stateLocation})
-      : _stateLocation = stateLocation ?? _getStandardStateLocation() {
-    if (fileReadMode != null) {
-      FileBasedSource.fileReadMode = fileReadMode;
-    }
-  }
+  PhysicalResourceProvider({String stateLocation})
+      : _stateLocation = stateLocation ?? _getStandardStateLocation();
 
   @override
   Context get pathContext => context;
@@ -188,7 +179,7 @@
   String readAsStringSync() {
     _throwIfWindowsDeviceDriver();
     try {
-      return FileBasedSource.fileReadMode(_file.readAsStringSync());
+      return _file.readAsStringSync();
     } on io.FileSystemException catch (exception) {
       throw FileSystemException(exception.path, exception.message);
     }
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 2a7a07a..0b776c8 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -1320,7 +1320,8 @@
   // ```
   //
   // If the instances of the class should be mutable, then remove the
-  //annotation, or choose a different superclass if the annotation is inherited:
+  // annotation, or choose a different superclass if the annotation is
+  // inherited:
   //
   // ```dart
   // class C {
diff --git a/pkg/analyzer/lib/src/generated/source_io.dart b/pkg/analyzer/lib/src/generated/source_io.dart
index 2188bba..fb337a8 100644
--- a/pkg/analyzer/lib/src/generated/source_io.dart
+++ b/pkg/analyzer/lib/src/generated/source_io.dart
@@ -13,9 +13,6 @@
 /// Instances of the class `FileBasedSource` implement a source that represents
 /// a file.
 class FileBasedSource extends Source {
-  /// A function that changes the way that files are read off of disk.
-  static Function fileReadMode = (String s) => s;
-
   /// Map from encoded URI/filepath pair to a unique integer identifier.  This
   /// identifier is used for equality tests and hash codes.
   ///
@@ -65,7 +62,7 @@
   /// See [contents].
   TimestampedData<String> get contentsFromFile {
     return TimestampedData<String>(
-        file.lastModified(), fileReadMode(file.readAsStringSync()));
+        file.lastModified(), file.readAsStringSync());
   }
 
   @override
diff --git a/pkg/analyzer/test/file_system/physical_file_system_test.dart b/pkg/analyzer/test/file_system/physical_file_system_test.dart
index 81e3c67..8f41e08 100644
--- a/pkg/analyzer/test/file_system/physical_file_system_test.dart
+++ b/pkg/analyzer/test/file_system/physical_file_system_test.dart
@@ -56,7 +56,7 @@
   /// Create the resource provider to be used by the tests. Subclasses can
   /// override this method to change the class of resource provider that is
   /// used.
-  PhysicalResourceProvider createProvider() => PhysicalResourceProvider(null);
+  PhysicalResourceProvider createProvider() => PhysicalResourceProvider();
 
   @override
   File getFile({@required bool exists, String content, String filePath}) {
diff --git a/pkg/analyzer/test/generated/all_the_rest_test.dart b/pkg/analyzer/test/generated/all_the_rest_test.dart
index 0706fb6..d6c1131 100644
--- a/pkg/analyzer/test/generated/all_the_rest_test.dart
+++ b/pkg/analyzer/test/generated/all_the_rest_test.dart
@@ -4,7 +4,6 @@
 
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:analyzer/src/dart/sdk/sdk.dart' hide SdkLibrariesReader;
 import 'package:analyzer/src/generated/java_engine_io.dart';
 import 'package:analyzer/src/generated/java_io.dart';
@@ -176,48 +175,6 @@
     expect(source1 == source2, isTrue);
   }
 
-  test_fileReadMode() async {
-    expect(FileBasedSource.fileReadMode('a'), 'a');
-    expect(FileBasedSource.fileReadMode('a\n'), 'a\n');
-    expect(FileBasedSource.fileReadMode('ab'), 'ab');
-    expect(FileBasedSource.fileReadMode('abc'), 'abc');
-    expect(FileBasedSource.fileReadMode('a\nb'), 'a\nb');
-    expect(FileBasedSource.fileReadMode('a\rb'), 'a\rb');
-    expect(FileBasedSource.fileReadMode('a\r\nb'), 'a\r\nb');
-  }
-
-  test_fileReadMode_changed() async {
-    FileBasedSource.fileReadMode = (String s) => s + 'xyz';
-    expect(FileBasedSource.fileReadMode('a'), 'axyz');
-    expect(FileBasedSource.fileReadMode('a\n'), 'a\nxyz');
-    expect(FileBasedSource.fileReadMode('ab'), 'abxyz');
-    expect(FileBasedSource.fileReadMode('abc'), 'abcxyz');
-    FileBasedSource.fileReadMode = (String s) => s;
-  }
-
-  test_fileReadMode_normalize_eol_always() async {
-    FileBasedSource.fileReadMode =
-        PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS;
-    expect(FileBasedSource.fileReadMode('a'), 'a');
-
-    // '\n' -> '\n' as first, last and only character
-    expect(FileBasedSource.fileReadMode('\n'), '\n');
-    expect(FileBasedSource.fileReadMode('a\n'), 'a\n');
-    expect(FileBasedSource.fileReadMode('\na'), '\na');
-
-    // '\r\n' -> '\n' as first, last and only character
-    expect(FileBasedSource.fileReadMode('\r\n'), '\n');
-    expect(FileBasedSource.fileReadMode('a\r\n'), 'a\n');
-    expect(FileBasedSource.fileReadMode('\r\na'), '\na');
-
-    // '\r' -> '\n' as first, last and only character
-    expect(FileBasedSource.fileReadMode('\r'), '\n');
-    expect(FileBasedSource.fileReadMode('a\r'), 'a\n');
-    expect(FileBasedSource.fileReadMode('\ra'), '\na');
-
-    FileBasedSource.fileReadMode = (String s) => s;
-  }
-
   test_getFullName() async {
     String fullPath = "/does/not/exist.dart";
     JavaFile file = FileUtilities2.createFile(fullPath);
diff --git a/pkg/analyzer/test/src/source/source_resource_test.dart b/pkg/analyzer/test/src/source/source_resource_test.dart
index 29cbc93..5e6f3ec 100644
--- a/pkg/analyzer/test/src/source/source_resource_test.dart
+++ b/pkg/analyzer/test/src/source/source_resource_test.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:analyzer/src/generated/java_engine_io.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -63,28 +62,6 @@
     FileSource.fileReadMode = (String s) => s;
   }
 
-  void test_fileReadMode_normalize_eol_always() {
-    FileSource.fileReadMode = PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS;
-    expect(FileSource.fileReadMode('a'), 'a');
-
-    // '\n' -> '\n' as first, last and only character
-    expect(FileSource.fileReadMode('\n'), '\n');
-    expect(FileSource.fileReadMode('a\n'), 'a\n');
-    expect(FileSource.fileReadMode('\na'), '\na');
-
-    // '\r\n' -> '\n' as first, last and only character
-    expect(FileSource.fileReadMode('\r\n'), '\n');
-    expect(FileSource.fileReadMode('a\r\n'), 'a\n');
-    expect(FileSource.fileReadMode('\r\na'), '\na');
-
-    // '\r' -> '\n' as first, last and only character
-    expect(FileSource.fileReadMode('\r'), '\n');
-    expect(FileSource.fileReadMode('a\r'), 'a\n');
-    expect(FileSource.fileReadMode('\ra'), '\na');
-
-    FileSource.fileReadMode = (String s) => s;
-  }
-
   void test_getFullName() {
     File file = getFile("/does/not/exist.dart");
     FileSource source = FileSource(file);
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 5b680ce..f9d33c5 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -5645,6 +5645,8 @@
 {% endprettify %}
 
 If the instances of the class should be mutable, then remove the
+annotation, or choose a different superclass if the annotation is
+inherited:
 
 {% prettify dart tag=pre+code %}
 class C {
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index a1dd1f5..6cb97b4 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -142,7 +142,7 @@
     abstractValueStrategy = options.useTrivialAbstractValueDomain
         ? const TrivialAbstractValueStrategy()
         : const TypeMaskStrategy();
-    if (options.experimentalWrapped) {
+    if (options.experimentalWrapped || options.testMode) {
       abstractValueStrategy =
           WrappedAbstractValueStrategy(abstractValueStrategy);
     } else if (options.experimentalPowersets) {
diff --git a/pkg/compiler/lib/src/js_backend/annotations.dart b/pkg/compiler/lib/src/js_backend/annotations.dart
index f80bbbf..8955a8d 100644
--- a/pkg/compiler/lib/src/js_backend/annotations.dart
+++ b/pkg/compiler/lib/src/js_backend/annotations.dart
@@ -102,6 +102,14 @@
       14, 'downcast:check',
       forFunctionsOnly: false, internalOnly: false);
 
+  static const PragmaAnnotation indexBoundsTrust = const PragmaAnnotation(
+      15, 'index-bounds:trust',
+      forFunctionsOnly: false, internalOnly: false);
+
+  static const PragmaAnnotation indexBoundsCheck = const PragmaAnnotation(
+      16, 'index-bounds:check',
+      forFunctionsOnly: false, internalOnly: false);
+
   static const List<PragmaAnnotation> values = [
     noInline,
     tryInline,
@@ -118,6 +126,8 @@
     parameterCheck,
     downcastTrust,
     downcastCheck,
+    indexBoundsTrust,
+    indexBoundsCheck,
   ];
 
   static const Map<PragmaAnnotation, Set<PragmaAnnotation>> implies = {
@@ -319,6 +329,12 @@
   ///
   /// If [member] is `null`, the default policy is returned.
   CheckPolicy getExplicitCastCheckPolicy(MemberEntity member);
+
+  /// What should the compiler do with index bounds checks `[]`, `[]=` and
+  /// `removeLast()` operations in the body of [member].
+  ///
+  /// If [member] is `null`, the default policy is returned.
+  CheckPolicy getIndexBoundsCheckPolicy(MemberEntity member);
 }
 
 class AnnotationsDataImpl implements AnnotationsData {
@@ -330,6 +346,7 @@
   final CheckPolicy _defaultImplicitDowncastCheckPolicy;
   final CheckPolicy _defaultConditionCheckPolicy;
   final CheckPolicy _defaultExplicitCastCheckPolicy;
+  final CheckPolicy _defaultIndexBoundsCheckPolicy;
   final Map<MemberEntity, EnumSet<PragmaAnnotation>> pragmaAnnotations;
 
   AnnotationsDataImpl(CompilerOptions options, this.pragmaAnnotations)
@@ -338,7 +355,9 @@
             options.defaultImplicitDowncastCheckPolicy,
         this._defaultConditionCheckPolicy = options.defaultConditionCheckPolicy,
         this._defaultExplicitCastCheckPolicy =
-            options.defaultExplicitCastCheckPolicy;
+            options.defaultExplicitCastCheckPolicy,
+        this._defaultIndexBoundsCheckPolicy =
+            options.defaultIndexBoundsCheckPolicy;
 
   factory AnnotationsDataImpl.readFromDataSource(
       CompilerOptions options, DataSource source) {
@@ -504,6 +523,21 @@
     }
     return _defaultExplicitCastCheckPolicy;
   }
+
+  @override
+  CheckPolicy getIndexBoundsCheckPolicy(MemberEntity member) {
+    if (member != null) {
+      EnumSet<PragmaAnnotation> annotations = pragmaAnnotations[member];
+      if (annotations != null) {
+        if (annotations.contains(PragmaAnnotation.indexBoundsTrust)) {
+          return CheckPolicy.trusted;
+        } else if (annotations.contains(PragmaAnnotation.indexBoundsCheck)) {
+          return CheckPolicy.checked;
+        }
+      }
+    }
+    return _defaultIndexBoundsCheckPolicy;
+  }
 }
 
 class AnnotationsDataBuilder {
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 378c5b4..47b0985 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -319,6 +319,11 @@
   /// This is an internal configuration option derived from other flags.
   CheckPolicy defaultExplicitCastCheckPolicy;
 
+  /// What should the compiler do with List index bounds checks.
+  ///
+  /// This is an internal configuration option derived from other flags.
+  CheckPolicy defaultIndexBoundsCheckPolicy;
+
   /// Whether to generate code compliant with content security policy (CSP).
   bool useContentSecurityPolicy = false;
 
@@ -610,6 +615,11 @@
     } else {
       defaultExplicitCastCheckPolicy = CheckPolicy.checked;
     }
+    if (trustPrimitives) {
+      defaultIndexBoundsCheckPolicy = CheckPolicy.trusted;
+    } else {
+      defaultIndexBoundsCheckPolicy = CheckPolicy.checked;
+    }
 
     if (_disableMinification) {
       enableMinification = false;
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 1032865..e1c0789 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1981,6 +1981,8 @@
           _sourceInformationBuilder.buildForInCurrent(node);
       HInstruction index = localsHandler.readLocal(indexVariable,
           sourceInformation: sourceInformation);
+      // No bound check is necessary on indexer as it is immediately guarded by
+      // the condition.
       HInstruction value = new HIndex(array, index, type)
         ..sourceInformation = sourceInformation;
       add(value);
@@ -4983,6 +4985,7 @@
           typeArguments, sourceInformation,
           isIntercepted: isIntercepted);
     }
+    invoke.instructionContext = _currentFrame.member;
     if (node is ir.MethodInvocation) {
       invoke.isInvariant = node.isInvariant;
       invoke.isBoundsSafe = node.isBoundsSafe;
diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
index 92745f6..97bb616 100644
--- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
+++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
@@ -84,6 +84,7 @@
         int argumentCount = selector.argumentCount;
         if (argumentCount == 0) {
           if (name == 'abs') return const AbsSpecializer();
+          if (name == 'removeLast') return const RemoveLastSpecializer();
           if (name == 'round') return const RoundSpecializer();
           if (name == 'toInt') return const ToIntSpecializer();
           if (name == 'trim') return const TrimSpecializer();
@@ -107,6 +108,27 @@
     }
     return const InvokeDynamicSpecializer();
   }
+
+  HBoundsCheck insertBoundsCheck(HInstruction indexerNode, HInstruction array,
+      HInstruction indexArgument, JClosedWorld closedWorld) {
+    final abstractValueDomain = closedWorld.abstractValueDomain;
+    HGetLength length = HGetLength(array, abstractValueDomain.positiveIntType,
+        isAssignable: abstractValueDomain
+            .isFixedLengthJsIndexable(array.instructionType)
+            .isPotentiallyFalse);
+    indexerNode.block.addBefore(indexerNode, length);
+
+    AbstractValue type =
+        indexArgument.isPositiveInteger(abstractValueDomain).isDefinitelyTrue
+            ? indexArgument.instructionType
+            : abstractValueDomain.positiveIntType;
+    HBoundsCheck check = HBoundsCheck(indexArgument, length, array, type)
+      ..sourceInformation = indexerNode.sourceInformation;
+    indexerNode.block.addBefore(indexerNode, check);
+    // TODO(sra): This should be useful but causes some crashes. Figure out why:
+    //     indexArgument.replaceAllUsersDominatedBy(indexerNode, check);
+    return check;
+  }
 }
 
 bool canBeNegativeZero(HInstruction input) {
@@ -178,8 +200,16 @@
         return null;
       }
     }
-    HIndexAssign converted = new HIndexAssign(
-        closedWorld.abstractValueDomain, receiver, index, value);
+
+    HInstruction checkedIndex = index;
+    if (closedWorld.annotationsData
+        .getIndexBoundsCheckPolicy(instruction.instructionContext)
+        .isEmitted) {
+      checkedIndex =
+          insertBoundsCheck(instruction, receiver, index, closedWorld);
+    }
+    HIndexAssign converted = HIndexAssign(
+        closedWorld.abstractValueDomain, receiver, checkedIndex, value);
     log?.registerIndexAssign(instruction, converted);
     return converted;
   }
@@ -231,36 +261,86 @@
       JCommonElements commonElements,
       JClosedWorld closedWorld,
       OptimizationTestLog log) {
+    HInstruction receiver = instruction.getDartReceiver(closedWorld);
     var abstractValueDomain = closedWorld.abstractValueDomain;
-    if (instruction.inputs[1]
-        .isIndexablePrimitive(abstractValueDomain)
-        .isPotentiallyFalse) {
+    if (receiver.isIndexablePrimitive(abstractValueDomain).isPotentiallyFalse) {
       return null;
     }
-    if (instruction.inputs[2]
-            .isInteger(abstractValueDomain)
-            .isPotentiallyFalse &&
+    HInstruction index = instruction.inputs.last;
+    if (index.isInteger(abstractValueDomain).isPotentiallyFalse &&
         // TODO(johnniwinther): Support annotations on the possible targets
         // and used their parameter check policy here.
         closedWorld.annotationsData.getParameterCheckPolicy(null).isEmitted) {
       // We want the right checked mode error.
       return null;
     }
-    AbstractValue receiverType =
-        instruction.getDartReceiver(closedWorld).instructionType;
+    AbstractValue receiverType = receiver.instructionType;
     AbstractValue elementType =
         AbstractValueFactory.inferredResultTypeForSelector(
             instruction.selector, receiverType, results);
     if (abstractValueDomain.isTypedArray(receiverType).isDefinitelyTrue) {
       elementType = abstractValueDomain.excludeNull(elementType);
     }
-    HIndex converted =
-        new HIndex(instruction.inputs[1], instruction.inputs[2], elementType);
+
+    HInstruction checkedIndex = index;
+    if (closedWorld.annotationsData
+        .getIndexBoundsCheckPolicy(instruction.instructionContext)
+        .isEmitted) {
+      checkedIndex =
+          insertBoundsCheck(instruction, receiver, index, closedWorld);
+    }
+    HIndex converted = HIndex(receiver, checkedIndex, elementType);
     log?.registerIndex(instruction, converted);
     return converted;
   }
 }
 
+class RemoveLastSpecializer extends InvokeDynamicSpecializer {
+  const RemoveLastSpecializer();
+
+  @override
+  HInstruction tryConvertToBuiltin(
+      HInvokeDynamic instruction,
+      HGraph graph,
+      GlobalTypeInferenceResults results,
+      JCommonElements commonElements,
+      JClosedWorld closedWorld,
+      OptimizationTestLog log) {
+    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    final abstractValueDomain = closedWorld.abstractValueDomain;
+    if (receiver.isExtendableArray(abstractValueDomain).isPotentiallyFalse) {
+      return null;
+    }
+
+    // We are essentially inlining `result = a[a.length - 1]`. `0` is the only
+    // index that can fail so we check zero directly, but we want to report the
+    // error index as `-1`, so we add `-1` as an extra input that to the check.
+    if (closedWorld.annotationsData
+        .getIndexBoundsCheckPolicy(instruction.instructionContext)
+        .isEmitted) {
+      HConstant zeroIndex = graph.addConstantInt(0, closedWorld);
+      HBoundsCheck check =
+          insertBoundsCheck(instruction, receiver, zeroIndex, closedWorld);
+      HInstruction minusOne = graph.addConstantInt(-1, closedWorld);
+      check.inputs.add(minusOne);
+      minusOne.usedBy.add(check);
+    }
+    // `Array.pop` is encoded as a non-intercepted call to `JSArray.removeLast`.
+    // TODO(sra): Add a better encoding for `Array.pop`, perhaps a HInstruction.
+    HInvokeDynamic converted = HInvokeDynamicMethod(
+        instruction.selector,
+        instruction.receiverType,
+        [receiver], // Drop interceptor.
+        instruction.instructionType,
+        instruction.typeArguments,
+        instruction.sourceInformation,
+        isIntercepted: false)
+      ..element = commonElements.jsArrayRemoveLast;
+    log?.registerRemoveLast(instruction, converted);
+    return converted;
+  }
+}
+
 class BitNotSpecializer extends InvokeDynamicSpecializer {
   const BitNotSpecializer();
 
diff --git a/pkg/compiler/lib/src/ssa/logging.dart b/pkg/compiler/lib/src/ssa/logging.dart
index e857648..d7c7e54 100644
--- a/pkg/compiler/lib/src/ssa/logging.dart
+++ b/pkg/compiler/lib/src/ssa/logging.dart
@@ -114,6 +114,10 @@
     _registerSpecializer(original, converted, 'Index');
   }
 
+  void registerRemoveLast(HInvokeDynamic original, HInvokeDynamic converted) {
+    _registerSpecializer(original, converted, 'RemoveLast');
+  }
+
   void registerBitNot(HInvokeDynamic original, HBitNot converted) {
     _registerSpecializer(original, converted, 'BitNot');
   }
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 77c24d3..115e891 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1389,6 +1389,15 @@
   String toString() => '${this.runtimeType}()';
 }
 
+/// An interface implemented by certain kinds of [HInstruction]. This makes it
+/// possible to discover which annotations were in force in the code from which
+/// the instruction originated.
+// TODO(sra): It would be easier to use a mostly-shared Map-like structure that
+// surfaces the ambient annotations at any point in the code.
+abstract class InstructionContext {
+  MemberEntity instructionContext;
+}
+
 /// The set of uses of [source] that are dominated by [dominator].
 class DominatedUses {
   final HInstruction _source;
@@ -1702,7 +1711,7 @@
   }
 }
 
-abstract class HInvokeDynamic extends HInvoke {
+abstract class HInvokeDynamic extends HInvoke implements InstructionContext {
   final InvokeDynamicSpecializer specializer;
 
   Selector _selector;
@@ -1728,6 +1737,9 @@
   // stub.
   MemberEntity element;
 
+  @override
+  MemberEntity instructionContext;
+
   HInvokeDynamic(Selector selector, this._receiverType, this.element,
       List<HInstruction> inputs, bool isIntercepted, AbstractValue resultType)
       : this._selector = selector,
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index ad03cf6..5ea6c69 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -72,8 +72,6 @@
       assert(graph.isValid(), 'Graph not valid after ${phase.name}');
     }
 
-    bool trustPrimitives = _options.trustPrimitives;
-    Set<HInstruction> boundsChecked = new Set<HInstruction>();
     SsaCodeMotion codeMotion;
     SsaLoadElimination loadElimination;
 
@@ -114,7 +112,6 @@
             typeRecipeDomain,
             registry,
             log),
-        new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
         new SsaInstructionSimplifier(
             globalInferenceResults,
             _options,
@@ -123,7 +120,6 @@
             typeRecipeDomain,
             registry,
             log),
-        new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
         new SsaTypePropagator(globalInferenceResults,
             closedWorld.commonElements, closedWorld, log),
         // Run a dead code eliminator before LICM because dead
@@ -155,7 +151,6 @@
             typeRecipeDomain,
             registry,
             log),
-        new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
       ];
       phases.forEach(runPhase);
 
@@ -184,7 +179,6 @@
               typeRecipeDomain,
               registry,
               log),
-          new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
           new SsaSimplifyInterceptors(closedWorld, member.enclosingClass),
           new SsaDeadCodeEliminator(closedWorld, this),
         ];
@@ -676,9 +670,7 @@
     if (selector.isCall || selector.isOperator) {
       FunctionEntity target;
       if (input.isExtendableArray(_abstractValueDomain).isDefinitelyTrue) {
-        if (applies(commonElements.jsArrayRemoveLast)) {
-          target = commonElements.jsArrayRemoveLast;
-        } else if (applies(commonElements.jsArrayAdd)) {
+        if (applies(commonElements.jsArrayAdd)) {
           // Codegen special cases array calls to `Array.push`, but does not
           // inline argument type checks. We lower if the check always passes
           // (due to invariance or being a top-type), or if the check is not
@@ -718,6 +710,7 @@
         // bounds check on removeLast). Once we start inlining, the
         // bounds check will become explicit, so we won't need this
         // optimization.
+        // TODO(sra): Fix comment - SsaCheckInserter is deleted.
         HInvokeDynamicMethod result = new HInvokeDynamicMethod(
             node.selector,
             node.receiverType,
@@ -2288,99 +2281,6 @@
   }
 }
 
-class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase {
-  final Set<HInstruction> boundsChecked;
-  final bool trustPrimitives;
-  final JClosedWorld closedWorld;
-  @override
-  final String name = "SsaCheckInserter";
-  HGraph graph;
-
-  SsaCheckInserter(this.trustPrimitives, this.closedWorld, this.boundsChecked);
-
-  AbstractValueDomain get _abstractValueDomain =>
-      closedWorld.abstractValueDomain;
-
-  @override
-  void visitGraph(HGraph graph) {
-    this.graph = graph;
-
-    // In --trust-primitives mode we don't add bounds checks.  This is better
-    // than trying to remove them later as the limit expression would become
-    // dead and require DCE.
-    if (trustPrimitives) return;
-
-    visitDominatorTree(graph);
-  }
-
-  @override
-  void visitBasicBlock(HBasicBlock block) {
-    HInstruction instruction = block.first;
-    while (instruction != null) {
-      HInstruction next = instruction.next;
-      instruction = instruction.accept(this);
-      instruction = next;
-    }
-  }
-
-  HBoundsCheck insertBoundsCheck(
-      HInstruction indexNode, HInstruction array, HInstruction indexArgument) {
-    HGetLength length = new HGetLength(
-        array, closedWorld.abstractValueDomain.positiveIntType,
-        isAssignable: !isFixedLength(array.instructionType, closedWorld));
-    indexNode.block.addBefore(indexNode, length);
-
-    AbstractValue type =
-        indexArgument.isPositiveInteger(_abstractValueDomain).isDefinitelyTrue
-            ? indexArgument.instructionType
-            : closedWorld.abstractValueDomain.positiveIntType;
-    HBoundsCheck check = new HBoundsCheck(indexArgument, length, array, type)
-      ..sourceInformation = indexNode.sourceInformation;
-    indexNode.block.addBefore(indexNode, check);
-    // If the index input to the bounds check was not known to be an integer
-    // then we replace its uses with the bounds check, which is known to be an
-    // integer.  However, if the input was already an integer we don't do this
-    // because putting in a check instruction might obscure the real nature of
-    // the index eg. if it is a constant.  The range information from the
-    // BoundsCheck instruction is attached to the input directly by
-    // visitBoundsCheck in the SsaValueRangeAnalyzer.
-    if (indexArgument.isInteger(_abstractValueDomain).isPotentiallyFalse) {
-      indexArgument.replaceAllUsersDominatedBy(indexNode, check);
-    }
-    boundsChecked.add(indexNode);
-    return check;
-  }
-
-  @override
-  void visitIndex(HIndex node) {
-    if (boundsChecked.contains(node)) return;
-    HInstruction index = node.index;
-    index = insertBoundsCheck(node, node.receiver, index);
-  }
-
-  @override
-  void visitIndexAssign(HIndexAssign node) {
-    if (boundsChecked.contains(node)) return;
-    HInstruction index = node.index;
-    index = insertBoundsCheck(node, node.receiver, index);
-  }
-
-  @override
-  void visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
-    MemberEntity element = node.element;
-    if (node.isInterceptedCall) return;
-    if (element != closedWorld.commonElements.jsArrayRemoveLast) return;
-    if (boundsChecked.contains(node)) return;
-    // `0` is the index we want to check, but we want to report `-1`, as if we
-    // executed `a[a.length-1]`
-    HBoundsCheck check = insertBoundsCheck(
-        node, node.receiver, graph.addConstantInt(0, closedWorld));
-    HInstruction minusOne = graph.addConstantInt(-1, closedWorld);
-    check.inputs.add(minusOne);
-    minusOne.usedBy.add(check);
-  }
-}
-
 class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
   @override
   final String name = "SsaDeadCodeEliminator";
@@ -3631,14 +3531,14 @@
   @override
   void visitIndex(HIndex instruction) {
     HInstruction receiver = instruction.receiver.nonCheck();
-    HInstruction existing =
-        memorySet.lookupKeyedValue(receiver, instruction.index);
+    HInstruction index = instruction.index.nonCheck();
+    HInstruction existing = memorySet.lookupKeyedValue(receiver, index);
     if (existing != null) {
       checkNewGvnCandidates(instruction, existing);
       instruction.block.rewriteWithBetterUser(instruction, existing);
       instruction.block.remove(instruction);
     } else {
-      memorySet.registerKeyedValue(receiver, instruction.index, instruction);
+      memorySet.registerKeyedValue(receiver, index, instruction);
     }
   }
 
@@ -3646,7 +3546,7 @@
   void visitIndexAssign(HIndexAssign instruction) {
     HInstruction receiver = instruction.receiver.nonCheck();
     memorySet.registerKeyedValueUpdate(
-        receiver, instruction.index, instruction.value);
+        receiver, instruction.index.nonCheck(), instruction.value);
   }
 
   // Pure operations that do not escape their inputs.
diff --git a/pkg/compiler/test/annotations/data/directives.dart b/pkg/compiler/test/annotations/data/directives.dart
index fadf7d8..5a11424 100644
--- a/pkg/compiler/test/annotations/data/directives.dart
+++ b/pkg/compiler/test/annotations/data/directives.dart
@@ -13,6 +13,8 @@
   downcastCheck();
   parameterTrust();
   parameterCheck();
+  indexBoundsTrust();
+  indexBoundsCheck();
 }
 
 /*member: typesTrust:types:trust*/
@@ -46,3 +48,11 @@
 /*member: parameterCheck:parameter:check*/
 @pragma('dart2js:parameter:check')
 parameterCheck() {}
+
+/*member: indexBoundsTrust:index-bounds:trust*/
+@pragma('dart2js:index-bounds:trust')
+indexBoundsTrust() {}
+
+/*member: indexBoundsCheck:index-bounds:check*/
+@pragma('dart2js:index-bounds:check')
+indexBoundsCheck() {}
diff --git a/pkg/compiler/test/codegen/expect_annotations_test.dart b/pkg/compiler/test/codegen/expect_annotations_test.dart
index 5fc56d7..9ed3517 100644
--- a/pkg/compiler/test/codegen/expect_annotations_test.dart
+++ b/pkg/compiler/test/codegen/expect_annotations_test.dart
@@ -10,7 +10,6 @@
 import 'package:compiler/src/commandline_options.dart';
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/inferrer/abstract_value_domain.dart';
-import 'package:compiler/src/inferrer/typemasks/masks.dart';
 import 'package:compiler/src/inferrer/types.dart';
 import 'package:compiler/src/world.dart' show JClosedWorld;
 import '../inference/type_mask_test_helper.dart';
@@ -50,16 +49,19 @@
   AbstractValueDomain commonMasks = closedWorld.abstractValueDomain;
   Expect.isFalse(compiler.compilationFailed, 'Unsuccessful compilation');
 
-  void testTypeMatch(FunctionEntity function, TypeMask expectedParameterType,
-      TypeMask expectedReturnType, GlobalTypeInferenceResults results) {
+  void testTypeMatch(
+      FunctionEntity function,
+      AbstractValue expectedParameterType,
+      AbstractValue expectedReturnType,
+      GlobalTypeInferenceResults results) {
     closedWorld.elementEnvironment.forEachParameterAsLocal(
         closedWorld.globalLocalsMap, function, (Local parameter) {
-      TypeMask type = results.resultOfParameter(parameter);
+      AbstractValue type = results.resultOfParameter(parameter);
       Expect.equals(
           expectedParameterType, simplify(type, commonMasks), "$parameter");
     });
     if (expectedReturnType != null) {
-      TypeMask type = results.resultOfMember(function).returnType;
+      AbstractValue type = results.resultOfMember(function).returnType;
       Expect.equals(
           expectedReturnType, simplify(type, commonMasks), "$function");
     }
@@ -67,8 +69,8 @@
 
   void test(String name,
       {bool expectNoInline: false,
-      TypeMask expectedParameterType: null,
-      TypeMask expectedReturnType: null,
+      AbstractValue expectedParameterType: null,
+      AbstractValue expectedReturnType: null,
       bool expectAssumeDynamic: false}) {
     LibraryEntity mainApp = closedWorld.elementEnvironment.mainLibrary;
     FunctionEntity method =
diff --git a/pkg/compiler/test/inference/map_tracer_test.dart b/pkg/compiler/test/inference/map_tracer_test.dart
index 4cb4c1e..5e0ebf4 100644
--- a/pkg/compiler/test/inference/map_tracer_test.dart
+++ b/pkg/compiler/test/inference/map_tracer_test.dart
@@ -258,7 +258,7 @@
 
   K(TypeMask other) => simplify(keyType.union(other, commonMasks), commonMasks);
   V(TypeMask other) =>
-      simplify(valueType.union(other, commonMasks), commonMasks).nullable();
+      simplify(valueType.union(other, commonMasks).nullable(), commonMasks);
 
   checkType('mapInField', K(aKeyType), V(commonMasks.numType));
   checkType('mapPassedToMethod', K(aKeyType), V(commonMasks.numType));
diff --git a/pkg/compiler/test/inference/type_mask_test_helper.dart b/pkg/compiler/test/inference/type_mask_test_helper.dart
index 9e5d665..ba29b08 100644
--- a/pkg/compiler/test/inference/type_mask_test_helper.dart
+++ b/pkg/compiler/test/inference/type_mask_test_helper.dart
@@ -6,19 +6,19 @@
 
 library type_mask_test_helper;
 
+import 'package:compiler/src/inferrer/abstract_value_domain.dart';
 import 'package:compiler/src/inferrer/typemasks/masks.dart';
 import 'package:compiler/src/world.dart' show JClosedWorld;
 
 export 'package:compiler/src/inferrer/types.dart';
 
-TypeMask simplify(TypeMask mask, CommonMasks commonMasks) {
-  if (mask is ForwardingTypeMask) {
-    return simplify(mask.forwardTo, commonMasks);
-  } else if (mask is UnionTypeMask) {
-    return UnionTypeMask.flatten(
-        mask.disjointMasks, mask.isNullable, commonMasks);
+AbstractValue simplify(AbstractValue value, AbstractValueDomain domain) {
+  if (value is ForwardingTypeMask) {
+    return simplify(value.forwardTo, domain);
+  } else if (value is UnionTypeMask) {
+    return UnionTypeMask.flatten(value.disjointMasks, value.isNullable, domain);
   } else {
-    return mask;
+    return value;
   }
 }
 
diff --git a/pkg/compiler/test/optimization/data/remove_last.dart b/pkg/compiler/test/optimization/data/remove_last.dart
new file mode 100644
index 0000000..a4f073f
--- /dev/null
+++ b/pkg/compiler/test/optimization/data/remove_last.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2020, 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.
+
+// @dart = 2.7
+
+import 'dart:collection';
+import 'dart:typed_data';
+
+/*member: dynamicIndex:Specializer=[!RemoveLast]*/
+@pragma('dart2js:noInline')
+dynamicIndex(var list) {
+  return list.removeLast(); // This is not known to be an indexable primitive.
+}
+
+/*member: unknownList:Specializer=[!RemoveLast]*/
+@pragma('dart2js:noInline')
+unknownList(List list) {
+  return list.removeLast(); // This is not known to be an indexable primitive.
+}
+
+/*member: possiblyNullMutableList:Specializer=[RemoveLast]*/
+@pragma('dart2js:noInline')
+possiblyNullMutableList(bool b) {
+  var list = b ? [0] : null;
+  return list.removeLast();
+}
+
+/*member: mutableList:Specializer=[RemoveLast]*/
+@pragma('dart2js:noInline')
+mutableList() {
+  var list = [0];
+  return list.removeLast();
+}
+
+/*member: typedList:Specializer=[!RemoveLast]*/
+@pragma('dart2js:noInline')
+typedList() {
+  var list = Uint8List(10);
+  return list.removeLast();
+}
+
+main() {
+  dynamicIndex([]);
+  dynamicIndex({});
+  unknownList([]);
+  unknownList(new MyList());
+  possiblyNullMutableList(true);
+  possiblyNullMutableList(false);
+  mutableList();
+  typedList();
+}
+
+class MyList<E> extends ListBase<E> {
+  E operator [](int index) => null;
+  void operator []=(int index, E value) {}
+  int get length => 0;
+  void set length(int value) {}
+}
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 3ded01d..9bf97f5 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -496,7 +496,7 @@
 ///
 /// This class may be used directly by clients that with to run migration but
 /// provide their own command-line interface.
-class MigrationCliRunner {
+class MigrationCliRunner implements DartFixListenerClient {
   final MigrationCli cli;
 
   /// Logger instance we use to give feedback to the user.
@@ -613,6 +613,55 @@
         sdkPath: sdkPath);
   }
 
+  @override
+  void onException(String detail) {
+    if (_hasExceptions) return;
+    _hasExceptions = true;
+    if (options.ignoreExceptions) {
+      logger.stdout('''
+Exception(s) occurred during migration.  Attempting to perform
+migration anyway due to the use of --${CommandLineOptions.ignoreExceptionsFlag}.
+
+To see exception details, re-run without --${CommandLineOptions.ignoreExceptionsFlag}.
+''');
+    } else {
+      if (_hasAnalysisErrors) {
+        logger.stderr('''
+Aborting migration due to an exception.  This may be due to a bug in
+the migration tool, or it may be due to errors in the source code
+being migrated.  If possible, try to fix errors in the source code and
+re-try migrating.  If that doesn't work, consider filing a bug report
+at:
+''');
+      } else {
+        logger.stderr('''
+Aborting migration due to an exception.  This most likely is due to a
+bug in the migration tool.  Please consider filing a bug report at:
+''');
+      }
+      logger.stderr('https://github.com/dart-lang/sdk/issues/new');
+      logger.stderr('''
+To attempt to perform migration anyway, you may re-run with
+--${CommandLineOptions.ignoreExceptionsFlag}.
+
+Exception details:
+''');
+      logger.stderr(detail);
+      throw MigrationExit(1);
+    }
+  }
+
+  @override
+  void onFatalError(String detail) {
+    logger.stderr(detail);
+    throw MigrationExit(1);
+  }
+
+  @override
+  void onMessage(String detail) {
+    logger.stdout(detail);
+  }
+
   /// Runs the full migration process.
   ///
   /// If something goes wrong, a message is printed using the logger configured
@@ -636,10 +685,8 @@
 
     logger.stdout(ansi.emphasized('Analyzing project...'));
     _fixCodeProcessor = _FixCodeProcessor(context, this);
-    _dartFixListener = DartFixListener(
-        DriverProviderImpl(resourceProvider, context),
-        _exceptionReported,
-        _fatalErrorReported);
+    _dartFixListener =
+        DartFixListener(DriverProviderImpl(resourceProvider, context), this);
     nonNullableFix = createNonNullableFix(_dartFixListener, resourceProvider,
         _fixCodeProcessor.getLineInfo, computeBindAddress(),
         included: [options.directory],
@@ -807,48 +854,6 @@
     }
   }
 
-  void _exceptionReported(String detail) {
-    if (_hasExceptions) return;
-    _hasExceptions = true;
-    if (options.ignoreExceptions) {
-      logger.stdout('''
-Exception(s) occurred during migration.  Attempting to perform
-migration anyway due to the use of --${CommandLineOptions.ignoreExceptionsFlag}.
-
-To see exception details, re-run without --${CommandLineOptions.ignoreExceptionsFlag}.
-''');
-    } else {
-      if (_hasAnalysisErrors) {
-        logger.stderr('''
-Aborting migration due to an exception.  This may be due to a bug in
-the migration tool, or it may be due to errors in the source code
-being migrated.  If possible, try to fix errors in the source code and
-re-try migrating.  If that doesn't work, consider filing a bug report
-at:
-''');
-      } else {
-        logger.stderr('''
-Aborting migration due to an exception.  This most likely is due to a
-bug in the migration tool.  Please consider filing a bug report at:
-''');
-      }
-      logger.stderr('https://github.com/dart-lang/sdk/issues/new');
-      logger.stderr('''
-To attempt to perform migration anyway, you may re-run with
---${CommandLineOptions.ignoreExceptionsFlag}.
-
-Exception details:
-''');
-      logger.stderr(detail);
-      throw MigrationExit(1);
-    }
-  }
-
-  void _fatalErrorReported(String detail) {
-    logger.stderr(detail);
-    throw MigrationExit(1);
-  }
-
   void _logErrors(AnalysisResult analysisResult) {
     logger.stdout('');
 
@@ -912,6 +917,7 @@
           _fixCodeProcessor._task.includedRoot,
           _dartFixListener,
           _fixCodeProcessor._task.instrumentationListener,
+          {},
           analysisResult);
     } else {
       logger.stdout(ansi.emphasized('Re-generating migration suggestions...'));
@@ -1044,9 +1050,6 @@
   }
 
   Future<AnalysisResult> runFirstPhase() async {
-    // Process package
-    _task.processPackage(context.contextRoot.root);
-
     var analysisErrors = <AnalysisError>[];
 
     // All tasks should be registered; [numPhases] should be finalized.
@@ -1107,6 +1110,7 @@
     _migrationCli.logger.stdout(_migrationCli.ansi
         .emphasized('Compiling instrumentation information...'));
     var state = await _task.finish();
+    _task.processPackage(context.contextRoot.root, state.neededPackages);
     if (_migrationCli.options.webPreview) {
       await _task.startPreviewServer(state, _migrationCli.applyHook);
     }
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index af33d0d..25499a8 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -12,6 +12,7 @@
 import 'package:meta/meta.dart';
 import 'package:nnbd_migration/instrumentation.dart';
 import 'package:nnbd_migration/src/nullability_migration_impl.dart';
+import 'package:pub_semver/pub_semver.dart';
 
 export 'package:nnbd_migration/src/utilities/hint_utils.dart' show HintComment;
 
@@ -314,18 +315,12 @@
   /// Optional parameter [warnOnWeakCode] indicates whether weak-only code
   /// should be warned about or removed (in the way specified by
   /// [removeViaComments]).
-  ///
-  /// Optional parameter [transformWhereOrNull] indicates whether Iterable
-  /// methods should be transformed to their "OrNull" equivalents when possible.
-  /// This feature is a work in progress, so by default they are not
-  /// transformed.
   factory NullabilityMigration(NullabilityMigrationListener listener,
       LineInfo Function(String) getLineInfo,
       {bool permissive,
       NullabilityMigrationInstrumentation instrumentation,
       bool removeViaComments,
-      bool warnOnWeakCode,
-      bool transformWhereOrNull}) = NullabilityMigrationImpl;
+      bool warnOnWeakCode}) = NullabilityMigrationImpl;
 
   /// Check if this migration is being run permissively.
   bool get isPermissive;
@@ -337,7 +332,13 @@
 
   void finalizeInput(ResolvedUnitResult result);
 
-  void finish();
+  /// Finishes the migration.  Returns a map indicating packages that have been
+  /// newly imported by the migration; the caller should ensure that these
+  /// packages are properly imported by the package's pubspec.
+  ///
+  /// Keys of the returned map are package names; values indicate the minimum
+  /// required version of each package.
+  Map<String, Version> finish();
 
   void prepareInput(ResolvedUnitResult result);
 
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 3611061..f3875f3 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -217,7 +217,7 @@
   final Map<Token, HintComment> _nullCheckHints = {};
 
   /// Helper that assists us in transforming Iterable methods to their "OrNull"
-  /// equivalents, or `null` if we are not doing such transformations.
+  /// equivalents.
   final WhereOrNullTransformer _whereOrNullTransformer;
 
   /// Deferred processing that should be performed once we have finished
@@ -225,20 +225,12 @@
   final Map<MethodInvocation, DecoratedType Function(DecoratedType)>
       _deferredMethodInvocationProcessing = {};
 
-  EdgeBuilder(
-      this.typeProvider,
-      this._typeSystem,
-      this._variables,
-      this._graph,
-      this.source,
-      this.listener,
-      this._decoratedClassHierarchy,
-      bool transformWhereOrNull,
+  EdgeBuilder(this.typeProvider, this._typeSystem, this._variables, this._graph,
+      this.source, this.listener, this._decoratedClassHierarchy,
       {this.instrumentation})
       : _inheritanceManager = InheritanceManager3(),
-        _whereOrNullTransformer = transformWhereOrNull
-            ? WhereOrNullTransformer(typeProvider, _typeSystem)
-            : null;
+        _whereOrNullTransformer =
+            WhereOrNullTransformer(typeProvider, _typeSystem);
 
   /// Gets the decorated type of [element] from [_variables], performing any
   /// necessary substitutions.
@@ -2304,7 +2296,7 @@
         }
       } else {
         var transformationInfo =
-            _whereOrNullTransformer?.tryTransformOrElseArgument(expression);
+            _whereOrNullTransformer.tryTransformOrElseArgument(expression);
         if (transformationInfo != null) {
           // Don't build any edges for this argument; if necessary we'll transform
           // it rather than make things nullable.  But do save the nullability of
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 32b2777..ae77229 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -40,6 +40,7 @@
 import 'package:nnbd_migration/src/utilities/resolution_utils.dart';
 import 'package:nnbd_migration/src/utilities/where_or_null_transformer.dart';
 import 'package:nnbd_migration/src/variables.dart';
+import 'package:pub_semver/pub_semver.dart';
 
 bool _isIncrementOrDecrementOperator(TokenType tokenType) {
   switch (tokenType) {
@@ -121,7 +122,7 @@
   final NullabilityGraph _graph;
 
   /// Helper that assists us in transforming Iterable methods to their "OrNull"
-  /// equivalents, or `null` if we are not doing such transformations.
+  /// equivalents.
   final WhereOrNullTransformer _whereOrNullTransformer;
 
   /// Indicates whether an import of package:collection's `IterableExtension`
@@ -129,6 +130,11 @@
   @visibleForTesting
   bool needsIterableExtension = false;
 
+  /// Map of additional package dependencies that will be required by the
+  /// migrated code.  Keys are package names; values indicate the minimum
+  /// required version of each package.
+  final Map<String, Version> _neededPackages;
+
   factory FixBuilder(
       Source source,
       DecoratedClassHierarchy decoratedClassHierarchy,
@@ -140,7 +146,7 @@
       CompilationUnit unit,
       bool warnOnWeakCode,
       NullabilityGraph graph,
-      bool transformWhereOrNull) {
+      Map<String, Version> neededPackages) {
     var migrationResolutionHooks = MigrationResolutionHooksImpl();
     return FixBuilder._(
         decoratedClassHierarchy,
@@ -156,7 +162,7 @@
         migrationResolutionHooks,
         warnOnWeakCode,
         graph,
-        transformWhereOrNull);
+        neededPackages);
   }
 
   FixBuilder._(
@@ -170,11 +176,10 @@
       this.migrationResolutionHooks,
       this.warnOnWeakCode,
       this._graph,
-      bool transformWhereOrNull)
+      this._neededPackages)
       : typeProvider = _typeSystem.typeProvider,
-        _whereOrNullTransformer = transformWhereOrNull
-            ? WhereOrNullTransformer(_typeSystem.typeProvider, _typeSystem)
-            : null {
+        _whereOrNullTransformer =
+            WhereOrNullTransformer(_typeSystem.typeProvider, _typeSystem) {
     migrationResolutionHooks._fixBuilder = this;
     assert(_typeSystem.isNonNullableByDefault);
     assert((typeProvider as TypeProviderImpl).isNonNullableByDefault);
@@ -640,10 +645,12 @@
         InferenceContext.getContext(ancestor) ?? DynamicTypeImpl.instance;
     if (!_fixBuilder._typeSystem.isSubtypeOf(type, context)) {
       var transformationInfo =
-          _fixBuilder._whereOrNullTransformer?.tryTransformOrElseArgument(node);
+          _fixBuilder._whereOrNullTransformer.tryTransformOrElseArgument(node);
       if (transformationInfo != null) {
         // We can fix this by dropping the node and changing the method call.
         _fixBuilder.needsIterableExtension = true;
+        _fixBuilder._neededPackages['collection'] =
+            Version.parse('1.15.0-nullsafety.4');
         var info = AtomicEditInfo(
             NullabilityFixDescription.changeMethodName(
                 transformationInfo.originalName,
diff --git a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
index 1a4e769..4c6fa9c 100644
--- a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
@@ -7,6 +7,7 @@
     hide AnalysisError;
 import 'package:meta/meta.dart';
 import 'package:nnbd_migration/src/front_end/driver_provider_impl.dart';
+import 'package:pub_semver/src/version.dart';
 
 class DartFixListener {
   final DriverProviderImpl server;
@@ -15,14 +16,9 @@
 
   final List<DartFixSuggestion> suggestions = [];
 
-  /// Add the given [detail] to the list of details to be returned to the
-  /// client.
-  final void Function(String detail) reportException;
+  final DartFixListenerClient client;
 
-  /// Callback that reports a fatal error to the client.
-  final void Function(String detail) reportFatalError;
-
-  DartFixListener(this.server, this.reportException, this.reportFatalError);
+  DartFixListener(this.server, this.client);
 
   /// Record an edit to be sent to the client.
   ///
@@ -49,6 +45,13 @@
     suggestions.add(DartFixSuggestion(description, location: location));
   }
 
+  /// Reports to then user that they need to run `dart pub get` after the
+  /// migration finishes.
+  void reportPubGetNeeded(Map<String, Version> neededPackages) {
+    client.onMessage(
+        'Your pubspec has been updated.  Please run `dart pub get`.');
+  }
+
   /// Reset this listener so that it can accrue a new set of changes.
   void reset() {
     suggestions.clear();
@@ -60,6 +63,18 @@
   }
 }
 
+abstract class DartFixListenerClient {
+  /// Add the given [detail] to the list of details to be returned to the
+  /// client.
+  void onException(String detail);
+
+  /// Callback that reports a fatal error to the client.
+  void onFatalError(String detail);
+
+  /// Reports the given [detail] message to the client; not an error condition.
+  void onMessage(String detail);
+}
+
 class DartFixSuggestion {
   final String description;
 
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
index bfbf7cd..016c204 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
@@ -11,6 +11,7 @@
 import 'package:nnbd_migration/src/front_end/instrumentation_listener.dart';
 import 'package:nnbd_migration/src/front_end/migration_info.dart';
 import 'package:nnbd_migration/src/front_end/path_mapper.dart';
+import 'package:pub_semver/src/version.dart';
 
 /// The state of an NNBD migration.
 class MigrationState {
@@ -44,10 +45,16 @@
 
   /*late*/ List<String> previewUrls;
 
+  /// Map of additional package dependencies that will be required by the
+  /// migrated code.  Keys are package names; values indicate the minimum
+  /// required version of each package.
+  final Map<String, Version> neededPackages;
+
   /// Initialize a newly created migration state with the given values.
   MigrationState(this.migration, this.includedRoot, this.listener,
-      this.instrumentationListener,
-      [this.analysisResult]);
+      this.instrumentationListener, this.neededPackages,
+      [this.analysisResult])
+      : assert(neededPackages != null);
 
   /// If the migration has been applied to disk.
   bool get hasBeenApplied => _hasBeenApplied;
diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
index a691876..7b21c71 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -105,9 +105,9 @@
   }
 
   Future<MigrationState> finish() async {
-    migration.finish();
-    final state = MigrationState(
-        migration, includedRoot, listener, instrumentationListener);
+    var neededPackages = migration.finish();
+    final state = MigrationState(migration, includedRoot, listener,
+        instrumentationListener, neededPackages);
     await state.refresh(_logger);
     return state;
   }
@@ -120,7 +120,11 @@
   ///
   /// This means updating the pubspec.yaml file, the package_config.json
   /// file, and the analysis_options.yaml file, each only if necessary.
-  void processPackage(Folder pkgFolder) {
+  ///
+  /// [neededPackages] is a map whose keys are the names of packages that should
+  /// be dependend upon by the package's pubspec, and whose values are the
+  /// minimum required versions of those packages.
+  void processPackage(Folder pkgFolder, Map<String, Version> neededPackages) {
     var pubspecFile = pkgFolder.getChildAssumingFile('pubspec.yaml');
     if (!pubspecFile.exists) {
       // If the pubspec file cannot be found, we do not attempt to change the
@@ -139,7 +143,7 @@
       return;
     }
 
-    var updated = _processPubspec(pubspec);
+    var updated = _processPubspec(pubspec, neededPackages);
     if (updated) {
       _processConfigFile(pkgFolder, pubspec);
     }
@@ -280,7 +284,12 @@
 
   /// Updates the pubspec.yaml file to specify a minimum Dart SDK version which
   /// supports null safety.
-  bool _processPubspec(_YamlFile pubspec) {
+  ///
+  /// Return value indicates whether the user's `package_config.json` file
+  /// should be updated.
+  bool _processPubspec(_YamlFile pubspec, Map<String, Version> neededPackages) {
+    bool packageConfigNeedsUpdate = false;
+    bool packageDepsUpdated = false;
     var pubspecMap = pubspec.content;
     YamlNode environmentOptions;
     if (pubspecMap is YamlMap) {
@@ -291,46 +300,56 @@
       var content = '''
 environment:
   sdk: '$_intendedSdkVersionConstraint'
-
 ''';
       pubspec._insertAfterParent(
           SourceSpan(start, start, ''), content, listener);
+      packageConfigNeedsUpdate = true;
     } else if (environmentOptions is YamlMap) {
-      var sdk = environmentOptions.nodes['sdk'];
-      if (sdk == null) {
-        var content = """
-
-  sdk: '$_intendedSdkVersionConstraint'""";
-        pubspec._insertAfterParent(environmentOptions.span, content, listener);
-      } else if (sdk is YamlScalar) {
-        VersionConstraint currentConstraint;
-        if (sdk.value is String) {
-          currentConstraint = VersionConstraint.parse(sdk.value as String);
-          if (currentConstraint is VersionRange &&
-              currentConstraint.min >= _intendedMinimumSdkVersion) {
-            // The current SDK version constraint already enables Null Safety.
-            // Do not edit pubspec.yaml, nor package_config.json.
-            return false;
-          } else {
-            // TODO(srawlins): This overwrites the current maximum version. In
-            // the uncommon situation that the maximum is not '<3.0.0', it
-            // should not.
-            pubspec._replaceSpan(
-                sdk.span, "'$_intendedSdkVersionConstraint'", listener);
+      if (_updatePubspecConstraint(pubspec, environmentOptions, 'sdk',
+          "'$_intendedSdkVersionConstraint'", _intendedMinimumSdkVersion)) {
+        packageConfigNeedsUpdate = true;
+      }
+    } else {
+      // Odd malformed pubspec.  Leave it alone, but go ahead and update the
+      // package_config.json file.
+      packageConfigNeedsUpdate = true;
+    }
+    if (neededPackages.isNotEmpty) {
+      YamlNode dependencies;
+      if (pubspecMap is YamlMap) {
+        dependencies = pubspecMap.nodes['dependencies'];
+      }
+      if (dependencies == null) {
+        var depLines = [
+          for (var entry in neededPackages.entries)
+            '  ${entry.key}: ^${entry.value}'
+        ];
+        var start = SourceLocation(0, line: 0, column: 0);
+        var content = '''
+dependencies:
+${depLines.join('\n')}
+''';
+        pubspec._insertAfterParent(
+            SourceSpan(start, start, ''), content, listener);
+        packageDepsUpdated = true;
+      } else if (dependencies is YamlMap) {
+        for (var neededPackage in neededPackages.entries) {
+          if (_updatePubspecConstraint(pubspec, dependencies, neededPackage.key,
+              '^${neededPackage.value}', neededPackage.value)) {
+            packageDepsUpdated = true;
           }
-        } else {
-          // Something is odd with the SDK constraint we've found in
-          // pubspec.yaml; Best to leave it alone.
-          return false;
         }
       }
     }
+    if (packageDepsUpdated) {
+      listener.reportPubGetNeeded(neededPackages);
+    }
 
-    return true;
+    return packageConfigNeedsUpdate;
   }
 
   void _processPubspecException(String action, String pubspecPath, error) {
-    listener.reportFatalError('''Failed to $action pubspec file
+    listener.client.onFatalError('''Failed to $action pubspec file
   $pubspecPath
   $error
 
@@ -343,6 +362,48 @@
     throw StateError('listener.reportFatalError should never return');
   }
 
+  /// Updates a constraint in the given [pubspec] file.  If [key] is found in
+  /// [map], and the corresponding value does has a minimum less than
+  /// [minimumVersion], it is updated to [fullVersionConstraint].  If it is not
+  /// found, then an entry is added.
+  ///
+  /// Return value indicates whether a change was made.
+  bool _updatePubspecConstraint(_YamlFile pubspec, YamlMap map, String key,
+      String fullVersionConstraint, Version minimumVersion) {
+    var node = map.nodes[key];
+    if (node == null) {
+      var content = '''
+
+  $key: $fullVersionConstraint''';
+      pubspec._insertAfterParent(map.span, content, listener);
+      return true;
+    } else if (node is YamlScalar) {
+      VersionConstraint currentConstraint;
+      if (node.value is String) {
+        currentConstraint = VersionConstraint.parse(node.value as String);
+        if (currentConstraint is VersionRange &&
+            currentConstraint.min >= minimumVersion) {
+          // The current version constraint is already up to date.  Do not edit.
+          return false;
+        } else {
+          // TODO(srawlins): This overwrites the current maximum version. In
+          // the uncommon situation that there is a special maximum, it should
+          // not.
+          pubspec._replaceSpan(node.span, fullVersionConstraint, listener);
+          return true;
+        }
+      } else {
+        // Something is odd with the constraint we've found in pubspec.yaml;
+        // Best to leave it alone.
+        return false;
+      }
+    } else {
+      // Something is odd with the format of pubspec.yaml; best to leave it
+      // alone.
+      return false;
+    }
+  }
+
   /// Allows unit tests to shut down any rogue servers that have been started,
   /// so that unit testing can complete.
   @visibleForTesting
@@ -449,7 +510,7 @@
   @override
   void reportException(
       Source source, AstNode node, Object exception, StackTrace stackTrace) {
-    listener.reportException('''
+    listener.client.onException('''
 $exception
 
 $stackTrace''');
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index 81a3079..6c89f59 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -23,6 +23,7 @@
 import 'package:nnbd_migration/src/nullability_node.dart';
 import 'package:nnbd_migration/src/postmortem_file.dart';
 import 'package:nnbd_migration/src/variables.dart';
+import 'package:pub_semver/pub_semver.dart';
 
 /// Implementation of the [NullabilityMigration] public API.
 class NullabilityMigrationImpl implements NullabilityMigration {
@@ -60,10 +61,6 @@
 
   final LineInfo Function(String) _getLineInfo;
 
-  /// Indicates whether we should transform iterable methods taking an "orElse"
-  /// parameter into their "OrNull" equivalents if possible.
-  final bool transformWhereOrNull;
-
   /// Map from [Source] object to a boolean indicating whether the source is
   /// opted in to null safety.
   final Map<Source, bool> _libraryOptInStatus = {};
@@ -71,6 +68,11 @@
   /// Indicates whether the client has used the [unmigratedDependencies] getter.
   bool _queriedUnmigratedDependencies = false;
 
+  /// Map of additional package dependencies that will be required by the
+  /// migrated code.  Keys are package names; values indicate the minimum
+  /// required version of each package.
+  final Map<String, Version> _neededPackages = {};
+
   /// Prepares to perform nullability migration.
   ///
   /// If [permissive] is `true`, exception handling logic will try to proceed
@@ -84,18 +86,12 @@
   /// Optional parameter [warnOnWeakCode] indicates whether weak-only code
   /// should be warned about or removed (in the way specified by
   /// [removeViaComments]).
-  ///
-  /// Optional parameter [transformWhereOrNull] indicates whether Iterable
-  /// methods should be transformed to their "OrNull" equivalents when possible.
-  /// This feature is a work in progress, so by default they are not
-  /// transformed.
   NullabilityMigrationImpl(NullabilityMigrationListener listener,
       LineInfo Function(String) getLineInfo,
       {bool permissive = false,
       NullabilityMigrationInstrumentation instrumentation,
       bool removeViaComments = false,
-      bool warnOnWeakCode = true,
-      bool transformWhereOrNull = true})
+      bool warnOnWeakCode = true})
       : this._(
             listener,
             NullabilityGraph(instrumentation: instrumentation),
@@ -103,8 +99,7 @@
             instrumentation,
             removeViaComments,
             warnOnWeakCode,
-            getLineInfo,
-            transformWhereOrNull);
+            getLineInfo);
 
   NullabilityMigrationImpl._(
       this.listener,
@@ -113,8 +108,7 @@
       this._instrumentation,
       this.removeViaComments,
       this.warnOnWeakCode,
-      this._getLineInfo,
-      this.transformWhereOrNull) {
+      this._getLineInfo) {
     _instrumentation?.immutableNodes(_graph.never, _graph.always);
     _postmortemFileWriter?.graph = _graph;
   }
@@ -169,7 +163,7 @@
         unit,
         warnOnWeakCode,
         _graph,
-        transformWhereOrNull);
+        _neededPackages);
     try {
       DecoratedTypeParameterBounds.current = _decoratedTypeParameterBounds;
       fixBuilder.visitAll();
@@ -196,7 +190,7 @@
     }
   }
 
-  void finish() {
+  Map<String, Version> finish() {
     if (!_propagated) {
       // [finalizeInput] sets this field to `true`, so if it's still false, that
       // means it was never called; this probably means that all the code fed
@@ -205,6 +199,7 @@
     }
     _postmortemFileWriter?.write();
     _instrumentation?.finished();
+    return _neededPackages;
   }
 
   void prepareInput(ResolvedUnitResult result) {
@@ -260,7 +255,6 @@
           unit.declaredElement.source,
           _permissive ? listener : null,
           _decoratedClassHierarchy,
-          transformWhereOrNull,
           instrumentation: _instrumentation));
     } finally {
       DecoratedTypeParameterBounds.current = null;
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 1e86235..1eb3737 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -60,8 +60,7 @@
     var migration = NullabilityMigration(listener, getLineInfo,
         permissive: _usePermissiveMode,
         removeViaComments: removeViaComments,
-        warnOnWeakCode: warnOnWeakCode,
-        transformWhereOrNull: true);
+        warnOnWeakCode: warnOnWeakCode);
     for (var path in input.keys) {
       if (!(session.getFile(path)).isPart) {
         for (var unit in (await session.getResolvedLibrary(path)).units) {
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index 29e39622..7989905 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -3538,8 +3538,7 @@
         null,
         scope.thisOrAncestorOfType<CompilationUnit>(),
         warnOnWeakCode,
-        graph,
-        true);
+        graph, {});
   }
 
   bool _isInScope(AstNode node, AstNode scope) {
diff --git a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
index 0ca5ea4..b941ca3 100644
--- a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
+++ b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
@@ -17,8 +17,25 @@
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
-import 'analysis_abstract.dart';
 import '../utilities/test_logger.dart';
+import 'analysis_abstract.dart';
+
+class ListenerClient implements DartFixListenerClient {
+  @override
+  void onException(String detail) {
+    fail('Unexpected call to onException($detail)');
+  }
+
+  @override
+  void onFatalError(String detail) {
+    fail('Unexpected call to onFatalError($detail)');
+  }
+
+  @override
+  void onMessage(String detail) {
+    fail('Unexpected call to onMessage($detail)');
+  }
+}
 
 @reflectiveTest
 class NnbdMigrationTestBase extends AbstractAnalysisTest {
@@ -201,16 +218,14 @@
     // Compute the analysis results.
     var server = DriverProviderImpl(resourceProvider, driver.analysisContext);
     // Run the migration engine.
-    var listener =
-        DartFixListener(server, _exceptionReported, _exceptionReported);
+    var listener = DartFixListener(server, ListenerClient());
     var instrumentationListener = InstrumentationListener();
     var adapter = NullabilityMigrationAdapter(listener);
     var migration = NullabilityMigration(adapter, getLineInfo,
         permissive: false,
         instrumentation: instrumentationListener,
         removeViaComments: removeViaComments,
-        warnOnWeakCode: warnOnWeakCode,
-        transformWhereOrNull: true);
+        warnOnWeakCode: warnOnWeakCode);
     Future<void> _forEachPath(
         void Function(ResolvedUnitResult) callback) async {
       for (var testPath in testPaths) {
@@ -231,8 +246,4 @@
         migration, nodeMapper, logger);
     infos = await builder.explainMigration();
   }
-
-  void _exceptionReported(String detail) {
-    fail('Unexpected error during migration: $detail');
-  }
 }
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index ce9eb5f..9648403 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -1694,6 +1694,101 @@
     }
   }
 
+  test_pubspec_add_collection_dependency() async {
+    var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+    => x.firstWhere((x) => x.isEven, orElse: () => null);
+''', pubspecText: '''
+name: test
+environment:
+  sdk: '>=2.6.0 <3.0.0'
+dependencies:
+  foo: ^1.2.3
+''');
+    var projectDir = createProjectDir(projectContents);
+    var cliRunner = _createCli()
+        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+    await cliRunner.run();
+    expect(
+        logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
+    // The Dart source code should still be migrated.
+    assertProjectContents(
+        projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+    => x.firstWhereOrNull((x) => x.isEven);
+''', pubspecText: '''
+name: test
+environment:
+  sdk: '>=2.12.0 <3.0.0'
+dependencies:
+  foo: ^1.2.3
+  collection: ^1.15.0-nullsafety.4
+'''));
+  }
+
+  test_pubspec_add_dependency_and_environment_sections() async {
+    var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+    => x.firstWhere((x) => x.isEven, orElse: () => null);
+''', pubspecText: '''
+name: test
+''');
+    var projectDir = createProjectDir(projectContents);
+    var cliRunner = _createCli()
+        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+    await cliRunner.run();
+    expect(
+        logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
+    // The Dart source code should still be migrated.
+    assertProjectContents(
+        projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+    => x.firstWhereOrNull((x) => x.isEven);
+''',
+            // Note: section order is weird, but it's valid and this is a rare use
+            // case.
+            pubspecText: '''
+environment:
+  sdk: '>=2.12.0 <3.0.0'
+dependencies:
+  collection: ^1.15.0-nullsafety.4
+name: test
+'''));
+  }
+
+  test_pubspec_add_dependency_section() async {
+    var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+    => x.firstWhere((x) => x.isEven, orElse: () => null);
+''');
+    var projectDir = createProjectDir(projectContents);
+    var cliRunner = _createCli()
+        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+    await cliRunner.run();
+    expect(
+        logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
+    // The Dart source code should still be migrated.
+    assertProjectContents(projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+    => x.firstWhereOrNull((x) => x.isEven);
+''',
+        // Note: `dependencies` section is in a weird place, but it's valid and
+        // this is a rare use case.
+        pubspecText: '''
+dependencies:
+  collection: ^1.15.0-nullsafety.4
+name: test
+environment:
+  sdk: '>=2.12.0 <3.0.0'
+'''));
+  }
+
   test_pubspec_does_not_exist() async {
     var projectContents = simpleProject()..remove('pubspec.yaml');
     var projectDir = createProjectDir(projectContents);
@@ -1801,7 +1896,6 @@
         '''
 environment:
   sdk: '>=2.12.0 <3.0.0'
-
 name: test
 '''));
   }
@@ -1817,6 +1911,72 @@
     expect(message, contains('Failed to parse pubspec file'));
   }
 
+  test_pubspec_preserve_collection_dependency() async {
+    var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+    => x.firstWhere((x) => x.isEven, orElse: () => null);
+''', pubspecText: '''
+name: test
+environment:
+  sdk: '>=2.6.0 <3.0.0'
+dependencies:
+  collection: ^1.16.0
+''');
+    var projectDir = createProjectDir(projectContents);
+    var cliRunner = _createCli()
+        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+    await cliRunner.run();
+    expect(logger.stdoutBuffer.toString(),
+        isNot(contains('Please run `dart pub get`')));
+    // The Dart source code should still be migrated.
+    assertProjectContents(
+        projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+    => x.firstWhereOrNull((x) => x.isEven);
+''', pubspecText: '''
+name: test
+environment:
+  sdk: '>=2.12.0 <3.0.0'
+dependencies:
+  collection: ^1.16.0
+'''));
+  }
+
+  test_pubspec_update_collection_dependency() async {
+    var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+    => x.firstWhere((x) => x.isEven, orElse: () => null);
+''', pubspecText: '''
+name: test
+environment:
+  sdk: '>=2.6.0 <3.0.0'
+dependencies:
+  collection: ^1.14.0
+''');
+    var projectDir = createProjectDir(projectContents);
+    var cliRunner = _createCli()
+        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+    await cliRunner.run();
+    expect(
+        logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
+    // The Dart source code should still be migrated.
+    assertProjectContents(
+        projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+    => x.firstWhereOrNull((x) => x.isEven);
+''', pubspecText: '''
+name: test
+environment:
+  sdk: '>=2.12.0 <3.0.0'
+dependencies:
+  collection: ^1.15.0-nullsafety.4
+'''));
+  }
+
   test_pubspec_with_sdk_version_beta() async {
     var projectDir = createProjectDir(simpleProject());
     var cliRunner = _createCli(sdkVersion: '2.12.0-1.2.beta')
diff --git a/pkg/nnbd_migration/test/migration_visitor_test_base.dart b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
index f853f96..5ed5feb 100644
--- a/pkg/nnbd_migration/test/migration_visitor_test_base.dart
+++ b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
@@ -149,7 +149,7 @@
     var unit = await super.analyze(code);
     decoratedClassHierarchy = DecoratedClassHierarchy(variables, graph);
     unit.accept(EdgeBuilder(typeProvider, typeSystem, variables, graph,
-        testSource, null, decoratedClassHierarchy, true));
+        testSource, null, decoratedClassHierarchy));
     return unit;
   }
 }
diff --git a/pkg/nnbd_migration/test/preview/preview_site_test.dart b/pkg/nnbd_migration/test/preview/preview_site_test.dart
index 43fd177..3f9ce89 100644
--- a/pkg/nnbd_migration/test/preview/preview_site_test.dart
+++ b/pkg/nnbd_migration/test/preview/preview_site_test.dart
@@ -36,11 +36,10 @@
   }
 
   void setUp() {
-    dartfixListener =
-        DartFixListener(null, _exceptionReported, _exceptionReported);
+    dartfixListener = DartFixListener(null, ListenerClient());
     resourceProvider = MemoryResourceProvider();
     final migrationInfo = MigrationInfo({}, {}, null, null);
-    state = MigrationState(null, null, dartfixListener, null);
+    state = MigrationState(null, null, dartfixListener, null, {});
     state.pathMapper = PathMapper(resourceProvider);
     state.migrationInfo = migrationInfo;
     site = PreviewSite(state, () async {
@@ -166,10 +165,6 @@
     expect(file.readAsStringSync(), currentContent);
     expect(state.hasBeenApplied, false);
   }
-
-  void _exceptionReported(String detail) {
-    fail('Unexpected error during migration: $detail');
-  }
 }
 
 mixin PreviewSiteTestMixin {
@@ -191,10 +186,9 @@
   @override
   void setUp() {
     super.setUp();
-    dartfixListener =
-        DartFixListener(null, _exceptionReported, _exceptionReported);
+    dartfixListener = DartFixListener(null, ListenerClient());
     final migrationInfo = MigrationInfo({}, {}, null, null);
-    state = MigrationState(null, null, dartfixListener, null);
+    state = MigrationState(null, null, dartfixListener, null, {});
     nodeMapper = state.nodeMapper;
     state.pathMapper = PathMapper(resourceProvider);
     state.migrationInfo = migrationInfo;
@@ -339,8 +333,4 @@
     expect(state.hasBeenApplied, false);
     expect(state.needsRerun, true);
   }
-
-  void _exceptionReported(String detail) {
-    fail('Unexpected error during migration: $detail');
-  }
 }
diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status
index f95c71d..bc2bb42 100644
--- a/runtime/observatory/tests/service/service.status
+++ b/runtime/observatory/tests/service/service.status
@@ -11,7 +11,6 @@
 pause_on_start_and_exit_with_child_test: Pass, RuntimeError # Issue 33049
 reload_sources_test: Pass, Slow # Reload is slow on the bots
 valid_source_locations_test: Pass, Slow # Generally slow, even in release-x64.
-validate_timer_port_behavior_test: Skip  # Issue 44166
 
 [ $arch == arm ]
 process_service_test: Pass, Fail # Issue 24344
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 8c09f06..d8996be 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -187,6 +187,7 @@
 step_through_switch_test: SkipByDesign
 step_through_switch_with_continue_test: SkipByDesign
 valid_source_locations_test: Skip, Timeout
+validate_timer_port_behavior_test: SkipByDesign, Timeout # Debugger disabled in AOT
 vm_timeline_flags_test: Skip, Timeout
 weak_properties_test: CompileTimeError
 yield_positions_with_finally_test: SkipByDesign
diff --git a/runtime/observatory_2/tests/service_2/service_2.status b/runtime/observatory_2/tests/service_2/service_2.status
index 6c5e1b4..c16feab 100644
--- a/runtime/observatory_2/tests/service_2/service_2.status
+++ b/runtime/observatory_2/tests/service_2/service_2.status
@@ -11,7 +11,6 @@
 pause_on_start_and_exit_with_child_test: Pass, RuntimeError # Issue 33049
 reload_sources_test: Pass, Slow # Reload is slow on the bots
 valid_source_locations_test: Pass, Slow # Generally slow, even in release-x64.
-validate_timer_port_behavior_test: Skip  # Issue 44166
 
 [ $arch == arm ]
 process_service_test: Pass, Fail # Issue 24344
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 8c09f06..d8996be 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -187,6 +187,7 @@
 step_through_switch_test: SkipByDesign
 step_through_switch_with_continue_test: SkipByDesign
 valid_source_locations_test: Skip, Timeout
+validate_timer_port_behavior_test: SkipByDesign, Timeout # Debugger disabled in AOT
 vm_timeline_flags_test: Skip, Timeout
 weak_properties_test: CompileTimeError
 yield_positions_with_finally_test: SkipByDesign
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 3091db9..73afe4c 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -14,6 +14,7 @@
 dart/data_uri_import_test/none: SkipByDesign
 dart/emit_aot_size_info_flag_test: Pass, Slow # Spawns several subprocesses
 dart/isolates/*: Pass, Slow # Tests use many isolates and take a longer time.
+dart/isolates/dart_api_create_lightweight_isolate_test: Skip # Issue #44180
 dart/minimal_kernel_test: Pass, Slow # Spawns several subprocesses
 dart/null_safety_autodetection_in_kernel_compiler_test: Pass, Slow # Spawns several subprocesses
 dart/slow_path_shared_stub_test: Pass, Slow # Uses --shared-slow-path-triggers-gc flag.
@@ -23,6 +24,7 @@
 dart_2/data_uri_import_test/none: SkipByDesign
 dart_2/emit_aot_size_info_flag_test: Pass, Slow # Spawns several subprocesses
 dart_2/isolates/*: Pass, Slow # Tests use many isolates and take a longer time.
+dart_2/isolates/dart_api_create_lightweight_isolate_test: Skip # Issue #44180
 dart_2/minimal_kernel_test: Pass, Slow # Spawns several subprocesses
 dart_2/null_safety_autodetection_in_kernel_compiler_test: Pass, Slow # Spawns several subprocesses
 dart_2/slow_path_shared_stub_test: Pass, Slow # Uses --shared-slow-path-triggers-gc flag.
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index f570648..1d9be24 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -806,7 +806,7 @@
   }
 
   // If we are loading from source, figure out the mode from the source.
-  if (!KernelIsolate::GetExperimentalFlag("no-non-nullable")) {
+  if (KernelIsolate::GetExperimentalFlag(ExperimentalFeature::non_nullable)) {
     return KernelIsolate::DetectNullSafety(script_uri, package_config,
                                            original_working_directory);
   }
diff --git a/runtime/vm/experimental_features.cc b/runtime/vm/experimental_features.cc
new file mode 100644
index 0000000..1fffd1a
--- /dev/null
+++ b/runtime/vm/experimental_features.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2020, 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.
+// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
+//
+// Instead modify 'tools/experimental_features.yaml' and run
+// 'dart tools/generate_experimental_flags.dart' to update.
+//
+// Current version: 2.12.0
+
+#include "vm/experimental_features.h"
+
+#include <cstring>
+#include "platform/assert.h"
+#include "vm/globals.h"
+
+namespace dart {
+
+bool GetExperimentalFeatureDefault(ExperimentalFeature feature) {
+  constexpr bool kFeatureValues[] = {
+    true,
+    true,
+    true,
+    true,
+    true,
+    true,
+  };
+  ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureValues));
+  return kFeatureValues[static_cast<int>(feature)];
+}
+
+const char* GetExperimentalFeatureName(ExperimentalFeature feature) {
+  constexpr const char* kFeatureNames[] = {
+    "non-nullable",
+    "extension-methods",
+    "constant-update-2018",
+    "control-flow-collections",
+    "set-literals",
+    "spread-collections",
+  };
+  ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureNames));
+  return kFeatureNames[static_cast<int>(feature)];
+}
+
+}  // namespace dart
diff --git a/runtime/vm/experimental_features.h b/runtime/vm/experimental_features.h
new file mode 100644
index 0000000..797b0d2
--- /dev/null
+++ b/runtime/vm/experimental_features.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2020, 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.
+// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
+//
+// Instead modify 'tools/experimental_features.yaml' and run
+// 'dart tools/generate_experimental_flags.dart' to update.
+//
+// Current version: 2.12.0
+
+#ifndef RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+#define RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+
+namespace dart {
+
+enum class ExperimentalFeature {
+  non_nullable,
+  extension_methods,
+  constant_update_2018,
+  control_flow_collections,
+  set_literals,
+  spread_collections,
+};
+
+bool GetExperimentalFeatureDefault(ExperimentalFeature feature);
+const char* GetExperimentalFeatureName(ExperimentalFeature feature);
+
+}  // namespace dart
+
+#endif  // RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index f8f0d54..9af2bd6 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -417,13 +417,16 @@
   free(temp);
 }
 
-bool KernelIsolate::GetExperimentalFlag(const char* value) {
+bool KernelIsolate::GetExperimentalFlag(ExperimentalFeature feature) {
+  const char* value = GetExperimentalFeatureName(feature);
   for (const char* str : *experimental_flags_) {
     if (strcmp(str, value) == 0) {
       return true;
+    } else if (strstr(str, "no-") == str && strcmp(str + 3, value) == 0) {
+      return false;
     }
   }
-  return false;
+  return GetExperimentalFeatureDefault(feature);
 }
 
 DEFINE_OPTION_HANDLER(KernelIsolate::AddExperimentalFlag,
diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h
index e45022d..0aa5221 100644
--- a/runtime/vm/kernel_isolate.h
+++ b/runtime/vm/kernel_isolate.h
@@ -12,6 +12,7 @@
 
 #include "vm/allocation.h"
 #include "vm/dart.h"
+#include "vm/experimental_features.h"
 #include "vm/os_thread.h"
 
 namespace dart {
@@ -78,7 +79,7 @@
   static void NotifyAboutIsolateShutdown(const Isolate* isolate);
 
   static void AddExperimentalFlag(const char* value);
-  static bool GetExperimentalFlag(const char* value);
+  static bool GetExperimentalFlag(ExperimentalFeature feature);
 
  protected:
   static void InitCallback(Isolate* I);
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index 528aeb9e..52f46f8 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -220,7 +220,7 @@
 }
 
 bool TestCase::IsNNBD() {
-  return !KernelIsolate::GetExperimentalFlag("no-non-nullable");
+  return KernelIsolate::GetExperimentalFlag(ExperimentalFeature::non_nullable);
 }
 
 #ifndef PRODUCT
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index a0e71ef..fda79fb 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -98,6 +98,8 @@
   "elf.h",
   "exceptions.cc",
   "exceptions.h",
+  "experimental_features.cc",
+  "experimental_features.h",
   "ffi_callback_trampolines.cc",
   "ffi_callback_trampolines.h",
   "field_table.cc",
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart
index 9b45b66..6799347 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart
@@ -83,7 +83,7 @@
 @patch
 class ReceivePort {
   @patch
-  factory ReceivePort() = _ReceivePort;
+  factory ReceivePort([String debugName]) = _ReceivePort;
 
   @patch
   factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) =>
@@ -94,6 +94,8 @@
 /// (async_helper, unittest) create a dummy receive port to keep the Dart VM
 /// alive.
 class _ReceivePort extends Stream implements ReceivePort {
+  _ReceivePort([String debugName = '']);
+
   close() {}
 
   get sendPort => _unsupported();
@@ -108,7 +110,8 @@
 @patch
 class RawReceivePort {
   @patch
-  factory RawReceivePort([Function? handler]) => _unsupported();
+  factory RawReceivePort([Function? handler, String debugName = '']) =>
+      _unsupported();
 }
 
 @patch
diff --git a/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart b/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart
index 228fc3a..1c0b053 100644
--- a/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart
@@ -111,7 +111,7 @@
 @patch
 class ReceivePort {
   @patch
-  factory ReceivePort() = _ReceivePortImpl;
+  factory ReceivePort([String debugName]) = _ReceivePortImpl;
 
   @patch
   factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) {
@@ -120,6 +120,8 @@
 }
 
 class _ReceivePortImpl extends Stream implements ReceivePort {
+  _ReceivePortImpl([String debugName = '']);
+
   StreamSubscription listen(void Function(dynamic)? onData,
       {Function? onError,
       void Function()? onDone,
@@ -135,7 +137,7 @@
 @patch
 class RawReceivePort {
   @patch
-  factory RawReceivePort([Function? handler]) {
+  factory RawReceivePort([Function? handler, String debugName = '']) {
     throw new UnsupportedError('new RawReceivePort');
   }
 }
diff --git a/sdk/lib/_internal/vm/bin/io_service_patch.dart b/sdk/lib/_internal/vm/bin/io_service_patch.dart
index d54bbf2..7b93d0e 100644
--- a/sdk/lib/_internal/vm/bin/io_service_patch.dart
+++ b/sdk/lib/_internal/vm/bin/io_service_patch.dart
@@ -74,7 +74,7 @@
 
   static void _ensureInitialize() {
     if (_receivePort == null) {
-      _receivePort = new RawReceivePort();
+      _receivePort = new RawReceivePort(null, 'IO Service');
       _replyToPort = _receivePort!.sendPort;
       _receivePort!.handler = (data) {
         assert(data is List && data.length == 2);
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index 976b1f1..d567603 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -1206,7 +1206,7 @@
   void connectToEventHandler() {
     assert(!isClosed);
     if (eventPort == null) {
-      eventPort = new RawReceivePort(multiplex);
+      eventPort = new RawReceivePort(multiplex, 'Socket Event Handler');
     }
   }
 
diff --git a/sdk/lib/_internal/vm/lib/isolate_patch.dart b/sdk/lib/_internal/vm/lib/isolate_patch.dart
index 187efdb..23d5dfe 100644
--- a/sdk/lib/_internal/vm/lib/isolate_patch.dart
+++ b/sdk/lib/_internal/vm/lib/isolate_patch.dart
@@ -391,7 +391,8 @@
     }
 
     const bool newIsolateGroup = false;
-    final RawReceivePort readyPort = new RawReceivePort();
+    final RawReceivePort readyPort =
+        new RawReceivePort(null, 'Isolate.spawn ready');
     try {
       spawnFunction(
           readyPort.sendPort,
@@ -468,7 +469,8 @@
     // The VM will invoke [_startIsolate] and not `main`.
     final packageConfigString = packageConfig?.toString();
 
-    final RawReceivePort readyPort = new RawReceivePort();
+    final RawReceivePort readyPort =
+        new RawReceivePort(null, 'Isolate.spawnUri ready');
     try {
       _spawnUri(
           readyPort.sendPort,
diff --git a/sdk/lib/_internal/vm/lib/timer_impl.dart b/sdk/lib/_internal/vm/lib/timer_impl.dart
index a348d19..ac1939d 100644
--- a/sdk/lib/_internal/vm/lib/timer_impl.dart
+++ b/sdk/lib/_internal/vm/lib/timer_impl.dart
@@ -452,7 +452,7 @@
     if (_receivePort == null) {
       assert(_receivePort == null);
       assert(_sendPort == null);
-      _receivePort = RawReceivePort(_handleMessage);
+      _receivePort = RawReceivePort(_handleMessage, 'Timer');
       _sendPort = _receivePort!.sendPort;
       _scheduledWakeupTime = 0;
     } else {
diff --git a/sdk/lib/developer/service.dart b/sdk/lib/developer/service.dart
index a563f39..6fd58c3 100644
--- a/sdk/lib/developer/service.dart
+++ b/sdk/lib/developer/service.dart
@@ -41,7 +41,8 @@
   /// Uri to access the service).
   static Future<ServiceProtocolInfo> getInfo() async {
     // Port to receive response from service isolate.
-    final RawReceivePort receivePort = new RawReceivePort();
+    final RawReceivePort receivePort =
+        new RawReceivePort(null, 'Service.getInfo');
     final Completer<Uri?> uriCompleter = new Completer<Uri?>();
     receivePort.handler = (Uri? uri) => uriCompleter.complete(uri);
     // Request the information from the service isolate.
@@ -62,7 +63,8 @@
     // TODO: When NNBD is complete, delete the following line.
     ArgumentError.checkNotNull(enable, 'enable');
     // Port to receive response from service isolate.
-    final RawReceivePort receivePort = new RawReceivePort();
+    final RawReceivePort receivePort =
+        new RawReceivePort(null, 'Service.controlWebServer');
     final Completer<Uri> uriCompleter = new Completer<Uri>();
     receivePort.handler = (Uri uri) => uriCompleter.complete(uri);
     // Request the information from the service isolate.
diff --git a/sdk/lib/vmservice/message.dart b/sdk/lib/vmservice/message.dart
index ee29fe3..5b07cea 100644
--- a/sdk/lib/vmservice/message.dart
+++ b/sdk/lib/vmservice/message.dart
@@ -156,7 +156,7 @@
   }
 
   Future<Response> sendToIsolate(SendPort sendPort) {
-    final receivePort = RawReceivePort();
+    final receivePort = RawReceivePort(null, 'Isolate Message');
     receivePort.handler = (value) {
       receivePort.close();
       _setResponseFromPort(value);
@@ -200,7 +200,7 @@
   }
 
   Future<Response> sendToVM() {
-    final receivePort = RawReceivePort();
+    final receivePort = RawReceivePort(null, 'VM Message');
     receivePort.handler = (value) {
       receivePort.close();
       _setResponseFromPort(value);
diff --git a/sdk/lib/vmservice/vmservice.dart b/sdk/lib/vmservice/vmservice.dart
index 98ce332..f7f4a26 100644
--- a/sdk/lib/vmservice/vmservice.dart
+++ b/sdk/lib/vmservice/vmservice.dart
@@ -21,8 +21,8 @@
 part 'message_router.dart';
 part 'named_lookup.dart';
 
-final isolateControlPort = RawReceivePort();
-final scriptLoadPort = RawReceivePort();
+final isolateControlPort = RawReceivePort(null, 'Isolate Control Port');
+final scriptLoadPort = RawReceivePort(null, 'Script Load');
 
 abstract class IsolateEmbedderData {
   void cleanup();
diff --git a/tools/VERSION b/tools/VERSION
index ffc3992..77d848e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 37
+PRERELEASE 38
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml
index e1403b8..99f8c15 100644
--- a/tools/experimental_features.yaml
+++ b/tools/experimental_features.yaml
@@ -10,7 +10,8 @@
 #
 # ### Code Generation
 #
-# When you change this file, run the following to update analyzer and kernel:
+# When you change this file, run the following to update analyzer, kernel, and
+# vm:
 #
 # analyzer:
 #   dart pkg/analyzer/tool/experiments/generate.dart
@@ -18,6 +19,9 @@
 # kernel:
 #   pkg/front_end/tool/fasta generate-experimental-flags
 #
+# vm:
+#   dart tools/generate_experimental_flags.dart
+#
 # ### Overview
 #
 # This document consists mostly of a map called "features".
diff --git a/tools/generate_experimental_flags.dart b/tools/generate_experimental_flags.dart
new file mode 100644
index 0000000..bdb089e
--- /dev/null
+++ b/tools/generate_experimental_flags.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2020, 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.
+
+// @dart = 2.9
+
+import 'dart:io' show File, Platform;
+import 'package:yaml/yaml.dart' show YamlMap, loadYaml;
+
+void main() {
+  YamlMap yaml =
+      loadYaml(new File.fromUri(computeYamlFile()).readAsStringSync());
+  final currentVersion = getAsVersionNumber(yaml['current-version']);
+  final enumNames = new StringBuffer();
+  final featureValues = new StringBuffer();
+  final featureNames = new StringBuffer();
+
+  YamlMap features = yaml['features'];
+  for (var entry in features.entries) {
+    final category = (entry.value as YamlMap)['category'];
+    if (category == null || category == "vm" || category == "language") {
+      final version = getAsVersionNumber((entry.value as YamlMap)['enabledIn']);
+      if (version != null) {
+        final value = isGreaterOrEqualVersion(currentVersion, version);
+        final name = entry.key.replaceAll('-', '_');
+        enumNames.write('  $name,\n');
+        featureValues.write('    $value,\n');
+        featureNames.write('    "${entry.key}",\n');
+      }
+    }
+  }
+
+  final h = '''
+// Copyright (c) 2020, 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.
+// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
+//
+// Instead modify 'tools/experimental_features.yaml' and run
+// 'dart tools/generate_experimental_flags.dart' to update.
+//
+// Current version: ${currentVersion.join('.')}
+
+#ifndef RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+#define RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+
+namespace dart {
+
+enum class ExperimentalFeature {
+$enumNames};
+
+bool GetExperimentalFeatureDefault(ExperimentalFeature feature);
+const char* GetExperimentalFeatureName(ExperimentalFeature feature);
+
+}  // namespace dart
+
+#endif  // RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+''';
+
+  final cc = '''
+// Copyright (c) 2020, 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.
+// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
+//
+// Instead modify 'tools/experimental_features.yaml' and run
+// 'dart tools/generate_experimental_flags.dart' to update.
+//
+// Current version: ${currentVersion.join('.')}
+
+#include "vm/experimental_features.h"
+
+#include <cstring>
+#include "platform/assert.h"
+#include "vm/globals.h"
+
+namespace dart {
+
+bool GetExperimentalFeatureDefault(ExperimentalFeature feature) {
+  constexpr bool kFeatureValues[] = {
+$featureValues  };
+  ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureValues));
+  return kFeatureValues[static_cast<int>(feature)];
+}
+
+const char* GetExperimentalFeatureName(ExperimentalFeature feature) {
+  constexpr const char* kFeatureNames[] = {
+$featureNames  };
+  ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureNames));
+  return kFeatureNames[static_cast<int>(feature)];
+}
+
+}  // namespace dart
+''';
+
+  File.fromUri(computeHFile()).writeAsStringSync(h);
+  File.fromUri(computeCcFile()).writeAsStringSync(cc);
+}
+
+Uri computeYamlFile() {
+  return Platform.script.resolve("experimental_features.yaml");
+}
+
+Uri computeCcFile() {
+  return Platform.script.resolve("../runtime/vm/experimental_features.cc");
+}
+
+Uri computeHFile() {
+  return Platform.script.resolve("../runtime/vm/experimental_features.h");
+}
+
+List<num> getAsVersionNumber(dynamic value) {
+  if (value == null) return null;
+  final version = List.of("$value".split(".").map(int.parse));
+  while (version.length < 3) version.add(0);
+  return version;
+}
+
+bool isGreaterOrEqualVersion(List<num> left, List<num> right) {
+  assert(left.length == right.length);
+  for (var i = 0; i < left.length; ++i) {
+    if (left[i] != right[i]) return left[i] > right[i];
+  }
+  return true;
+}