Version 2.12.0-194.0.dev

Merge commit 'cd2258572de9feeacc423eaaddf62cf5ff69e3b9' into 'dev'
diff --git a/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart b/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
index d168cee..3e2a88f 100644
--- a/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
+++ b/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
@@ -135,7 +135,7 @@
     "kind": "CLASS",
     "name": "A",
     "location": {
-      "file": "/home/test/lib/a.dart",
+      "file": ${jsonOfPath(a_path)},
       "offset": 21,
       "length": 0,
       "startLine": 2,
@@ -160,7 +160,7 @@
     "kind": "CLASS",
     "name": "B",
     "location": {
-      "file": "/home/test/lib/b.dart",
+      "file": ${jsonOfPath(b_path)},
       "offset": 24,
       "length": 0,
       "startLine": 2,
diff --git a/pkg/dev_compiler/lib/js/legacy/dart_library.js b/pkg/dev_compiler/lib/js/legacy/dart_library.js
index 609945c..3c8be22 100644
--- a/pkg/dev_compiler/lib/js/legacy/dart_library.js
+++ b/pkg/dev_compiler/lib/js/legacy/dart_library.js
@@ -17,8 +17,10 @@
   function throwLibraryError(message) {
     // Dispatch event to allow others to react to the load error without
     // capturing the exception.
-    window.dispatchEvent(
-      new CustomEvent('dartLoadException', { detail: message }));
+    if (!!self.dispatchEvent) {
+      self.dispatchEvent(
+        new CustomEvent('dartLoadException', { detail: message }));
+    }
     throw Error(message);
   }
 
@@ -231,7 +233,7 @@
     // patch the code for the application while it is executing
     // (https://flutter.io/hot-reload/), whereas "hot restart" refers to what
     // dartdevc supports: tear down the app, update the code, and rerun the app.
-    if (!window || !window.$dartWarmReload) {
+    if (!self || !self.$dartWarmReload) {
       console.warn('Hot restart not supported in this environment.');
       return;
     }
@@ -246,14 +248,14 @@
 
     /// Once the `onReloadStart()` completes, this finishes the restart.
     function finishHotRestart() {
-      window.console.clear();
+      self.console.clear();
       if (clearState) {
         // This resets all initialized fields and clears type caches and other
         // temporary data structures used by the compiler/SDK.
         sdk.dart.hotRestart();
       }
       // Call the module loader to reload the necessary modules.
-      window.$dartWarmReload(() => {
+      self.$dartWarmReload(() => {
         // Once the modules are loaded, rerun `main()`.
         start(_lastModuleName, _lastLibraryName, true);
       });
@@ -293,15 +295,21 @@
       if (library.onReloadEnd) {
         library.onReloadEnd();
         return;
-      } else { 
-        if (dart_sdk.dart.global.document) {
-          dart_sdk.dart.global.document.body = _originalBody;
+      } else {
+        if (!!self.document) {
+          // Note: we expect _originalBody to be undefined in non-browser
+          // environments, but in that case so is the body.
+          if (!_originalBody && !!self.document.body) {
+            self.console.warn('No body saved to update on reload');
+          } else {
+            self.document.body = _originalBody;
+          }
         }
       }
     } else {
       // If not a reload then store the initial html to reset it on reload.
-      if (dart_sdk.dart.global.document) {
-        _originalBody = dart_sdk.dart.global.document.body.cloneNode(true);
+      if (!!self.document && !!self.document.body) {
+        _originalBody = self.document.body.cloneNode(true);
       }
     }
     library.main([]);
diff --git a/pkg/smith/lib/configuration.dart b/pkg/smith/lib/configuration.dart
index 846d39d..370e772 100644
--- a/pkg/smith/lib/configuration.dart
+++ b/pkg/smith/lib/configuration.dart
@@ -663,10 +663,9 @@
 
       case Compiler.dartdevc:
       case Compiler.dartdevk:
-        // TODO(rnystrom): Expand to support other JS execution environments
-        // (other browsers, d8) when tested and working.
         return const [
           Runtime.none,
+          Runtime.d8,
           Runtime.chrome,
           Runtime.edge,
           Runtime.firefox,
diff --git a/pkg/test_runner/lib/src/compiler_configuration.dart b/pkg/test_runner/lib/src/compiler_configuration.dart
index d92797d..b6e07e5 100644
--- a/pkg/test_runner/lib/src/compiler_configuration.dart
+++ b/pkg/test_runner/lib/src/compiler_configuration.dart
@@ -557,29 +557,36 @@
     args.addAll(options);
     args.addAll(_configuration.sharedOptions);
 
+    bool d8Runtime = _configuration.runtime == Runtime.d8;
+
     args.addAll([
       "--ignore-unrecognized-flags",
       "--no-summarize",
+      if (d8Runtime) "--modules=legacy",
       "-o",
       outputFile,
       inputFile,
     ]);
 
-    // Link to the summaries for the available packages, so that they don't
-    // get recompiled into the test's own module.
-    var packageSummaryDir =
-        _configuration.nnbdMode == NnbdMode.strong ? 'pkg_sound' : 'pkg_kernel';
-    for (var package in testPackages) {
-      args.add("-s");
+    if (!d8Runtime) {
+      // TODO(sigmund): allow caching of shared packages in legacy mode too.
+      // Link to the summaries for the available packages, so that they don't
+      // get recompiled into the test's own module.
+      var packageSummaryDir = _configuration.nnbdMode == NnbdMode.strong
+          ? 'pkg_sound'
+          : 'pkg_kernel';
+      for (var package in testPackages) {
+        args.add("-s");
 
-      // Since the summaries for the packages are not near the tests, we give
-      // dartdevc explicit module paths for each one. When the test is run, we
-      // will tell require.js where to find each package's compiled JS.
-      var summary = Path(_configuration.buildDirectory)
-          .append("/gen/utils/dartdevc/$packageSummaryDir/$package.dill")
-          .absolute
-          .toNativePath();
-      args.add("$summary=$package");
+        // Since the summaries for the packages are not near the tests, we give
+        // dartdevc explicit module paths for each one. When the test is run, we
+        // will tell require.js where to find each package's compiled JS.
+        var summary = Path(_configuration.buildDirectory)
+            .append("/gen/utils/dartdevc/$packageSummaryDir/$package.dill")
+            .absolute
+            .toNativePath();
+        args.add("$summary=$package");
+      }
     }
 
     var inputDir = Path(inputFile).append("..").canonicalize().toNativePath();
@@ -598,12 +605,80 @@
     // computeCompilerArguments() to here seems hacky. Is there a cleaner way?
     var sharedOptions = arguments.sublist(0, arguments.length - 1);
     var inputFile = arguments.last;
-    var inputFilename = Uri.file(inputFile).pathSegments.last;
-    var outputFile = "$tempDir/${inputFilename.replaceAll('.dart', '.js')}";
+    var inputUri = Uri.file(inputFile);
+    var inputFilename = inputUri.pathSegments.last;
+    var moduleName =
+        inputFilename.substring(0, inputFilename.length - ".dart".length);
+    var outputFile = "$tempDir/$moduleName.js";
+    var runFile = outputFile;
+
+    if (_configuration.runtime == Runtime.d8) {
+      // TODO(sigmund): ddc should have a flag to emit an entrypoint file like
+      // the one below, otherwise it is succeptible to break, for example, if
+      // library naming conventions were to change in the future.
+      runFile = "$tempDir/$moduleName.d8.js";
+      var nonNullAsserts = arguments.contains('--null-assertions');
+      var nativeNonNullAsserts = arguments.contains('--native-null-assertions');
+      var weakNullSafetyErrors =
+          arguments.contains('--weak-null-safety-errors');
+      var soundNullSafety = _configuration.nnbdMode == NnbdMode.strong;
+      var weakNullSafetyWarnings = !(weakNullSafetyErrors || soundNullSafety);
+      var repositoryUri = Uri.directory(Repository.dir.toNativePath());
+      var dartLibraryPath = repositoryUri
+          .resolve('pkg/dev_compiler/lib/js/legacy/dart_library.js')
+          .path;
+      var sdkJsDir = Uri.directory(_configuration.buildDirectory)
+          .resolve('gen/utils/dartdevc/');
+      var sdkJsPath = soundNullSafety
+          ? 'sound/legacy/dart_sdk.js'
+          : 'kernel/legacy/dart_sdk.js';
+      var libraryName = inputUri.path
+          .substring(repositoryUri.path.length)
+          .replaceAll("/", "__")
+          .replaceAll("-", "_")
+          .replaceAll(".dart", "");
+
+      // Note: this assumes that d8 is invoked with the dart2js d8.js preamble.
+      // TODO(sigmund): to support other runtimes like js-shell, we may want to
+      // remove the `load` statements here and instead provide those files
+      // through the runtime command-line arguments.
+      File(runFile).writeAsStringSync('''
+        load("$dartLibraryPath");
+        load("$sdkJsDir/$sdkJsPath");
+        load("$outputFile");
+
+        let sdk = dart_library.import("dart_sdk");
+        sdk.dart.weakNullSafetyWarnings($weakNullSafetyWarnings);
+        sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
+        sdk.dart.nonNullAsserts($nonNullAsserts);
+        sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
+
+        // Invoke main through the d8 preamble to ensure the code is running
+        // within the fake event loop.
+        self.dartMainRunner(function () {
+          dart_library.start("$moduleName", "$libraryName");
+        });
+      '''
+          .replaceAll("\n        ", "\n"));
+    }
 
     return CommandArtifact([
       _createCommand(inputFile, outputFile, sharedOptions, environmentOverrides)
-    ], outputFile, "application/javascript");
+    ], runFile, "application/javascript");
+  }
+
+  List<String> computeRuntimeArguments(
+      RuntimeConfiguration runtimeConfiguration,
+      TestFile testFile,
+      List<String> vmOptions,
+      List<String> originalArguments,
+      CommandArtifact artifact) {
+    var sdkDir = _useSdk
+        ? Uri.directory(_configuration.buildDirectory).resolve('dart-sdk/')
+        : Uri.directory(Repository.dir.toNativePath()).resolve('sdk/');
+    var preambleDir = sdkDir.resolve('lib/_internal/js_runtime/lib/preambles/');
+    return runtimeConfiguration.dart2jsPreambles(preambleDir)
+      ..add(artifact.filename);
   }
 }
 
diff --git a/pkg/test_runner/lib/src/test_suite.dart b/pkg/test_runner/lib/src/test_suite.dart
index 6f5d5f2..9c1baf2 100644
--- a/pkg/test_runner/lib/src/test_suite.dart
+++ b/pkg/test_runner/lib/src/test_suite.dart
@@ -234,8 +234,11 @@
     var minified = configuration.isMinified ? '-minified' : '';
     var csp = configuration.isCsp ? '-csp' : '';
     var sdk = configuration.useSdk ? '-sdk' : '';
+    var isLegacyModule = configuration.compiler == Compiler.dartdevk &&
+        configuration.runtime == Runtime.d8;
+    var module = isLegacyModule ? '-legacy' : '';
     var dirName = "${configuration.compiler.name}"
-        "$checked$minified$csp$sdk";
+        "$checked$minified$csp$sdk$module";
     return createGeneratedTestDirectoryHelper(
         "compilations", dirName, testPath);
   }
diff --git a/tests/language/const/constant_dag_test.dart b/tests/language/const/constant_dag_test.dart
new file mode 100644
index 0000000..7e14ebb
--- /dev/null
+++ b/tests/language/const/constant_dag_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+
+// Test the efficient processing of constants that are DAGs.
+//
+// This test requires processing a constant that is a DAG of N nodes (N=40). If
+// the DAG is traversed as a tree, it will require exponential node visits and
+// take time O(2^N) i.e. 2^40, and the test will time out.
+
+main() {
+  Expect.equals(40, n40.lengthA);
+  Expect.equals(40, n40.lengthB);
+}
+
+class Node {
+  final Node? a;
+  final Node? b;
+  const Node(this.a, this.b);
+
+  int get lengthA => a == null ? 0 : 1 + a!.lengthA;
+  int get lengthB => a == null ? 0 : 1 + b!.lengthB;
+}
+
+const n0 = Node(null, null);
+const n1 = Node(n0, n0);
+const n2 = Node(n1, n1);
+const n3 = Node(n2, n2);
+const n4 = Node(n3, n3);
+const n5 = Node(n4, n4);
+const n6 = Node(n5, n5);
+const n7 = Node(n6, n6);
+const n8 = Node(n7, n7);
+const n9 = Node(n8, n8);
+const n10 = Node(n9, n9);
+const n11 = Node(n10, n10);
+const n12 = Node(n11, n11);
+const n13 = Node(n12, n12);
+const n14 = Node(n13, n13);
+const n15 = Node(n14, n14);
+const n16 = Node(n15, n15);
+const n17 = Node(n16, n16);
+const n18 = Node(n17, n17);
+const n19 = Node(n18, n18);
+const n20 = Node(n19, n19);
+const n21 = Node(n20, n20);
+const n22 = Node(n21, n21);
+const n23 = Node(n22, n22);
+const n24 = Node(n23, n23);
+const n25 = Node(n24, n24);
+const n26 = Node(n25, n25);
+const n27 = Node(n26, n26);
+const n28 = Node(n27, n27);
+const n29 = Node(n28, n28);
+const n30 = Node(n29, n29);
+const n31 = Node(n30, n30);
+const n32 = Node(n31, n31);
+const n33 = Node(n32, n32);
+const n34 = Node(n33, n33);
+const n35 = Node(n34, n34);
+const n36 = Node(n35, n35);
+const n37 = Node(n36, n36);
+const n38 = Node(n37, n37);
+const n39 = Node(n38, n38);
+const n40 = Node(n39, n39);
diff --git a/tests/language_2/const/constant_dag_test.dart b/tests/language_2/const/constant_dag_test.dart
new file mode 100644
index 0000000..3be6889
--- /dev/null
+++ b/tests/language_2/const/constant_dag_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+
+// Test the efficient processing of constants that are DAGs.
+//
+// This test requires processing a constant that is a DAG of N nodes (N=40). If
+// the DAG is traversed as a tree, it will require exponential node visits and
+// take time O(2^N) i.e. 2^40, and the test will time out.
+
+main() {
+  Expect.equals(40, n40.lengthA);
+  Expect.equals(40, n40.lengthB);
+}
+
+class Node {
+  final Node a;
+  final Node b;
+  const Node(this.a, this.b);
+
+  int get lengthA => a == null ? 0 : 1 + a.lengthA;
+  int get lengthB => b == null ? 0 : 1 + b.lengthB;
+}
+
+const n0 = Node(null, null);
+const n1 = Node(n0, n0);
+const n2 = Node(n1, n1);
+const n3 = Node(n2, n2);
+const n4 = Node(n3, n3);
+const n5 = Node(n4, n4);
+const n6 = Node(n5, n5);
+const n7 = Node(n6, n6);
+const n8 = Node(n7, n7);
+const n9 = Node(n8, n8);
+const n10 = Node(n9, n9);
+const n11 = Node(n10, n10);
+const n12 = Node(n11, n11);
+const n13 = Node(n12, n12);
+const n14 = Node(n13, n13);
+const n15 = Node(n14, n14);
+const n16 = Node(n15, n15);
+const n17 = Node(n16, n16);
+const n18 = Node(n17, n17);
+const n19 = Node(n18, n18);
+const n20 = Node(n19, n19);
+const n21 = Node(n20, n20);
+const n22 = Node(n21, n21);
+const n23 = Node(n22, n22);
+const n24 = Node(n23, n23);
+const n25 = Node(n24, n24);
+const n26 = Node(n25, n25);
+const n27 = Node(n26, n26);
+const n28 = Node(n27, n27);
+const n29 = Node(n28, n28);
+const n30 = Node(n29, n29);
+const n31 = Node(n30, n30);
+const n32 = Node(n31, n31);
+const n33 = Node(n32, n32);
+const n34 = Node(n33, n33);
+const n35 = Node(n34, n34);
+const n36 = Node(n35, n35);
+const n37 = Node(n36, n36);
+const n38 = Node(n37, n37);
+const n39 = Node(n38, n38);
+const n40 = Node(n39, n39);
diff --git a/tools/VERSION b/tools/VERSION
index ef75a34..771533f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 193
+PRERELEASE 194
 PRERELEASE_PATCH 0
\ No newline at end of file