Version 2.13.0-72.0.dev

Merge commit 'f63f7736c5681d8a2c1c5bdccbc4feaf403fbdac' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes.dart
index acc5edc..bea98e1 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes.dart
@@ -131,11 +131,11 @@
     return message.compareTo(message);
   }
 
-  FormattedMessage withFormatting(String formatted, int line, int column,
-      Severity severity, List<FormattedMessage> relatedInformation,
+  FormattedMessage withFormatting(PlainAndColorizedString formatted, int line,
+      int column, Severity severity, List<FormattedMessage> relatedInformation,
       {List<Uri>? involvedFiles}) {
-    return new FormattedMessage(
-        this, formatted, line, column, severity, relatedInformation,
+    return new FormattedMessage(this, formatted.plain, formatted.colorized,
+        line, column, severity, relatedInformation,
         involvedFiles: involvedFiles);
   }
 
@@ -162,10 +162,20 @@
       'messageObject=$messageObject)';
 }
 
+class PlainAndColorizedString {
+  final String plain;
+  final String colorized;
+
+  const PlainAndColorizedString(this.plain, this.colorized);
+  const PlainAndColorizedString.plainOnly(this.plain) : this.colorized = plain;
+}
+
 class FormattedMessage implements DiagnosticMessage {
   final LocatedMessage locatedMessage;
 
-  final String formatted;
+  final String formattedPlain;
+
+  final String formattedColorized;
 
   final int line;
 
@@ -178,8 +188,14 @@
 
   final List<Uri>? involvedFiles;
 
-  const FormattedMessage(this.locatedMessage, this.formatted, this.line,
-      this.column, this.severity, this.relatedInformation,
+  const FormattedMessage(
+      this.locatedMessage,
+      this.formattedPlain,
+      this.formattedColorized,
+      this.line,
+      this.column,
+      this.severity,
+      this.relatedInformation,
       {this.involvedFiles});
 
   Code<dynamic> get code => locatedMessage.code;
@@ -200,18 +216,22 @@
 
   @override
   Iterable<String> get ansiFormatted sync* {
-    yield formatted;
+    yield formattedColorized;
     if (relatedInformation != null) {
       for (FormattedMessage m in relatedInformation!) {
-        yield m.formatted;
+        yield m.formattedColorized;
       }
     }
   }
 
   @override
-  Iterable<String> get plainTextFormatted {
-    // TODO(ahe): Implement this correctly.
-    return ansiFormatted;
+  Iterable<String> get plainTextFormatted sync* {
+    yield formattedPlain;
+    if (relatedInformation != null) {
+      for (FormattedMessage m in relatedInformation!) {
+        yield m.formattedPlain;
+      }
+    }
   }
 
   Map<String, Object?> toJson() {
diff --git a/pkg/analyzer/lib/file_system/memory_file_system.dart b/pkg/analyzer/lib/file_system/memory_file_system.dart
index e18d6aa..7f8587d 100644
--- a/pkg/analyzer/lib/file_system/memory_file_system.dart
+++ b/pkg/analyzer/lib/file_system/memory_file_system.dart
@@ -510,7 +510,10 @@
       : super(provider, path);
 
   @override
-  bool get exists => provider._pathToResource[path] is _MemoryFolder;
+  bool get exists {
+    var canonicalPath = provider._resolveLinks(path);
+    return provider._pathToResource[canonicalPath] is _MemoryFolder;
+  }
 
   @override
   bool get isRoot {
@@ -580,15 +583,47 @@
 
   @override
   List<Resource> getChildren() {
+    var canonicalPath = provider._resolveLinks(path);
+    if (canonicalPath != path) {
+      var target = provider.getFolder(canonicalPath);
+      var canonicalChildren = target.getChildren();
+      return canonicalChildren.map((child) {
+        var childPath = provider.pathContext.join(path, child.shortName);
+        if (child is Folder) {
+          return _MemoryFolder(provider, childPath);
+        } else {
+          return _MemoryFile(provider, childPath);
+        }
+      }).toList();
+    }
+
     if (!exists) {
       throw FileSystemException(path, 'Folder does not exist.');
     }
-    List<Resource> children = <Resource>[];
+
+    var children = <Resource>[];
+
     provider._pathToResource.forEach((resourcePath, resource) {
       if (provider.pathContext.dirname(resourcePath) == path) {
         children.add(resource);
       }
     });
+
+    provider._pathToLinkedPath.forEach((resourcePath, targetPath) {
+      if (provider.pathContext.dirname(resourcePath) == path) {
+        var target = provider.getResource(targetPath);
+        if (target is File) {
+          children.add(
+            _MemoryFile(provider, resourcePath),
+          );
+        } else if (target is Folder) {
+          children.add(
+            _MemoryFolder(provider, resourcePath),
+          );
+        }
+      }
+    });
+
     return children;
   }
 
diff --git a/pkg/analyzer/lib/file_system/overlay_file_system.dart b/pkg/analyzer/lib/file_system/overlay_file_system.dart
index dfe0af7..5936481 100644
--- a/pkg/analyzer/lib/file_system/overlay_file_system.dart
+++ b/pkg/analyzer/lib/file_system/overlay_file_system.dart
@@ -399,5 +399,8 @@
       _OverlayResource._from(provider, _resource.resolveSymbolicLinksSync());
 
   @override
+  String toString() => path;
+
+  @override
   Uri toUri() => _resource.toUri();
 }
diff --git a/pkg/analyzer/lib/src/context/builder.dart b/pkg/analyzer/lib/src/context/builder.dart
index 8e3378d..b5e8a0a 100644
--- a/pkg/analyzer/lib/src/context/builder.dart
+++ b/pkg/analyzer/lib/src/context/builder.dart
@@ -56,12 +56,6 @@
 ///
 /// [1]: https://github.com/dart-lang/dart_enhancement_proposals/blob/master/Accepted/0005%20-%20Package%20Specification/DEP-pkgspec.md.
 class ContextBuilder {
-  /// A callback for when analysis drivers are created, which takes all the same
-  /// arguments as the dart analysis driver constructor so that plugins may
-  /// create their own drivers with the same tools, in theory. Here as a stopgap
-  /// until the official plugin API is complete
-  static Function? onCreateAnalysisDriver;
-
   /// The [ResourceProvider] by which paths are converted into [Resource]s.
   final ResourceProvider resourceProvider;
 
@@ -160,11 +154,6 @@
       ),
     );
 
-    // temporary plugin support:
-    if (onCreateAnalysisDriver != null) {
-      onCreateAnalysisDriver!(driver, analysisDriverScheduler, performanceLog,
-          resourceProvider, byteStore, fileContentOverlay, path, sf, options);
-    }
     declareVariablesInDriver(driver);
     return driver;
   }
diff --git a/pkg/analyzer/test/file_system/file_system_test_support.dart b/pkg/analyzer/test/file_system/file_system_test_support.dart
index 7947b4b..2323080 100644
--- a/pkg/analyzer/test/file_system/file_system_test_support.dart
+++ b/pkg/analyzer/test/file_system/file_system_test_support.dart
@@ -30,6 +30,10 @@
   /// to work.
   String /*!*/ get tempPath;
 
+  /// Create a link from [path] to [target].
+  /// The [target] does not have to exist, can be create later, or not at all.
+  void createLink({required String path, required String target});
+
   /// Return a file accessed through the resource provider. If [exists] is
   /// `true` then the returned file will exist, otherwise it won't. If [content]
   /// is provided then the file will have the given content, otherwise it will
@@ -134,6 +138,33 @@
     expect(file.exists, isTrue);
   }
 
+  test_exists_links_existing() {
+    var a_path = join(tempPath, 'a.dart');
+    var b_path = join(tempPath, 'b.dart');
+
+    createLink(path: b_path, target: a_path);
+    getFile(exists: true, filePath: a_path);
+
+    var a = provider.getFile(a_path);
+    var b = provider.getFile(b_path);
+
+    expect(a.exists, isTrue);
+    expect(b.exists, isTrue);
+  }
+
+  test_exists_links_notExisting() {
+    var a_path = join(tempPath, 'a.dart');
+    var b_path = join(tempPath, 'b.dart');
+
+    createLink(path: b_path, target: a_path);
+
+    var a = provider.getFile(a_path);
+    var b = provider.getFile(b_path);
+
+    expect(a.exists, isFalse);
+    expect(b.exists, isFalse);
+  }
+
   test_exists_notExisting() {
     File file = getFile(exists: false);
 
@@ -458,6 +489,33 @@
     expect(folder1 == folder2, isTrue);
   }
 
+  test_exists_links_existing() {
+    var foo_path = join(tempPath, 'foo');
+    var bar_path = join(tempPath, 'bar');
+
+    createLink(path: bar_path, target: foo_path);
+    getFolder(exists: true, folderPath: foo_path);
+
+    var foo = provider.getFolder(foo_path);
+    var bar = provider.getFolder(bar_path);
+
+    expect(foo.exists, isTrue);
+    expect(bar.exists, isTrue);
+  }
+
+  test_exists_links_notExisting() {
+    var foo_path = join(tempPath, 'foo');
+    var bar_path = join(tempPath, 'bar');
+
+    createLink(path: bar_path, target: foo_path);
+
+    var foo = provider.getFolder(foo_path);
+    var bar = provider.getFolder(bar_path);
+
+    expect(foo.exists, isFalse);
+    expect(bar.exists, isFalse);
+  }
+
   test_getChild_doesNotExist() {
     Folder folder = getFolder(exists: true);
 
@@ -568,6 +626,68 @@
     expect(children[2], isFile);
   }
 
+  test_getChildren_hasLink_file() {
+    var a_path = join(tempPath, 'a.dart');
+    var b_path = join(tempPath, 'b.dart');
+
+    createLink(path: b_path, target: a_path);
+    var a = getFile(exists: true, filePath: a_path);
+
+    var children = provider.getFolder(tempPath).getChildren();
+    expect(children, hasLength(2));
+    expect(
+      children.map((e) => e.path),
+      unorderedEquals([a_path, b_path]),
+    );
+
+    var b = children.singleWhere((e) => e.path == b_path) as File;
+    expect(b.resolveSymbolicLinksSync(), a);
+  }
+
+  test_getChildren_hasLink_folder() {
+    var foo_path = join(tempPath, 'foo');
+    var bar_path = join(tempPath, 'bar');
+
+    var foo = getFolder(exists: true, folderPath: foo_path);
+    createLink(path: bar_path, target: foo_path);
+
+    var children = provider.getFolder(tempPath).getChildren();
+    expect(children, hasLength(2));
+    expect(
+      children.map((e) => e.path),
+      unorderedEquals([foo_path, bar_path]),
+    );
+
+    var b = children.singleWhere((e) => e.path == bar_path) as Folder;
+    expect(b.resolveSymbolicLinksSync(), foo);
+  }
+
+  test_getChildren_isLink() {
+    var foo_path = join(tempPath, 'foo');
+    var bar_path = join(tempPath, 'bar');
+    var foo_a_path = join(foo_path, 'a.dart');
+    var bar_a_path = join(bar_path, 'a.dart');
+    var foo_b_path = join(foo_path, 'b');
+    var bar_b_path = join(bar_path, 'b');
+
+    var foo_a = getFile(exists: true, filePath: foo_a_path);
+    var foo_b = getFolder(exists: true, folderPath: foo_b_path);
+    createLink(path: bar_path, target: foo_path);
+
+    var children = provider.getFolder(bar_path).getChildren();
+    expect(children, hasLength(2));
+    expect(
+      children.map((e) => e.path),
+      unorderedEquals([bar_a_path, bar_b_path]),
+    );
+
+    var bar_a = children.singleWhere((e) => e.path == bar_a_path) as File;
+    expect(bar_a.resolveSymbolicLinksSync(), foo_a);
+
+    var bar_b = children.singleWhere((e) => e.path == bar_b_path) as Folder;
+    expect(bar_b.resolveSymbolicLinksSync(), foo_b);
+  }
+
   test_hashCode() {
     Folder folder1 = getFolder(exists: false);
     Folder folder2 = getFolder(exists: false);
diff --git a/pkg/analyzer/test/file_system/memory_file_system_test.dart b/pkg/analyzer/test/file_system/memory_file_system_test.dart
index 6c925f4..9dc70d1 100644
--- a/pkg/analyzer/test/file_system/memory_file_system_test.dart
+++ b/pkg/analyzer/test/file_system/memory_file_system_test.dart
@@ -52,6 +52,11 @@
   @override
   MemoryResourceProvider get provider => _provider ??= createProvider();
 
+  @override
+  void createLink({required String path, required String target}) {
+    provider.newLink(path, target);
+  }
+
   /// 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.
@@ -252,14 +257,14 @@
 
   @override
   test_resolveSymbolicLinksSync_links_existing() {
-    var a = provider.convertPath('/test/lib/a.dart');
-    var b = provider.convertPath('/test/lib/b.dart');
+    var a_path = provider.convertPath('/test/lib/a.dart');
+    var b_path = provider.convertPath('/test/lib/b.dart');
 
-    provider.newLink(b, a);
-    provider.newFile(a, 'aaa');
+    provider.newLink(b_path, a_path);
+    provider.newFile(a_path, '');
 
-    var resolved = provider.getFile(b).resolveSymbolicLinksSync();
-    expect(resolved.path, a);
+    var resolved = provider.getFile(b_path).resolveSymbolicLinksSync();
+    expect(resolved.path, a_path);
   }
 
   @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 63a4e5a..48a75fa 100644
--- a/pkg/analyzer/test/file_system/physical_file_system_test.dart
+++ b/pkg/analyzer/test/file_system/physical_file_system_test.dart
@@ -52,6 +52,11 @@
   @override
   PhysicalResourceProvider get provider => _provider ??= createProvider();
 
+  @override
+  void createLink({required String path, required String target}) {
+    io.Link(path).createSync(target);
+  }
+
   /// 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.
diff --git a/pkg/analyzer/test/src/services/available_declarations_test.dart b/pkg/analyzer/test/src/services/available_declarations_test.dart
index 6ba066f..019fe7b 100644
--- a/pkg/analyzer/test/src/services/available_declarations_test.dart
+++ b/pkg/analyzer/test/src/services/available_declarations_test.dart
@@ -211,6 +211,10 @@
     expect(library.id, id);
   }
 
+  @SkippedTest(
+    issue: 'https://github.com/dart-lang/sdk/issues/44501',
+    reason: 'Actually, with fixed ResourceProvider, this test cycles',
+  )
   test_getLibrary_exportViaRecursiveLink() async {
     resourceProvider.newLink(
       convertPath('/home/test/lib/foo'),
diff --git a/pkg/analyzer_plugin/CHANGELOG.md b/pkg/analyzer_plugin/CHANGELOG.md
index a837766..8215421 100644
--- a/pkg/analyzer_plugin/CHANGELOG.md
+++ b/pkg/analyzer_plugin/CHANGELOG.md
@@ -1,5 +1,7 @@
-## 0.4.1-dev
+## 0.5.0-dev
 - Changed the support version range of the analyzer to `>=0.42.0 <0.43.0`.
+- Removed `Plugin.fileContentOverlay`, instead `Plugin.resourceProvider` is
+  now `OverlayResourceProvider`, and `analysis.updateContent` updates it.
 
 ## 0.4.0
 - Deprecated the class `DartChangeBuilder` and enhanced `ChangeBuilder` to be
diff --git a/pkg/analyzer_plugin/lib/plugin/plugin.dart b/pkg/analyzer_plugin/lib/plugin/plugin.dart
index cdf8829..9fb108f 100644
--- a/pkg/analyzer_plugin/lib/plugin/plugin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/plugin.dart
@@ -4,12 +4,12 @@
 
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/overlay_file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart'
     show AnalysisDriver, AnalysisDriverGeneric, AnalysisDriverScheduler;
 import 'package:analyzer/src/dart/analysis/file_byte_store.dart';
-import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer_plugin/channel/channel.dart';
@@ -36,7 +36,10 @@
   PluginCommunicationChannel _channel;
 
   /// The resource provider used to access the file system.
-  final ResourceProvider resourceProvider;
+  final OverlayResourceProvider resourceProvider;
+
+  /// The next modification stamp for a changed file in the [resourceProvider].
+  int _overlayModificationStamp = 0;
 
   /// The object used to manage analysis subscriptions.
   final SubscriptionManager subscriptionManager = SubscriptionManager();
@@ -60,14 +63,12 @@
   /// The SDK manager used to manage SDKs.
   DartSdkManager _sdkManager;
 
-  /// The file content overlay used by any analysis drivers that are created.
-  final FileContentOverlay fileContentOverlay = FileContentOverlay();
-
   /// Initialize a newly created analysis server plugin. If a resource [provider]
   /// is given, then it will be used to access the file system. Otherwise a
   /// resource provider that accesses the physical file system will be used.
   ServerPlugin(ResourceProvider provider)
-      : resourceProvider = provider ?? PhysicalResourceProvider.INSTANCE {
+      : resourceProvider = OverlayResourceProvider(
+            provider ?? PhysicalResourceProvider.INSTANCE) {
     analysisDriverScheduler = AnalysisDriverScheduler(performanceLog);
     analysisDriverScheduler.start();
   }
@@ -275,11 +276,20 @@
       AnalysisUpdateContentParams parameters) async {
     Map<String, Object> files = parameters.files;
     files.forEach((String filePath, Object overlay) {
+      // Prepare the old overlay contents.
+      String oldContents;
+      try {
+        if (resourceProvider.hasOverlay(filePath)) {
+          var file = resourceProvider.getFile(filePath);
+          oldContents = file.readAsStringSync();
+        }
+      } catch (_) {}
+
+      // Prepare the new contents.
+      String newContents;
       if (overlay is AddContentOverlay) {
-        fileContentOverlay[filePath] = overlay.content;
+        newContents = overlay.content;
       } else if (overlay is ChangeContentOverlay) {
-        var oldContents = fileContentOverlay[filePath];
-        String newContents;
         if (oldContents == null) {
           // The server should only send a ChangeContentOverlay if there is
           // already an existing overlay for the source.
@@ -292,10 +302,20 @@
           throw RequestFailure(
               RequestErrorFactory.invalidOverlayChangeInvalidEdit());
         }
-        fileContentOverlay[filePath] = newContents;
       } else if (overlay is RemoveContentOverlay) {
-        fileContentOverlay[filePath] = null;
+        newContents = null;
       }
+
+      if (newContents != null) {
+        resourceProvider.setOverlay(
+          filePath,
+          content: newContents,
+          modificationStamp: _overlayModificationStamp++,
+        );
+      } else {
+        resourceProvider.removeOverlay(filePath);
+      }
+
       contentChanged(filePath);
     });
     return AnalysisUpdateContentResult();
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml
index 07562d9..2a91465 100644
--- a/pkg/analyzer_plugin/pubspec.yaml
+++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -1,6 +1,6 @@
 name: analyzer_plugin
 description: A framework and support code for building plugins for the analysis server.
-version: 0.4.1-dev
+version: 0.5.0-dev
 author: Dart Team <misc@dartlang.org>
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_plugin
 
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index 8037da0..780642b 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -8,6 +8,9 @@
 /// convenience methods.
 abstract class AbstractDataSource extends DataSourceMixin
     implements DataSource {
+  static final List<ir.DartType> emptyListOfDartTypes =
+      List<ir.DartType>.filled(0, null, growable: false);
+
   final bool useDataKinds;
   EntityReader _entityReader = const EntityReader();
   ComponentLookup _componentLookup;
@@ -143,6 +146,7 @@
   List<ir.DartType> _readDartTypeNodes(
       List<ir.TypeParameter> functionTypeVariables) {
     int count = readInt();
+    if (count == 0) return emptyListOfDartTypes;
     List<ir.DartType> types = new List<ir.DartType>.filled(count, null);
     for (int index = 0; index < count; index++) {
       types[index] = _readDartTypeNode(functionTypeVariables);
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
index 58af2ac..a4c69bd 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
@@ -9,7 +9,8 @@
 import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
     show DiagnosticMessage, DiagnosticMessageHandler;
 
-import 'package:_fe_analyzer_shared/src/messages/codes.dart' show Message, Code;
+import 'package:_fe_analyzer_shared/src/messages/codes.dart'
+    show Code, Message, PlainAndColorizedString;
 
 import 'package:dev_compiler/dev_compiler.dart';
 import 'package:dev_compiler/src/js_ast/js_ast.dart' as js_ast;
@@ -44,8 +45,8 @@
   return Message(Code<String>('Expression Compiler Internal error'),
           message: msg)
       .withLocation(uri, 0, 0)
-      .withFormatting(
-          'Internal error: $msg', line, col, Severity.internalProblem, []);
+      .withFormatting(PlainAndColorizedString.plainOnly('Internal error: $msg'),
+          line, col, Severity.internalProblem, []);
 }
 
 /// Dart scope
diff --git a/pkg/front_end/lib/src/api_prototype/terminal_color_support.dart b/pkg/front_end/lib/src/api_prototype/terminal_color_support.dart
index ab69e10..124a899 100644
--- a/pkg/front_end/lib/src/api_prototype/terminal_color_support.dart
+++ b/pkg/front_end/lib/src/api_prototype/terminal_color_support.dart
@@ -4,110 +4,18 @@
 
 library front_end.terminal_color_support;
 
-import 'dart:convert' show jsonEncode;
-
-import 'dart:io' show Platform, Process, ProcessResult, stderr, stdout;
-
 import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
     show DiagnosticMessage;
 
-import 'package:_fe_analyzer_shared/src/util/colors.dart'
-    show ALL_CODES, TERMINAL_CAPABILITIES;
+import 'package:_fe_analyzer_shared/src/util/colors.dart' show enableColors;
 
-/// True if we should enable colors in output.
-///
-/// We enable colors only when both [stdout] and [stderr] support ANSI escapes.
-final bool enableTerminalColors = _computeEnableColors();
+export 'package:_fe_analyzer_shared/src/util/colors.dart' show enableColors;
 
 void printDiagnosticMessage(
     DiagnosticMessage message, void Function(String) println) {
-  if (enableTerminalColors) {
+  if (enableColors) {
     message.ansiFormatted.forEach(println);
   } else {
     message.plainTextFormatted.forEach(println);
   }
 }
-
-/// On Windows, colors are enabled if both stdout and stderr supports ANSI
-/// escapes.  On other platforms, we rely on the external programs `tty` and
-/// `tput` to compute if ANSI colors are supported.
-bool _computeEnableColors() {
-  const bool debug =
-      const bool.fromEnvironment("front_end.debug_compute_enable_colors");
-
-  if (Platform.isWindows) {
-    if (!stdout.supportsAnsiEscapes || !stderr.supportsAnsiEscapes) {
-      // In this case, either [stdout] or [stderr] did not support the property
-      // `supportsAnsiEscapes`. Since we do not have another way to determine
-      // support for colors, we disable them.
-      if (debug) {
-        print("Not enabling colors as ANSI is not supported.");
-      }
-      return false;
-    }
-    if (debug) {
-      print("Enabling colors as OS is Windows.");
-    }
-    return true;
-  }
-
-  // We have to check if the terminal actually supports colors. Currently, to
-  // avoid linking the Dart VM with ncurses, ANSI escape support is reduced to
-  // `Platform.environment['TERM'].contains("xterm")`.
-
-  // Check if stdin is a terminal (TTY).
-  ProcessResult result =
-      Process.runSync("/bin/sh", ["-c", "tty > /dev/null 2> /dev/null"]);
-
-  if (result.exitCode != 0) {
-    if (debug) {
-      print("Not enabling colors, stdin isn't a terminal.");
-    }
-    return false;
-  }
-
-  // The `-S` option of `tput` allows us to query multiple capabilities at
-  // once.
-  result = Process.runSync(
-      "/bin/sh", ["-c", "printf '%s' '$TERMINAL_CAPABILITIES' | tput -S"]);
-
-  if (result.exitCode != 0) {
-    if (debug) {
-      print("Not enabling colors, running tput failed.");
-    }
-    return false;
-  }
-
-  List<String> lines = result.stdout.split("\n");
-
-  if (lines.length != 2) {
-    if (debug) {
-      print("Not enabling colors, unexpected output from tput: "
-          "${jsonEncode(result.stdout)}.");
-    }
-    return false;
-  }
-
-  String numberOfColors = lines[0];
-  if ((int.tryParse(numberOfColors) ?? -1) < 8) {
-    if (debug) {
-      print("Not enabling colors, less than 8 colors supported: "
-          "${jsonEncode(numberOfColors)}.");
-    }
-    return false;
-  }
-
-  String allCodes = lines[1].trim();
-  if (ALL_CODES != allCodes) {
-    if (debug) {
-      print("Not enabling colors, color codes don't match: "
-          "${jsonEncode(ALL_CODES)} != ${jsonEncode(allCodes)}.");
-    }
-    return false;
-  }
-
-  if (debug) {
-    print("Enabling colors.");
-  }
-  return true;
-}
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index 1b23da1..a3dcde5 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -36,7 +36,7 @@
 export '../api_prototype/standard_file_system.dart' show StandardFileSystem;
 
 export '../api_prototype/terminal_color_support.dart'
-    show printDiagnosticMessage;
+    show printDiagnosticMessage, enableColors;
 
 export '../base/nnbd_mode.dart' show NnbdMode;
 
diff --git a/pkg/front_end/lib/src/base/processed_options.dart b/pkg/front_end/lib/src/base/processed_options.dart
index 94a7d0d..1ea8dc4 100644
--- a/pkg/front_end/lib/src/base/processed_options.dart
+++ b/pkg/front_end/lib/src/base/processed_options.dart
@@ -45,6 +45,7 @@
         FormattedMessage,
         LocatedMessage,
         Message,
+        PlainAndColorizedString,
         messageCantInferPackagesFromManyInputs,
         messageCantInferPackagesFromPackageUri,
         messageCompilingWithSoundNullSafety,
@@ -216,7 +217,7 @@
     int offset = message.charOffset;
     Uri uri = message.uri;
     Location location = offset == -1 ? null : getLocation(uri, offset);
-    String formatted =
+    PlainAndColorizedString formatted =
         command_line_reporting.format(message, severity, location: location);
     List<FormattedMessage> formattedContext;
     if (context != null && context.isNotEmpty) {
diff --git a/pkg/front_end/lib/src/fasta/command_line_reporting.dart b/pkg/front_end/lib/src/fasta/command_line_reporting.dart
index 2b05cc6..9ff9cf4 100644
--- a/pkg/front_end/lib/src/fasta/command_line_reporting.dart
+++ b/pkg/front_end/lib/src/fasta/command_line_reporting.dart
@@ -21,7 +21,7 @@
     show $CARET, $SPACE, $TAB;
 
 import 'package:_fe_analyzer_shared/src/util/colors.dart'
-    show enableColors, green, magenta, red, yellow;
+    show green, magenta, red, yellow;
 
 import 'package:_fe_analyzer_shared/src/util/relativize.dart'
     show isWindows, relativizeUri;
@@ -34,7 +34,7 @@
 
 import 'crash.dart' show Crash, safeToString;
 
-import 'fasta_codes.dart' show LocatedMessage;
+import 'fasta_codes.dart' show LocatedMessage, PlainAndColorizedString;
 
 import 'messages.dart' show getLocation, getSourceLine;
 
@@ -42,10 +42,10 @@
 
 const bool hideWarnings = false;
 
-/// Formats [message] as a string that is suitable for output from a
-/// command-line tool. This includes source snippets and different colors based
-/// on [severity].
-String format(LocatedMessage message, Severity severity,
+/// Formats [message] as two strings that is suitable for output from a
+/// command-line tool. This includes source snippets and - in the colorized
+/// version - different colors based on [severity].
+PlainAndColorizedString format(LocatedMessage message, Severity severity,
     {Location location, Map<Uri, Source> uriToSource}) {
   try {
     int length = message.length;
@@ -55,34 +55,34 @@
       length = 1;
     }
     String prefix = severityPrefixes[severity];
-    String messageText =
+    String messageTextTmp =
         prefix == null ? message.message : "$prefix: ${message.message}";
     if (message.tip != null) {
-      messageText += "\n${message.tip}";
+      messageTextTmp += "\n${message.tip}";
     }
-    if (enableColors) {
-      switch (severity) {
-        case Severity.error:
-        case Severity.internalProblem:
-          messageText = red(messageText);
-          break;
+    final String messageTextPlain = messageTextTmp;
+    String messageTextColorized;
+    switch (severity) {
+      case Severity.error:
+      case Severity.internalProblem:
+        messageTextColorized = red(messageTextPlain);
+        break;
 
-        case Severity.warning:
-          messageText = magenta(messageText);
-          break;
+      case Severity.warning:
+        messageTextColorized = magenta(messageTextPlain);
+        break;
 
-        case Severity.context:
-          messageText = green(messageText);
-          break;
+      case Severity.context:
+        messageTextColorized = green(messageTextPlain);
+        break;
 
-        case Severity.info:
-          messageText = yellow(messageText);
-          break;
+      case Severity.info:
+        messageTextColorized = yellow(messageTextPlain);
+        break;
 
-        case Severity.ignored:
-        default:
-          return unhandled("$severity", "format", -1, null);
-      }
+      case Severity.ignored:
+      default:
+        return unhandled("$severity", "format", -1, null);
     }
 
     if (message.uri != null) {
@@ -94,10 +94,17 @@
         location = null;
       }
       String sourceLine = getSourceLine(location, uriToSource);
-      return formatErrorMessage(
-          sourceLine, location, length, path, messageText);
+      return new PlainAndColorizedString(
+        formatErrorMessage(
+            sourceLine, location, length, path, messageTextPlain),
+        formatErrorMessage(
+            sourceLine, location, length, path, messageTextColorized),
+      );
     } else {
-      return messageText;
+      return new PlainAndColorizedString(
+        messageTextPlain,
+        messageTextColorized,
+      );
     }
   } catch (error, trace) {
     print("Crash when formatting: "
diff --git a/pkg/front_end/lib/src/fasta/compiler_context.dart b/pkg/front_end/lib/src/fasta/compiler_context.dart
index a1d2005..913a4e1 100644
--- a/pkg/front_end/lib/src/fasta/compiler_context.dart
+++ b/pkg/front_end/lib/src/fasta/compiler_context.dart
@@ -8,6 +8,7 @@
 
 import 'dart:async' show Zone, runZoned;
 
+import 'package:_fe_analyzer_shared/src/messages/codes.dart';
 import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
 
 import 'package:_fe_analyzer_shared/src/util/colors.dart' as colors;
@@ -79,16 +80,10 @@
   }
 
   /// Format [message] as a text string that can be included in generated code.
-  String format(LocatedMessage message, Severity severity) {
+  PlainAndColorizedString format(LocatedMessage message, Severity severity) {
     return command_line_reporting.format(message, severity);
   }
 
-  /// Format [message] as a text string that can be included in generated code.
-  // TODO(askesc): Remove this and direct callers directly to format.
-  String formatWithoutLocation(Message message, Severity severity) {
-    return command_line_reporting.format(message.withoutLocation(), severity);
-  }
-
   // TODO(ahe): Remove this.
   void logError(Object message, Severity severity) {
     errors.add(message);
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index edcd72b..e9cd910 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -5976,7 +5976,8 @@
           wasHandled: true, context: context);
     }
     String text = libraryBuilder.loader.target.context
-        .format(message.withLocation(uri, charOffset, length), Severity.error);
+        .format(message.withLocation(uri, charOffset, length), Severity.error)
+        .plain;
     InvalidExpression expression = new InvalidExpression(text)
       ..fileOffset = charOffset;
     return expression;
@@ -6451,7 +6452,8 @@
     addProblemErrorIfConst(message, charOffset, length,
         wasHandled: wasHandled, context: context);
     String text = libraryBuilder.loader.target.context
-        .format(message.withLocation(uri, charOffset, length), Severity.error);
+        .format(message.withLocation(uri, charOffset, length), Severity.error)
+        .plain;
     InvalidExpression expression = new InvalidExpression(text)
       ..fileOffset = charOffset;
     return expression;
diff --git a/pkg/front_end/test/lint_test.status b/pkg/front_end/test/lint_test.status
index 2728954..691adfa 100644
--- a/pkg/front_end/test/lint_test.status
+++ b/pkg/front_end/test/lint_test.status
@@ -14,6 +14,7 @@
 front_end/lib/src/api_prototype/front_end/Exports: Fail
 front_end/lib/src/api_prototype/incremental_kernel_generator/Exports: Fail
 front_end/lib/src/api_prototype/language_version/Exports: Fail
+front_end/lib/src/api_prototype/terminal_color_support/Exports: Fail
 front_end/lib/src/api_unstable/bazel_worker/ImportsTwice: Fail
 front_end/lib/src/fasta/fasta_codes/Exports: Fail
 front_end/lib/src/fasta/incremental_compiler/ImportsTwice: Fail
diff --git a/pkg/front_end/test/messages_json_test.dart b/pkg/front_end/test/messages_json_test.dart
index e8fe28a..7f87ca4 100644
--- a/pkg/front_end/test/messages_json_test.dart
+++ b/pkg/front_end/test/messages_json_test.dart
@@ -27,18 +27,34 @@
     LocatedMessage locatedMessage1 =
         new LocatedMessage(Uri.parse("what:ever/fun_1.dart"), 117, 2, message);
     FormattedMessage formattedMessage2 = new FormattedMessage(
-        null, "Formatted string #2", 13, 2, Severity.error, []);
+        null,
+        "Formatted string Plain #2",
+        "Formatted string Colorized #2",
+        13,
+        2,
+        Severity.error, []);
     FormattedMessage formattedMessage3 = new FormattedMessage(
-        null, "Formatted string #3", 313, 32, Severity.error, []);
+        null,
+        "Formatted string Plain #3",
+        "Formatted string Colorized #3",
+        313,
+        32,
+        Severity.error, []);
 
     FormattedMessage formattedMessage1 = new FormattedMessage(
-        locatedMessage1, "Formatted string", 42, 86, severity, [
+        locatedMessage1,
+        "Formatted string Plain",
+        "Formatted string Colorized",
+        42,
+        86,
+        severity, [
       formattedMessage2,
       formattedMessage3
-    ], involvedFiles: [
-      Uri.parse("what:ever/foo.dart"),
-      Uri.parse("what:ever/bar.dart")
-    ]);
+    ],
+        involvedFiles: [
+          Uri.parse("what:ever/foo.dart"),
+          Uri.parse("what:ever/bar.dart")
+        ]);
     expect(formattedMessage1.codeName, "MyCodeName");
 
     DiagnosticMessageFromJson diagnosticMessageFromJson =
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 626be69..e262498 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -210,6 +210,7 @@
 codec
 codes
 collision
+colorized
 com
 combinations
 combinator
diff --git a/pkg/front_end/test/static_types/cfe_allowed.json b/pkg/front_end/test/static_types/cfe_allowed.json
index 63088f0..ca079c1 100644
--- a/pkg/front_end/test/static_types/cfe_allowed.json
+++ b/pkg/front_end/test/static_types/cfe_allowed.json
@@ -1,7 +1,4 @@
 {
-  "pkg/front_end/lib/src/api_prototype/terminal_color_support.dart": {
-    "Dynamic invocation of 'split'.": 1
-  },
   "pkg/front_end/lib/src/base/libraries_specification.dart": {
     "Dynamic invocation of 'toList'.": 1,
     "Dynamic invocation of 'map'.": 1,
diff --git a/pkg/front_end/test/utils/validating_instrumentation.dart b/pkg/front_end/test/utils/validating_instrumentation.dart
index 184e7cb..1c1af83 100644
--- a/pkg/front_end/test/utils/validating_instrumentation.dart
+++ b/pkg/front_end/test/utils/validating_instrumentation.dart
@@ -228,11 +228,14 @@
 
   String _formatProblem(
       Uri uri, int offset, String desc, StackTrace stackTrace) {
-    return CompilerContext.current.format(
-        templateUnspecified
-            .withArguments('$desc${stackTrace == null ? '' : '\n$stackTrace'}')
-            .withLocation(uri, offset, noLength),
-        Severity.internalProblem);
+    return CompilerContext.current
+        .format(
+            templateUnspecified
+                .withArguments(
+                    '$desc${stackTrace == null ? '' : '\n$stackTrace'}')
+                .withLocation(uri, offset, noLength),
+            Severity.internalProblem)
+        .plain;
   }
 
   String _makeExpectationComment(String property, InstrumentationValue value) {
diff --git a/pkg/front_end/tool/_fasta/command_line.dart b/pkg/front_end/tool/_fasta/command_line.dart
index 754f7f2..e38ab78 100644
--- a/pkg/front_end/tool/_fasta/command_line.dart
+++ b/pkg/front_end/tool/_fasta/command_line.dart
@@ -24,6 +24,7 @@
 
 import 'package:front_end/src/api_prototype/standard_file_system.dart'
     show StandardFileSystem;
+import 'package:front_end/src/api_prototype/terminal_color_support.dart';
 import 'package:front_end/src/base/nnbd_mode.dart';
 
 import 'package:front_end/src/base/processed_options.dart'
@@ -39,9 +40,10 @@
 import 'package:front_end/src/fasta/fasta_codes.dart'
     show
         Message,
-        templateFastaCLIArgumentRequired,
+        PlainAndColorizedString,
         messageFastaUsageLong,
         messageFastaUsageShort,
+        templateFastaCLIArgumentRequired,
         templateUnspecified;
 
 import 'package:front_end/src/fasta/problems.dart' show DebugAbort;
@@ -440,10 +442,18 @@
     problem = e;
   }
 
-  return CompilerContext.runWithOptions<T>(options, (c) {
+  return CompilerContext.runWithOptions<T>(options, (CompilerContext c) {
     if (problem != null) {
       print(computeUsage(programName, options.verbose).message);
-      print(c.formatWithoutLocation(problem.message, Severity.error));
+      PlainAndColorizedString formatted =
+          c.format(problem.message.withoutLocation(), Severity.error);
+      String formattedText;
+      if (enableColors) {
+        formattedText = formatted.colorized;
+      } else {
+        formattedText = formatted.plain;
+      }
+      print(formattedText);
       exit(1);
     }
 
diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart
index 0fcaf18..e740e1a 100644
--- a/pkg/vm/bin/kernel_service.dart
+++ b/pkg/vm/bin/kernel_service.dart
@@ -93,7 +93,8 @@
     int nullSafety,
     List<String> experimentalFlags,
     Uri packagesUri,
-    List<String> errors,
+    List<String> errorsPlain,
+    List<String> errorsColorized,
     String invocationModes,
     String verbosityLevel) {
   final expFlags = <String>[];
@@ -113,8 +114,10 @@
     ..verbose = verbose
     ..omitPlatform = true
     ..explicitExperimentalFlags = parseExperimentalFlags(
-        parseExperimentalArguments(expFlags),
-        onError: (msg) => errors.add(msg))
+        parseExperimentalArguments(expFlags), onError: (msg) {
+      errorsPlain.add(msg);
+      errorsColorized.add(msg);
+    })
     ..environmentDefines = new EnvironmentMap()
     ..nnbdMode = (nullSafety == kNullSafetyOptionStrong)
         ? NnbdMode.Strong
@@ -128,7 +131,8 @@
           // TODO(sigmund): support emitting code with errors as long as they
           // are handled in the generated code.
           printToStdErr = false; // errors are printed by VM
-          errors.addAll(message.plainTextFormatted);
+          errorsPlain.addAll(message.plainTextFormatted);
+          errorsColorized.addAll(message.ansiFormatted);
           break;
         case Severity.warning:
           printToStdErr = true;
@@ -168,7 +172,8 @@
   final bool supportCodeCoverage;
   final bool supportHotReload;
 
-  final List<String> errors = <String>[];
+  final List<String> errorsPlain = <String>[];
+  final List<String> errorsColorized = <String>[];
 
   CompilerOptions options;
 
@@ -202,7 +207,8 @@
         nullSafety,
         experimentalFlags,
         packagesUri,
-        errors,
+        errorsPlain,
+        errorsColorized,
         invocationModes,
         verbosityLevel);
   }
@@ -212,7 +218,7 @@
       final CompilerResult compilerResult = await compileInternal(script);
       final Component component = compilerResult.component;
 
-      if (errors.isEmpty) {
+      if (errorsPlain.isEmpty) {
         // Record dependencies only if compilation was error free.
         _recordDependencies(isolateGroupId, component, options.packagesFileUri);
       }
@@ -333,7 +339,8 @@
     if (generator == null) {
       generator = new IncrementalCompiler(options, script);
     }
-    errors.clear();
+    errorsPlain.clear();
+    errorsColorized.clear();
     final component = await generator.compile(entryPoint: script);
     return new CompilerResult(component, const {},
         generator.getClassHierarchy(), generator.getCoreTypes());
@@ -609,7 +616,8 @@
     return;
   }
 
-  compiler.errors.clear();
+  compiler.errorsPlain.clear();
+  compiler.errorsColorized.clear();
 
   CompilationResult result;
   try {
@@ -622,10 +630,13 @@
       return;
     }
 
-    if (compiler.errors.isNotEmpty) {
+    assert(compiler.errorsPlain.length == compiler.errorsColorized.length);
+    // Any error will be printed verbatim in observatory, so we always use the
+    // plain version (i.e. the one without ANSI escape codes in it).
+    if (compiler.errorsPlain.isNotEmpty) {
       // TODO(sigmund): the compiler prints errors to the console, so we
       // shouldn't print those messages again here.
-      result = new CompilationResult.errors(compiler.errors, null);
+      result = new CompilationResult.errors(compiler.errorsPlain, null);
     } else {
       Component component = createExpressionEvaluationComponent(procedure);
       result = new CompilationResult.ok(serializeComponent(component));
@@ -811,7 +822,8 @@
       // resolve it against the working directory.
       packagesUri = Uri.directory(workingDirectory).resolveUri(packagesUri);
     }
-    final List<String> errors = <String>[];
+    final List<String> errorsPlain = <String>[];
+    final List<String> errorsColorized = <String>[];
     var options = setupCompilerOptions(
         fileSystem,
         platformKernelPath,
@@ -819,7 +831,8 @@
         nullSafety,
         experimentalFlags,
         packagesUri,
-        errors,
+        errorsPlain,
+        errorsColorized,
         invocationModes,
         verbosityLevel);
 
@@ -872,14 +885,17 @@
     CompilerResult compilerResult = await compiler.compile(script);
     Set<Library> loadedLibraries = compilerResult.loadedLibraries;
 
-    if (compiler.errors.isNotEmpty) {
+    assert(compiler.errorsPlain.length == compiler.errorsColorized.length);
+    final List<String> errors =
+        enableColors ? compiler.errorsColorized : compiler.errorsPlain;
+    if (errors.isNotEmpty) {
       if (compilerResult.component != null) {
         result = new CompilationResult.errors(
-            compiler.errors,
+            errors,
             serializeComponent(compilerResult.component,
                 filter: (lib) => !loadedLibraries.contains(lib)));
       } else {
-        result = new CompilationResult.errors(compiler.errors, null);
+        result = new CompilationResult.errors(errors, null);
       }
     } else {
       // We serialize the component excluding vm_platform.dill because the VM has
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 3d14826..ab707d3 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -37,6 +37,9 @@
 html/js_interop_constructor_name/*: SkipByDesign # Issue 42085.
 isolate/deferred_in_isolate2_test: Skip # Issue 16898. Deferred loading does not work from an isolate in CSP-mode
 js/instanceof_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
+js/js_util/async_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
+js/js_util/jsify_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
+js/js_util/properties_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
 js/method_call_on_object_test: SkipByDesign # Issue 42085.
 js/mock_test/*: SkipByDesign # Issue 42085.
 js/parameters_test: SkipByDesign # Issue 42085.
diff --git a/tests/lib_2/lib_2.status b/tests/lib_2/lib_2.status
index a5af78e..f338b66 100644
--- a/tests/lib_2/lib_2.status
+++ b/tests/lib_2/lib_2.status
@@ -37,6 +37,9 @@
 html/js_interop_constructor_name/*: SkipByDesign # Issue 42085.
 isolate/deferred_in_isolate2_test: Skip # Issue 16898. Deferred loading does not work from an isolate in CSP-mode
 js/instanceof_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
+js/js_util/async_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
+js/js_util/jsify_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
+js/js_util/properties_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
 js/method_call_on_object_test: SkipByDesign # Issue 42085.
 js/mock_test/*: SkipByDesign # Issue 42085.
 js/parameters_test: SkipByDesign # Issue 42085.
diff --git a/tools/VERSION b/tools/VERSION
index c50233a..ca15bdb 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 71
+PRERELEASE 72
 PRERELEASE_PATCH 0
\ No newline at end of file