Support data URIs in StandardFileSystem.

Closes https://github.com/dart-lang/sdk/issues/31594

Change-Id: I8dad1429d91cc5934b37cb0be60d1e4039293b71
Reviewed-on: https://dart-review.googlesource.com/33461
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/api_prototype/standard_file_system.dart b/pkg/front_end/lib/src/api_prototype/standard_file_system.dart
index fbe8155..c64cfd8 100644
--- a/pkg/front_end/lib/src/api_prototype/standard_file_system.dart
+++ b/pkg/front_end/lib/src/api_prototype/standard_file_system.dart
@@ -9,8 +9,10 @@
 
 import 'file_system.dart';
 
-/// Concrete implementation of [FileSystem] which performs its operations using
-/// I/O.
+/// Concrete implementation of [FileSystem] handling standard URI schemes.
+///
+/// file: URIs are handled using file I/O.
+/// data: URIs return their data contents.
 ///
 /// Not intended to be implemented or extended by clients.
 class StandardFileSystem implements FileSystem {
@@ -20,28 +22,31 @@
 
   @override
   FileSystemEntity entityForUri(Uri uri) {
-    if (uri.scheme != 'file' && uri.scheme != '') {
+    if (uri.scheme == 'file' || uri.scheme == '') {
+      // TODO(askesc): Empty schemes should have been handled elsewhere.
+      return new _IoFileSystemEntity(Uri.base.resolveUri(uri));
+    } else if (uri.scheme == 'data') {
+      return new _DataFileSystemEntity(Uri.base.resolveUri(uri));
+    } else {
       throw new FileSystemException(
-          uri, 'StandardFileSystem only supports file:* URIs');
+          uri, 'StandardFileSystem only supports file:* and data:* URIs');
     }
-    return new _StandardFileSystemEntity(Uri.base.resolveUri(uri));
   }
 }
 
-/// Concrete implementation of [FileSystemEntity] for use by
-/// [StandardFileSystem].
-class _StandardFileSystemEntity implements FileSystemEntity {
+/// Concrete implementation of [FileSystemEntity] for file: URIs.
+class _IoFileSystemEntity implements FileSystemEntity {
   @override
   final Uri uri;
 
-  _StandardFileSystemEntity(this.uri);
+  _IoFileSystemEntity(this.uri);
 
   @override
   int get hashCode => uri.hashCode;
 
   @override
   bool operator ==(Object other) =>
-      other is _StandardFileSystemEntity && other.uri == uri;
+      other is _IoFileSystemEntity && other.uri == uri;
 
   @override
   Future<bool> exists() async {
@@ -82,3 +87,33 @@
     return new FileSystemException(uri, message);
   }
 }
+
+/// Concrete implementation of [FileSystemEntity] for data: URIs.
+class _DataFileSystemEntity implements FileSystemEntity {
+  @override
+  final Uri uri;
+
+  _DataFileSystemEntity(this.uri);
+
+  @override
+  int get hashCode => uri.hashCode;
+
+  @override
+  bool operator ==(Object other) =>
+      other is _DataFileSystemEntity && other.uri == uri;
+
+  @override
+  Future<bool> exists() async {
+    return true;
+  }
+
+  @override
+  Future<List<int>> readAsBytes() async {
+    return uri.data.contentAsBytes();
+  }
+
+  @override
+  Future<String> readAsString() async {
+    return uri.data.contentAsString();
+  }
+}
diff --git a/pkg/front_end/lib/src/testing/hybrid_file_system.dart b/pkg/front_end/lib/src/testing/hybrid_file_system.dart
index 8c0828c..ba2a2fe 100644
--- a/pkg/front_end/lib/src/testing/hybrid_file_system.dart
+++ b/pkg/front_end/lib/src/testing/hybrid_file_system.dart
@@ -37,7 +37,8 @@
   Future<FileSystemEntity> get delegate async {
     if (_delegate != null) return _delegate;
     FileSystemEntity entity = _fs.memory.entityForUri(uri);
-    if (uri.scheme != 'file' || await entity.exists()) {
+    if ((uri.scheme != 'file' && uri.scheme != 'data') ||
+        await entity.exists()) {
       _delegate = entity;
       return _delegate;
     }
diff --git a/pkg/front_end/test/standard_file_system_test.dart b/pkg/front_end/test/standard_file_system_test.dart
index 80ffe8e..e78a3c7 100644
--- a/pkg/front_end/test/standard_file_system_test.dart
+++ b/pkg/front_end/test/standard_file_system_test.dart
@@ -8,6 +8,7 @@
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io' as io;
+import 'dart:math' show Random;
 
 import 'package:front_end/src/api_prototype/file_system.dart';
 import 'package:front_end/src/api_prototype/standard_file_system.dart';
@@ -20,6 +21,7 @@
     defineReflectiveTests(StandardFileSystemTest);
     defineReflectiveTests(FileTest);
     defineReflectiveTests(DirectoryTest);
+    defineReflectiveTests(DataTest);
   });
 }
 
@@ -248,3 +250,23 @@
     }
   }
 }
+
+@reflectiveTest
+class DataTest {
+  test_Data_URIs() async {
+    String string = "<{[DART]}>";
+    Uri string_uri = new Uri.dataFromString(string, base64: false);
+    Uri string_uri_base64 = new Uri.dataFromString(string, base64: true);
+
+    Random random = new Random(123);
+    List<int> bytes = new List.generate(1000, (index) => random.nextInt(256));
+    Uri bytes_uri = new Uri.dataFromBytes(bytes, percentEncoded: true);
+    Uri bytes_uri_base64 = new Uri.dataFromBytes(bytes, percentEncoded: false);
+
+    StandardFileSystem fs = StandardFileSystem.instance;
+    expect(string, await fs.entityForUri(string_uri).readAsString());
+    expect(string, await fs.entityForUri(string_uri_base64).readAsString());
+    expect(bytes, await fs.entityForUri(bytes_uri).readAsBytes());
+    expect(bytes, await fs.entityForUri(bytes_uri_base64).readAsBytes());
+  }
+}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index fc087b9a..d2ec7b8 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -296,12 +296,7 @@
 cc/SourceReport_PossibleBreakpoints_Simple: Fail
 cc/StackTraceFormat: Fail
 cc/UseDartApi: Fail
-dart/data_uri_import_test/base64: CompileTimeError
-dart/data_uri_import_test/nocharset: CompileTimeError
-dart/data_uri_import_test/nomime: CompileTimeError
-dart/data_uri_import_test/percentencoded: Fail
-dart/data_uri_import_test/wrongmime: CompileTimeError
-dart/data_uri_spawn_test: RuntimeError
+dart/data_uri_import_test/utf16: MissingRuntimeError # UTF-16 data URIs work in dartk
 dart/redirection_type_shuffling_test/00: Crash
 dart/redirection_type_shuffling_test/none: Crash
 dart/spawn_shutdown_test: SkipSlow
@@ -350,12 +345,7 @@
 dart/truncating_ints_test: Skip # This test cannot be run in dartkp/legacy mode (gen_kernel does not pass --limit-ints-to-64-bits in legacy mode).
 
 [ $compiler == dartkp && ($runtime == dart_precompiled || $runtime == vm) ]
-dart/data_uri_import_test/base64: CompileTimeError
-dart/data_uri_import_test/nocharset: CompileTimeError
-dart/data_uri_import_test/nomime: CompileTimeError
-dart/data_uri_import_test/percentencoded: CompileTimeError
-dart/data_uri_import_test/wrongmime: CompileTimeError
-dart/data_uri_spawn_test: RuntimeError
+dart/data_uri_import_test/utf16: MissingRuntimeError # UTF-16 data URIs work in dartk
 dart/redirection_type_shuffling_test: SkipByDesign # Includes dart:mirrors.
 dart/spawn_shutdown_test: SkipSlow