Version 2.17.0-108.0.dev

Merge commit '447e802aaa653223903fe0b2a0dd00392e830072' into 'dev'
diff --git a/pkg/compiler/README.md b/pkg/compiler/README.md
index a0c6462..bf2c604 100644
--- a/pkg/compiler/README.md
+++ b/pkg/compiler/README.md
@@ -265,21 +265,7 @@
 
 * `lib/src/compiler.dart`: defines the core `Compiler` object, which contains
   all the logic about what the compiler pipeline does and how data is organized
-  and communicated between different phases. For a long time, `Compiler` was
-  also used throughout the system as a global dependency-injection object.
-  We've been slowly disentangling those dependencies, but there are still many
-  references to `compiler` still in use.
-
-* `lib/src/apiimpl.dart`: defines `CompilerImpl` a subclass of `Compiler` that
-  adds support for loading scripts, resolving package URIs and patch files. The
-  separation here is a bit historical and we should be able to remove it. It was
-  added to make it easier to create a `MockCompiler` implementation for unit
-  testing. The `MockCompiler` has been replaced in most unit tests by a regular
-  `CompilerImpl` that uses a mock of the file-system (see
-  `pkg/compiler/test/memory_compiler.dart`).
-
-  AI: Once all tests are migrated to this memory compiler, we should merge
-  `Compiler` and `CompilerImpl` and remove this file.
+  and communicated between different phases.
 
 * `lib/src/closure.dart`: closures are compiled as classes, this file has the
   logic to do this kind of conversion in the Dart element model. This includes
diff --git a/pkg/compiler/lib/compiler.dart b/pkg/compiler/lib/compiler.dart
index c90854f..b9ade95 100644
--- a/pkg/compiler/lib/compiler.dart
+++ b/pkg/compiler/lib/compiler.dart
@@ -8,7 +8,7 @@
 
 import 'package:front_end/src/api_unstable/dart2js.dart' as fe;
 
-import 'src/apiimpl.dart';
+import 'src/compiler.dart';
 import 'src/options.dart' show CompilerOptions;
 
 // Unless explicitly allowed, passing [:null:] for any argument to the
@@ -242,7 +242,7 @@
     throw new ArgumentError("compilerOutput must be non-null");
   }
 
-  CompilerImpl compiler = new CompilerImpl(
+  var compiler = Compiler(
       compilerInput, compilerOutput, compilerDiagnostics, compilerOptions);
   return compiler.run().then((bool success) {
     return new CompilationResult(compiler,
diff --git a/pkg/compiler/lib/src/apiimpl.dart b/pkg/compiler/lib/src/apiimpl.dart
deleted file mode 100644
index 3759604..0000000
--- a/pkg/compiler/lib/src/apiimpl.dart
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright (c) 2012, 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.
-
-library leg_apiimpl;
-
-import 'dart:async';
-
-import '../compiler.dart' as api;
-import 'common/metrics.dart' show Metrics, Metric;
-import 'common/tasks.dart' show GenericTask, Measurer;
-import 'common.dart';
-import 'compiler.dart';
-import 'diagnostics/messages.dart' show Message;
-import 'environment.dart';
-import 'options.dart' show CompilerOptions;
-
-/// Implements the [Compiler] using a [api.CompilerInput] for supplying the
-/// sources.
-class CompilerImpl extends Compiler {
-  @override
-  final Measurer measurer;
-  @override
-  api.CompilerInput provider;
-  api.CompilerDiagnostics handler;
-
-  GenericTask userHandlerTask;
-  GenericTask userProviderTask;
-
-  CompilerImpl(this.provider, api.CompilerOutput outputProvider, this.handler,
-      CompilerOptions options,
-      {MakeReporterFunction makeReporter})
-      // NOTE: allocating measurer is done upfront to ensure the wallclock is
-      // started before other computations.
-      : measurer = Measurer(enableTaskMeasurements: options.verbose),
-        super(
-            options: options,
-            outputProvider: outputProvider,
-            environment: _Environment(options.environment),
-            makeReporter: makeReporter) {
-    tasks.addAll([
-      userHandlerTask = GenericTask('Diagnostic handler', measurer),
-      userProviderTask = GenericTask('Input provider', measurer),
-    ]);
-  }
-
-  void logInfo(String message) {
-    callUserHandler(null, null, null, null, message, api.Diagnostic.INFO);
-  }
-
-  void logVerbose(String message) {
-    callUserHandler(
-        null, null, null, null, message, api.Diagnostic.VERBOSE_INFO);
-  }
-
-  @override
-  Future<bool> run() {
-    Duration setupDuration = measurer.elapsedWallClock;
-    return selfTask.measureSubtask("impl.run", () async {
-      bool success = await super.run();
-      if (options.verbose) {
-        StringBuffer timings = StringBuffer();
-        computeTimings(setupDuration, timings);
-        logVerbose('$timings');
-      }
-      if (options.reportPrimaryMetrics || options.reportSecondaryMetrics) {
-        StringBuffer metrics = StringBuffer();
-        collectMetrics(metrics);
-        logInfo('$metrics');
-      }
-      return success;
-    });
-  }
-
-  String _formatMs(int ms) {
-    return (ms / 1000).toStringAsFixed(3) + 's';
-  }
-
-  void computeTimings(Duration setupDuration, StringBuffer timings) {
-    timings.writeln("Timings:");
-    Duration totalDuration = measurer.elapsedWallClock;
-    Duration asyncDuration = measurer.elapsedAsyncWallClock;
-    Duration cumulatedDuration = Duration.zero;
-    List<_TimingData> timingData = [];
-    for (final task in tasks) {
-      String running = task.isRunning ? "*" : " ";
-      Duration duration = task.duration;
-      if (duration != Duration.zero) {
-        cumulatedDuration += duration;
-        int milliseconds = duration.inMilliseconds;
-        timingData.add(_TimingData('   $running${task.name}:', milliseconds,
-            milliseconds * 100 / totalDuration.inMilliseconds));
-        for (String subtask in task.subtasks) {
-          int subtime = task.getSubtaskTime(subtask);
-          String running = task.getSubtaskIsRunning(subtask) ? "*" : " ";
-          timingData.add(_TimingData('   $running${task.name} > $subtask:',
-              subtime, subtime * 100 / totalDuration.inMilliseconds));
-        }
-      }
-    }
-    int longestDescription = timingData
-        .map((d) => d.description.length)
-        .fold(0, (a, b) => a < b ? b : a);
-    for (var data in timingData) {
-      var ms = _formatMs(data.milliseconds);
-      var padding =
-          " " * (longestDescription + 10 - data.description.length - ms.length);
-      var percentPadding = data.percent < 10 ? " " : "";
-      timings.writeln('${data.description}$padding $ms '
-          '$percentPadding(${data.percent.toStringAsFixed(1)}%)');
-    }
-    Duration unaccountedDuration =
-        totalDuration - cumulatedDuration - setupDuration - asyncDuration;
-    double percent =
-        unaccountedDuration.inMilliseconds * 100 / totalDuration.inMilliseconds;
-    timings.write(
-        '    Total compile-time ${_formatMs(totalDuration.inMilliseconds)};'
-        ' setup ${_formatMs(setupDuration.inMilliseconds)};'
-        ' async ${_formatMs(asyncDuration.inMilliseconds)};'
-        ' unaccounted ${_formatMs(unaccountedDuration.inMilliseconds)}'
-        ' (${percent.toStringAsFixed(2)}%)');
-  }
-
-  void collectMetrics(StringBuffer buffer) {
-    buffer.writeln('Metrics:');
-    for (final task in tasks) {
-      Metrics metrics = task.metrics;
-      String namespace = metrics.namespace;
-      if (namespace == '') {
-        namespace =
-            task.name.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]+'), '_');
-      }
-      void report(Metric metric) {
-        buffer
-            .writeln('  ${namespace}.${metric.name}: ${metric.formatValue()}');
-      }
-
-      for (final metric in metrics.primary) {
-        report(metric);
-      }
-      if (options.reportSecondaryMetrics) {
-        for (final metric in metrics.secondary) {
-          report(metric);
-        }
-      }
-    }
-  }
-
-  @override
-  void reportDiagnostic(DiagnosticMessage message,
-      List<DiagnosticMessage> infos, api.Diagnostic kind) {
-    _reportDiagnosticMessage(message, kind);
-    for (DiagnosticMessage info in infos) {
-      _reportDiagnosticMessage(info, api.Diagnostic.INFO);
-    }
-  }
-
-  void _reportDiagnosticMessage(
-      DiagnosticMessage diagnosticMessage, api.Diagnostic kind) {
-    // [:span.uri:] might be [:null:] in case of a [Script] with no [uri]. For
-    // instance in the [Types] constructor in typechecker.dart.
-    SourceSpan span = diagnosticMessage.sourceSpan;
-    Message message = diagnosticMessage.message;
-    if (span == null || span.uri == null) {
-      callUserHandler(message, null, null, null, '$message', kind);
-    } else {
-      callUserHandler(
-          message, span.uri, span.begin, span.end, '$message', kind);
-    }
-  }
-
-  void callUserHandler(Message message, Uri uri, int begin, int end,
-      String text, api.Diagnostic kind) {
-    try {
-      userHandlerTask.measure(() {
-        handler.report(message, uri, begin, end, text, kind);
-      });
-    } catch (ex, s) {
-      reportCrashInUserCode('Uncaught exception in diagnostic handler', ex, s);
-      rethrow;
-    }
-  }
-
-  Future<api.Input> callUserProvider(Uri uri, api.InputKind inputKind) {
-    try {
-      return userProviderTask
-          .measureIo(() => provider.readFromUri(uri, inputKind: inputKind));
-    } catch (ex, s) {
-      reportCrashInUserCode('Uncaught exception in input provider', ex, s);
-      rethrow;
-    }
-  }
-}
-
-class _Environment implements Environment {
-  final Map<String, String> definitions;
-  Map<String, String> _completeMap;
-
-  _Environment(this.definitions);
-
-  @override
-  Map<String, String> toMap() {
-    if (_completeMap == null) {
-      _completeMap = Map<String, String>.from(definitions);
-    }
-    return _completeMap;
-  }
-}
-
-class _TimingData {
-  final String description;
-  final int milliseconds;
-  final double percent;
-
-  _TimingData(this.description, this.milliseconds, this.percent);
-}
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index ad4f8c8..271c7c6 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -16,6 +16,7 @@
 import 'common.dart';
 import 'common/codegen.dart';
 import 'common/elements.dart' show ElementEnvironment;
+import 'common/metrics.dart' show Metric;
 import 'common/names.dart' show Selectors;
 import 'common/tasks.dart' show CompilerTask, GenericTask, Measurer;
 import 'common/work.dart' show WorkItem;
@@ -63,15 +64,19 @@
 typedef MakeReporterFunction = CompilerDiagnosticReporter Function(
     Compiler compiler, CompilerOptions options);
 
-abstract class Compiler {
-  Measurer get measurer;
-
-  api.CompilerInput get provider;
+/// Implementation of the compiler using  a [api.CompilerInput] for supplying
+/// the sources.
+class Compiler {
+  final Measurer measurer;
+  final api.CompilerInput provider;
+  final api.CompilerDiagnostics handler;
 
   FrontendStrategy frontendStrategy;
   BackendStrategy backendStrategy;
   CompilerDiagnosticReporter _reporter;
   Map<Entity, WorldImpact> _impactCache;
+  GenericTask userHandlerTask;
+  GenericTask userProviderTask;
 
   /// Options provided from command-line arguments.
   final CompilerOptions options;
@@ -133,12 +138,12 @@
   // Callback function used for testing codegen enqueuing.
   void Function() onCodegenQueueEmptyForTesting;
 
-  Compiler(
-      {CompilerOptions options,
-      api.CompilerOutput outputProvider,
-      this.environment = const _EmptyEnvironment(),
-      MakeReporterFunction makeReporter})
-      : this.options = options {
+  Compiler(this.provider, this._outputProvider, this.handler, this.options,
+      {MakeReporterFunction makeReporter})
+      // NOTE: allocating measurer is done upfront to ensure the wallclock is
+      // started before other computations.
+      : measurer = Measurer(enableTaskMeasurements: options.verbose),
+        this.environment = Environment(options.environment) {
     options.deriveOptions();
     options.validate();
 
@@ -184,9 +189,10 @@
       selfTask,
       serializationTask = SerializationTask(
           options, reporter, provider, outputProvider, measurer),
+      ...backendStrategy.tasks,
+      userHandlerTask = GenericTask('Diagnostic handler', measurer),
+      userProviderTask = GenericTask('Input provider', measurer)
     ];
-
-    tasks.addAll(backendStrategy.tasks);
   }
 
   /// Creates the backend strategy.
@@ -220,8 +226,8 @@
   // succeeded.
   Future<bool> run() => selfTask.measureSubtask("run", () {
         measurer.startWallClock();
-
-        return Future.sync(() => runInternal())
+        var setupDuration = measurer.elapsedWallClock;
+        var success = Future.sync(() => runInternal())
             .catchError((error, StackTrace stackTrace) =>
                 _reporter.onError(options.compilationTarget, error, stackTrace))
             .whenComplete(() {
@@ -229,6 +235,17 @@
         }).then((_) {
           return !compilationFailed;
         });
+        if (options.verbose) {
+          var timings = StringBuffer();
+          computeTimings(setupDuration, timings);
+          logVerbose('$timings');
+        }
+        if (options.reportPrimaryMetrics || options.reportSecondaryMetrics) {
+          var metrics = StringBuffer();
+          collectMetrics(metrics);
+          logInfo('$metrics');
+        }
+        return success;
       });
 
   bool get onlyPerformGlobalTypeInference {
@@ -640,7 +657,48 @@
   }
 
   void reportDiagnostic(DiagnosticMessage message,
-      List<DiagnosticMessage> infos, api.Diagnostic kind);
+      List<DiagnosticMessage> infos, api.Diagnostic kind) {
+    _reportDiagnosticMessage(message, kind);
+    for (DiagnosticMessage info in infos) {
+      _reportDiagnosticMessage(info, api.Diagnostic.INFO);
+    }
+  }
+
+  void _reportDiagnosticMessage(
+      DiagnosticMessage diagnosticMessage, api.Diagnostic kind) {
+    // [:span.uri:] might be [:null:] in case of a [Script] with no [uri]. For
+    // instance in the [Types] constructor in typechecker.dart.
+    var span = diagnosticMessage.sourceSpan;
+    var message = diagnosticMessage.message;
+    if (span == null || span.uri == null) {
+      callUserHandler(message, null, null, null, '$message', kind);
+    } else {
+      callUserHandler(
+          message, span.uri, span.begin, span.end, '$message', kind);
+    }
+  }
+
+  void callUserHandler(Message message, Uri uri, int begin, int end,
+      String text, api.Diagnostic kind) {
+    try {
+      userHandlerTask.measure(() {
+        handler.report(message, uri, begin, end, text, kind);
+      });
+    } catch (ex, s) {
+      reportCrashInUserCode('Uncaught exception in diagnostic handler', ex, s);
+      rethrow;
+    }
+  }
+
+  Future<api.Input> callUserProvider(Uri uri, api.InputKind inputKind) {
+    try {
+      return userProviderTask
+          .measureIo(() => provider.readFromUri(uri, inputKind: inputKind));
+    } catch (ex, s) {
+      reportCrashInUserCode('Uncaught exception in input provider', ex, s);
+      rethrow;
+    }
+  }
 
   void reportCrashInUserCode(String message, exception, stackTrace) {
     reporter.onCrashInUserCode(message, exception, stackTrace);
@@ -733,6 +791,89 @@
     }
     return null;
   }
+
+  void logInfo(String message) {
+    callUserHandler(null, null, null, null, message, api.Diagnostic.INFO);
+  }
+
+  void logVerbose(String message) {
+    callUserHandler(
+        null, null, null, null, message, api.Diagnostic.VERBOSE_INFO);
+  }
+
+  String _formatMs(int ms) {
+    return (ms / 1000).toStringAsFixed(3) + 's';
+  }
+
+  void computeTimings(Duration setupDuration, StringBuffer timings) {
+    timings.writeln("Timings:");
+    var totalDuration = measurer.elapsedWallClock;
+    var asyncDuration = measurer.elapsedAsyncWallClock;
+    var cumulatedDuration = Duration.zero;
+    var timingData = <_TimingData>[];
+    for (final task in tasks) {
+      var running = task.isRunning ? "*" : " ";
+      var duration = task.duration;
+      if (duration != Duration.zero) {
+        cumulatedDuration += duration;
+        var milliseconds = duration.inMilliseconds;
+        timingData.add(_TimingData('   $running${task.name}:', milliseconds,
+            milliseconds * 100 / totalDuration.inMilliseconds));
+        for (String subtask in task.subtasks) {
+          var subtime = task.getSubtaskTime(subtask);
+          var running = task.getSubtaskIsRunning(subtask) ? "*" : " ";
+          timingData.add(_TimingData('   $running${task.name} > $subtask:',
+              subtime, subtime * 100 / totalDuration.inMilliseconds));
+        }
+      }
+    }
+    int longestDescription = timingData
+        .map((d) => d.description.length)
+        .fold(0, (a, b) => a < b ? b : a);
+    for (var data in timingData) {
+      var ms = _formatMs(data.milliseconds);
+      var padding =
+          " " * (longestDescription + 10 - data.description.length - ms.length);
+      var percentPadding = data.percent < 10 ? " " : "";
+      timings.writeln('${data.description}$padding $ms '
+          '$percentPadding(${data.percent.toStringAsFixed(1)}%)');
+    }
+    var unaccountedDuration =
+        totalDuration - cumulatedDuration - setupDuration - asyncDuration;
+    var percent =
+        unaccountedDuration.inMilliseconds * 100 / totalDuration.inMilliseconds;
+    timings.write(
+        '    Total compile-time ${_formatMs(totalDuration.inMilliseconds)};'
+        ' setup ${_formatMs(setupDuration.inMilliseconds)};'
+        ' async ${_formatMs(asyncDuration.inMilliseconds)};'
+        ' unaccounted ${_formatMs(unaccountedDuration.inMilliseconds)}'
+        ' (${percent.toStringAsFixed(2)}%)');
+  }
+
+  void collectMetrics(StringBuffer buffer) {
+    buffer.writeln('Metrics:');
+    for (final task in tasks) {
+      var metrics = task.metrics;
+      var namespace = metrics.namespace;
+      if (namespace == '') {
+        namespace =
+            task.name.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]+'), '_');
+      }
+      void report(Metric metric) {
+        buffer
+            .writeln('  ${namespace}.${metric.name}: ${metric.formatValue()}');
+      }
+
+      for (final metric in metrics.primary) {
+        report(metric);
+      }
+      if (options.reportSecondaryMetrics) {
+        for (final metric in metrics.secondary) {
+          report(metric);
+        }
+      }
+    }
+  }
 }
 
 class _CompilerOutput implements api.CompilerOutput {
@@ -1045,11 +1186,12 @@
   }
 }
 
-class _EmptyEnvironment implements Environment {
-  const _EmptyEnvironment();
+class _TimingData {
+  final String description;
+  final int milliseconds;
+  final double percent;
 
-  @override
-  Map<String, String> toMap() => const {};
+  _TimingData(this.description, this.milliseconds, this.percent);
 }
 
 /// Interface for showing progress during compilation.
diff --git a/pkg/compiler/lib/src/environment.dart b/pkg/compiler/lib/src/environment.dart
index efc2bb0..cb5fdbd 100644
--- a/pkg/compiler/lib/src/environment.dart
+++ b/pkg/compiler/lib/src/environment.dart
@@ -7,7 +7,10 @@
 /// Environment variables can be used in the user code in two ways. From
 /// conditional imports, and from `const String.fromEnvironment` and
 /// other similar constructors.
-abstract class Environment {
-  /// Returns the full environment as map.
-  Map<String, String> toMap();
+class Environment {
+  /// An immutable map of environment variables.
+  final Map<String, String> definitions;
+
+  Environment(Map<String, String> definitions)
+      : this.definitions = Map.unmodifiable(definitions);
 }
diff --git a/pkg/compiler/lib/src/ir/constants.dart b/pkg/compiler/lib/src/ir/constants.dart
index d95ffb2..4ac13df 100644
--- a/pkg/compiler/lib/src/ir/constants.dart
+++ b/pkg/compiler/lib/src/ir/constants.dart
@@ -8,6 +8,7 @@
 import 'package:kernel/src/printer.dart' as ir;
 import 'package:kernel/type_environment.dart' as ir;
 
+import '../environment.dart';
 import '../kernel/dart2js_target.dart';
 
 typedef ReportErrorFunction = void Function(
@@ -20,7 +21,7 @@
 
   Dart2jsConstantEvaluator(ir.Component component,
       ir.TypeEnvironment typeEnvironment, ReportErrorFunction reportError,
-      {Map<String, String> environment = const {},
+      {Environment environment,
       bool supportReevaluationForTesting = false,
       ir.EvaluationMode evaluationMode})
       : _supportReevaluationForTesting = supportReevaluationForTesting,
@@ -29,7 +30,7 @@
             const Dart2jsDartLibrarySupport(),
             const Dart2jsConstantsBackend(supportsUnevaluatedConstants: false),
             component,
-            environment,
+            environment?.definitions ?? const {},
             typeEnvironment,
             ErrorReporter(reportError),
             enableTripleShift: true,
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index 9ca6a7c..48f715e 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -1169,7 +1169,7 @@
             (ir.LocatedMessage message, List<ir.LocatedMessage> context) {
       reportLocatedMessage(reporter, message, context);
     },
-            environment: _environment.toMap(),
+            environment: _environment,
             evaluationMode: options.useLegacySubtyping
                 ? ir.EvaluationMode.weak
                 : ir.EvaluationMode.strong);
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 99cc1e2..ba76698 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -811,7 +811,7 @@
             (ir.LocatedMessage message, List<ir.LocatedMessage> context) {
       reportLocatedMessage(reporter, message, context);
     },
-            environment: _environment.toMap(),
+            environment: _environment,
             evaluationMode: options.useLegacySubtyping
                 ? ir.EvaluationMode.weak
                 : ir.EvaluationMode.strong);
diff --git a/pkg/compiler/lib/src/source_file_provider.dart b/pkg/compiler/lib/src/source_file_provider.dart
index 894d7fc..4ab19c1 100644
--- a/pkg/compiler/lib/src/source_file_provider.dart
+++ b/pkg/compiler/lib/src/source_file_provider.dart
@@ -240,7 +240,7 @@
     if (kind != api.Diagnostic.INFO) {
       lastKind = kind;
     }
-    var color;
+    String Function(String) color;
     if (kind == api.Diagnostic.ERROR) {
       color = colors.red;
     } else if (kind == api.Diagnostic.WARNING) {
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 139b3a2..ac66787 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -6347,7 +6347,7 @@
     if (element == _commonElements.traceHelper) return;
     // TODO(sigmund): create a better uuid for elements.
     HConstant idConstant = graph.addConstantInt(element.hashCode, closedWorld);
-    n(e) => e == null ? '' : e.name;
+    String n(Entity e) => e == null ? '' : e.name;
     String name = "${n(element.library)}:${n(element.enclosingClass)}."
         "${n(element)}";
     HConstant nameConstant = graph.addConstantString(name, closedWorld);
@@ -6437,7 +6437,7 @@
     kernelBuilder.open(startTryBlock);
   }
 
-  void _addExitTrySuccessor(successor) {
+  void _addExitTrySuccessor(HBasicBlock successor) {
     if (successor == null) return;
     // Iterate over all blocks created inside this try/catch, and
     // attach successor information to blocks that end with
@@ -6451,7 +6451,7 @@
     }
   }
 
-  void _addOptionalSuccessor(block1, block2) {
+  void _addOptionalSuccessor(HBasicBlock block1, HBasicBlock block2) {
     if (block2 != null) block1.addSuccessor(block2);
   }
 
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 0d27a96..b1a37fb 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -1024,6 +1024,9 @@
 
     if (!identical(end.predecessors[1], elseBlock)) return;
     HPhi phi = end.phis.first;
+    // This useless phi should have been removed.  Do not generate-at-use if
+    // there is no use. See #48383.
+    if (phi.usedBy.isEmpty) return;
     HInstruction thenInput = phi.inputs[0];
     HInstruction elseInput = phi.inputs[1];
     if (thenInput.isJsStatement() || elseInput.isJsStatement()) return;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 5ef83e7..fe51fdd 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -2541,6 +2541,11 @@
   }
 
   void simplifyPhi(HPhi phi) {
+    // Remove an unused HPhi so that the inputs can become potentially dead.
+    if (phi.usedBy.isEmpty) {
+      phi.block.removePhi(phi);
+      return;
+    }
     // If the phi is of the form `phi(x, HTypeKnown(x))`, it does not strengthen
     // `x`.  We can replace the phi with `x` to potentially make the HTypeKnown
     // refinement node dead and potentially make a HIf control no HPhis.
diff --git a/pkg/compiler/lib/src/universe/side_effects.dart b/pkg/compiler/lib/src/universe/side_effects.dart
index c05eaf7..f4101839 100644
--- a/pkg/compiler/lib/src/universe/side_effects.dart
+++ b/pkg/compiler/lib/src/universe/side_effects.dart
@@ -58,7 +58,8 @@
   }
 
   @override
-  bool operator ==(other) => _flags == other._flags;
+  bool operator ==(Object other) =>
+      other is SideEffects && _flags == other._flags;
 
   @override
   int get hashCode => throw UnsupportedError('SideEffects.hashCode');
diff --git a/pkg/compiler/lib/src/util/util.dart b/pkg/compiler/lib/src/util/util.dart
index 7c5f50b..c9fa382 100644
--- a/pkg/compiler/lib/src/util/util.dart
+++ b/pkg/compiler/lib/src/util/util.dart
@@ -160,8 +160,8 @@
 /// anything which supports [:write:] and [:writeCharCode:], for example,
 /// [StringBuffer].  Note that JS supports \xnn and \unnnn whereas JSON only
 /// supports the \unnnn notation.  Therefore we use the \unnnn notation.
-void writeJsonEscapedCharsOn(String string, buffer) {
-  void addCodeUnitEscaped(var buffer, int code) {
+void writeJsonEscapedCharsOn(String string, StringBuffer buffer) {
+  void addCodeUnitEscaped(StringBuffer buffer, int code) {
     assert(code < 0x10000);
     buffer.write(r'\u');
     if (code < 0x1000) {
@@ -176,7 +176,7 @@
     buffer.write(code.toRadixString(16));
   }
 
-  void writeEscapedOn(String string, var buffer) {
+  void writeEscapedOn(String string, StringBuffer buffer) {
     for (int i = 0; i < string.length; i++) {
       int code = string.codeUnitAt(i);
       if (code == $DQ) {
diff --git a/pkg/compiler/test/analyses/api_allowed.json b/pkg/compiler/test/analyses/api_allowed.json
index c54c832..645ff86 100644
--- a/pkg/compiler/test/analyses/api_allowed.json
+++ b/pkg/compiler/test/analyses/api_allowed.json
@@ -14,15 +14,11 @@
   "org-dartlang-sdk:///lib/_http/websocket_impl.dart": {
     "Dynamic invocation of 'dart._http::_toJSON'.": 1
   },
-  "org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_number.dart": {
-    "Dynamic invocation of '[]'.": 5
-  },
   "org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart": {
     "Dynamic access of 'length'.": 1
   },
   "org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart": {
-    "Dynamic access of 'iterator'.": 1,
-    "Dynamic invocation of 'then'.": 1
+    "Dynamic access of 'iterator'.": 1
   },
   "org-dartlang-sdk:///lib/_internal/js_runtime/lib/convert_patch.dart": {
     "Dynamic invocation of 'clear'.": 1
@@ -109,4 +105,4 @@
   "org-dartlang-sdk:///lib/core/errors.dart": {
     "Dynamic access of 'length'.": 2
   }
-}
\ No newline at end of file
+}
diff --git a/pkg/compiler/test/analyses/dart2js_allowed.json b/pkg/compiler/test/analyses/dart2js_allowed.json
index 7f34cc7..ea447f6 100644
--- a/pkg/compiler/test/analyses/dart2js_allowed.json
+++ b/pkg/compiler/test/analyses/dart2js_allowed.json
@@ -4,13 +4,6 @@
     "Dynamic invocation of 'resume'.": 1,
     "Dynamic invocation of 'pause'.": 1
   },
-  "pkg/compiler/lib/src/source_file_provider.dart": {
-    "Dynamic invocation of 'call'.": 3
-  },
-  "pkg/compiler/lib/src/util/util.dart": {
-    "Dynamic invocation of 'write'.": 11,
-    "Dynamic invocation of 'writeCharCode'.": 1
-  },
   "pkg/compiler/lib/src/util/maplet.dart": {
     "Dynamic access of 'isEmpty'.": 1,
     "Dynamic access of 'length'.": 1,
@@ -85,14 +78,6 @@
   "pkg/dart2js_info/lib/binary_serialization.dart": {
     "Dynamic invocation of 'cast'.": 1
   },
-  "pkg/compiler/lib/src/universe/side_effects.dart": {
-    "Dynamic access of 'universe.side_effects::_flags'.": 1
-  },
-  "pkg/compiler/lib/src/ssa/builder_kernel.dart": {
-    "Dynamic access of 'name'.": 1,
-    "Dynamic access of 'id'.": 1,
-    "Dynamic invocation of 'addSuccessor'.": 1
-  },
   "pkg/compiler/lib/src/ssa/types.dart": {
     "Dynamic access of 'element'.": 1
   },
diff --git a/pkg/compiler/test/end_to_end/dill_loader_test.dart b/pkg/compiler/test/end_to_end/dill_loader_test.dart
index 6cf4fb9..6e22f60 100644
--- a/pkg/compiler/test/end_to_end/dill_loader_test.dart
+++ b/pkg/compiler/test/end_to_end/dill_loader_test.dart
@@ -6,7 +6,6 @@
 
 import '../helpers/memory_compiler.dart';
 import 'package:async_helper/async_helper.dart';
-import 'package:compiler/src/apiimpl.dart' show CompilerImpl;
 import 'package:compiler/src/common/elements.dart';
 import 'package:compiler/src/elements/entities.dart'
     show LibraryEntity, ClassEntity;
@@ -38,7 +37,7 @@
 
     List<int> kernelBinary =
         serializeComponent((await kernelForProgram(uri, options)).component);
-    CompilerImpl compiler = compilerFor(
+    var compiler = compilerFor(
         entryPoint: uri,
         memorySourceFiles: {'main.dill': kernelBinary},
         diagnosticHandler: diagnostics,
diff --git a/pkg/compiler/test/end_to_end/exit_code_test.dart b/pkg/compiler/test/end_to_end/exit_code_test.dart
index 76920d1..ee7a324 100644
--- a/pkg/compiler/test/end_to_end/exit_code_test.dart
+++ b/pkg/compiler/test/end_to_end/exit_code_test.dart
@@ -22,7 +22,6 @@
 import 'package:compiler/src/diagnostics/invariant.dart';
 import 'package:compiler/src/diagnostics/messages.dart';
 import 'package:compiler/src/diagnostics/spannable.dart';
-import 'package:compiler/src/apiimpl.dart';
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/null_compiler_output.dart';
@@ -33,7 +32,7 @@
 import 'diagnostic_reporter_helper.dart';
 import '../helpers/memory_compiler.dart';
 
-class TestCompiler extends CompilerImpl {
+class TestCompiler extends Compiler {
   final String testMarker;
   final String testType;
   final Function onTest;
diff --git a/pkg/compiler/test/end_to_end/modular_loader_test.dart b/pkg/compiler/test/end_to_end/modular_loader_test.dart
index bc59954..00abbf1 100644
--- a/pkg/compiler/test/end_to_end/modular_loader_test.dart
+++ b/pkg/compiler/test/end_to_end/modular_loader_test.dart
@@ -6,7 +6,6 @@
 
 import '../helpers/memory_compiler.dart';
 import 'package:async_helper/async_helper.dart';
-import 'package:compiler/src/apiimpl.dart' show CompilerImpl;
 import 'package:compiler/src/common/elements.dart';
 import 'package:compiler/src/elements/entities.dart'
     show LibraryEntity, ClassEntity;
@@ -35,7 +34,7 @@
     DiagnosticCollector diagnostics = DiagnosticCollector();
     OutputCollector output = OutputCollector();
     Uri entryPoint = Uri.parse('org-dartlang-test:///c2.dart');
-    CompilerImpl compiler = compilerFor(
+    var compiler = compilerFor(
         entryPoint: entryPoint,
         options: [
           '--input-dill=memory:c.dill',
diff --git a/pkg/compiler/test/helpers/memory_compiler.dart b/pkg/compiler/test/helpers/memory_compiler.dart
index 0e3ed0c..7131e00 100644
--- a/pkg/compiler/test/helpers/memory_compiler.dart
+++ b/pkg/compiler/test/helpers/memory_compiler.dart
@@ -10,6 +10,7 @@
 
 import 'package:compiler/compiler.dart'
     show CompilationResult, CompilerDiagnostics, CompilerOutput, Diagnostic;
+import 'package:compiler/src/compiler.dart' show Compiler;
 import 'package:compiler/src/common.dart';
 import 'package:compiler/src/commandline_options.dart';
 import 'package:compiler/src/diagnostics/messages.dart' show Message;
@@ -89,12 +90,12 @@
     bool showDiagnostics: true,
     Uri librariesSpecificationUri,
     Uri packageConfig,
-    void beforeRun(CompilerImpl compiler),
+    void beforeRun(Compiler compiler),
     bool unsafeToTouchSourceFiles: false}) async {
   if (entryPoint == null) {
     entryPoint = Uri.parse('memory:main.dart');
   }
-  CompilerImpl compiler = compilerFor(
+  Compiler compiler = compilerFor(
       entryPoint: entryPoint,
       memorySourceFiles: memorySourceFiles,
       diagnosticHandler: diagnosticHandler,
@@ -114,7 +115,7 @@
       isSuccess: isSuccess, kernelInitializedCompilerState: compilerState);
 }
 
-CompilerImpl compilerFor(
+Compiler compilerFor(
     {Uri entryPoint,
     Map<String, dynamic> memorySourceFiles: const <String, dynamic>{},
     CompilerDiagnostics diagnosticHandler,
@@ -173,7 +174,7 @@
     ..packageConfig = packageConfig;
   compilerOptions.kernelInitializedCompilerState =
       kernelInitializedCompilerState;
-  CompilerImpl compiler = new CompilerImpl(
+  var compiler = new Compiler(
       provider, outputProvider, diagnosticHandler, compilerOptions);
 
   return compiler;
diff --git a/pkg/compiler/test/helpers/memory_source_file_helper.dart b/pkg/compiler/test/helpers/memory_source_file_helper.dart
index 3591263..db35ef1 100644
--- a/pkg/compiler/test/helpers/memory_source_file_helper.dart
+++ b/pkg/compiler/test/helpers/memory_source_file_helper.dart
@@ -11,8 +11,6 @@
 
 import 'package:compiler/compiler.dart';
 
-export 'package:compiler/src/apiimpl.dart' show CompilerImpl;
-
 import 'package:compiler/src/io/source_file.dart'
     show Binary, StringSourceFile, Utf8BytesSourceFile;
 
diff --git a/pkg/compiler/test/model/cfe_constant_evaluation_test.dart b/pkg/compiler/test/model/cfe_constant_evaluation_test.dart
index 693dccb..ae213d2 100644
--- a/pkg/compiler/test/model/cfe_constant_evaluation_test.dart
+++ b/pkg/compiler/test/model/cfe_constant_evaluation_test.dart
@@ -16,6 +16,7 @@
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/elements/indexed.dart';
 import 'package:compiler/src/elements/types.dart';
+import 'package:compiler/src/environment.dart';
 import 'package:compiler/src/ir/constants.dart';
 import 'package:compiler/src/ir/visitors.dart';
 import 'package:compiler/src/kernel/kernel_strategy.dart';
@@ -606,7 +607,7 @@
             errors.add(context.first.code.name);
             reportLocatedMessage(elementMap.reporter, message, context);
           },
-              environment: environment,
+              environment: Environment(environment),
               supportReevaluationForTesting: true,
               evaluationMode: compiler.options.useLegacySubtyping
                   ? ir.EvaluationMode.weak
diff --git a/pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart b/pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart
index 09a86fd..24ad0a3 100644
--- a/pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart
+++ b/pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart
@@ -9,8 +9,8 @@
 import 'dart:async';
 import 'dart:io';
 import 'package:compiler/compiler.dart';
-import 'package:compiler/src/apiimpl.dart' as api;
 import 'package:compiler/src/commandline_options.dart';
+import 'package:compiler/src/compiler.dart' as api;
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/io/code_output.dart';
 import 'package:compiler/src/io/source_file.dart';
@@ -357,7 +357,7 @@
       throw "Compilation failed.";
     }
 
-    api.CompilerImpl compiler = result.compiler;
+    var compiler = result.compiler;
     JsBackendStrategy backendStrategy = compiler.backendStrategy;
     RecordingSourceInformationStrategy strategy =
         backendStrategy.sourceInformationStrategy;
@@ -414,7 +414,7 @@
 }
 
 class SourceMaps {
-  final api.CompilerImpl compiler;
+  final api.Compiler compiler;
   final SourceFileManager sourceFileManager;
   // TODO(johnniwinther): Supported multiple output units.
   final SourceMapInfo mainSourceMapInfo;
diff --git a/pkg/dartdev/lib/src/templates/common.dart b/pkg/dartdev/lib/src/templates/common.dart
index ac53756..3c4a79c 100644
--- a/pkg/dartdev/lib/src/templates/common.dart
+++ b/pkg/dartdev/lib/src/templates/common.dart
@@ -4,9 +4,7 @@
 
 import '../sdk.dart';
 
-String get sdkConstraint => '''
-  sdk: '>=${sdk.version} <3.0.0'
-''';
+String get sdkConstraint => "sdk: '>=${sdk.version} <3.0.0'";
 
 const String gitignore = '''
 # Files and directories created by pub.
diff --git a/pkg/dartdev/lib/src/templates/console_full.dart b/pkg/dartdev/lib/src/templates/console_full.dart
index d4c2965..06583d9 100644
--- a/pkg/dartdev/lib/src/templates/console_full.dart
+++ b/pkg/dartdev/lib/src/templates/console_full.dart
@@ -38,7 +38,7 @@
 # homepage: https://www.example.com
 
 environment:
-${common.sdkConstraint}
+  ${common.sdkConstraint}
 
 # dependencies:
 #   path: ^1.8.0
diff --git a/pkg/dartdev/lib/src/templates/console_simple.dart b/pkg/dartdev/lib/src/templates/console_simple.dart
index ed6f793..0234d18 100644
--- a/pkg/dartdev/lib/src/templates/console_simple.dart
+++ b/pkg/dartdev/lib/src/templates/console_simple.dart
@@ -36,7 +36,7 @@
 # homepage: https://www.example.com
 
 environment:
-${common.sdkConstraint}
+  ${common.sdkConstraint}
 
 # dependencies:
 #   path: ^1.8.0
diff --git a/pkg/dartdev/lib/src/templates/package_simple.dart b/pkg/dartdev/lib/src/templates/package_simple.dart
index 9827dd7..6bdbd57 100644
--- a/pkg/dartdev/lib/src/templates/package_simple.dart
+++ b/pkg/dartdev/lib/src/templates/package_simple.dart
@@ -55,7 +55,7 @@
 # homepage: https://www.example.com
 
 environment:
-${common.sdkConstraint}
+  ${common.sdkConstraint}
 
 # dependencies:
 #   path: ^1.8.0
diff --git a/pkg/dartdev/lib/src/templates/server_shelf.dart b/pkg/dartdev/lib/src/templates/server_shelf.dart
index c075f02..12a2e3b 100644
--- a/pkg/dartdev/lib/src/templates/server_shelf.dart
+++ b/pkg/dartdev/lib/src/templates/server_shelf.dart
@@ -42,7 +42,7 @@
 # homepage: https://www.example.com
 
 environment:
-${common.sdkConstraint}
+  ${common.sdkConstraint}
 
 dependencies:
   args: ^2.0.0
diff --git a/pkg/dartdev/lib/src/templates/web_simple.dart b/pkg/dartdev/lib/src/templates/web_simple.dart
index e22be74..04e8776 100644
--- a/pkg/dartdev/lib/src/templates/web_simple.dart
+++ b/pkg/dartdev/lib/src/templates/web_simple.dart
@@ -42,7 +42,7 @@
 # homepage: https://www.example.com
 
 environment:
-${common.sdkConstraint}
+  ${common.sdkConstraint}
 
 # dependencies:
 #   path: ^1.7.0
diff --git a/runtime/tests/vm/dart/spawn_uri_aot_test.dart b/runtime/tests/vm/dart/spawn_uri_aot_test.dart
index ded95ce..44acb3a 100644
--- a/runtime/tests/vm/dart/spawn_uri_aot_test.dart
+++ b/runtime/tests/vm/dart/spawn_uri_aot_test.dart
@@ -55,11 +55,17 @@
       ''');
     }
 
+    // '--enable-asserts' is not available in product mode, so we skip the
+    // negative test.
+    final isProductMode = const bool.fromEnvironment('dart.vm.product');
+
     for (final basename in ['main', 'spawnee', 'spawnee_checked']) {
       final script = path.join(dir, '$basename.dart');
       final scriptDill = path.join(dir, '$basename.dart.dill');
       final bool checked = basename.endsWith('_checked');
 
+      if (isProductMode && checked) continue;
+
       await run(genKernel, <String>[
         if (checked) '--enable-asserts',
         '--aot',
@@ -89,18 +95,20 @@
       'success',
     ], result1);
 
-    // File exists and is AOT snapshot but was compiled with different flags
-    // (namely --enable-asserts)
-    final result2 = await runHelper(aotRuntime, [
-      path.join(dir, 'main.dart.dill.so'),
-      path.join(dir, 'spawnee_checked.dart.dill.so'),
-    ]);
-    Expect.notEquals(0, result2.exitCode);
-    Expect.contains('Snapshot not compatible with the current VM configuration',
-        result2.stderr);
+    if (!isProductMode) {
+      // File exists and is AOT snapshot but was compiled with different flags
+      // (namely --enable-asserts)
+      final result2 = await runHelper(aotRuntime, [
+        path.join(dir, 'main.dart.dill.so'),
+        path.join(dir, 'spawnee_checked.dart.dill.so'),
+      ]);
+      Expect.notEquals(0, result2.exitCode);
+      Expect.contains(
+          'Snapshot not compatible with the current VM configuration',
+          result2.stderr);
+    }
 
-    // File exists and is AOT snapshot but was compiled with different flags
-    // (namely --enable-asserts)
+    // File does not exist.
     final result3 = await runHelper(aotRuntime, [
       path.join(dir, 'main.dart.dill.so'),
       path.join(dir, 'does_not_exist.dart.dill.so'),
diff --git a/runtime/tests/vm/dart_2/spawn_uri_aot_test.dart b/runtime/tests/vm/dart_2/spawn_uri_aot_test.dart
index b3e3c21..ca4bd6a 100644
--- a/runtime/tests/vm/dart_2/spawn_uri_aot_test.dart
+++ b/runtime/tests/vm/dart_2/spawn_uri_aot_test.dart
@@ -57,11 +57,17 @@
       ''');
     }
 
+    // '--enable-asserts' is not available in product mode, so we skip the
+    // negative test.
+    final isProductMode = const bool.fromEnvironment('dart.vm.product');
+
     for (final basename in ['main', 'spawnee', 'spawnee_checked']) {
       final script = path.join(dir, '$basename.dart');
       final scriptDill = path.join(dir, '$basename.dart.dill');
       final bool checked = basename.endsWith('_checked');
 
+      if (isProductMode && checked) continue;
+
       await run(genKernel, <String>[
         if (checked) '--enable-asserts',
         '--aot',
@@ -91,18 +97,20 @@
       'success',
     ], result1);
 
-    // File exists and is AOT snapshot but was compiled with different flags
-    // (namely --enable-asserts)
-    final result2 = await runHelper(aotRuntime, [
-      path.join(dir, 'main.dart.dill.so'),
-      path.join(dir, 'spawnee_checked.dart.dill.so'),
-    ]);
-    Expect.notEquals(0, result2.exitCode);
-    Expect.contains('Snapshot not compatible with the current VM configuration',
-        result2.stderr);
+    if (!isProductMode) {
+      // File exists and is AOT snapshot but was compiled with different flags
+      // (namely --enable-asserts)
+      final result2 = await runHelper(aotRuntime, [
+        path.join(dir, 'main.dart.dill.so'),
+        path.join(dir, 'spawnee_checked.dart.dill.so'),
+      ]);
+      Expect.notEquals(0, result2.exitCode);
+      Expect.contains(
+          'Snapshot not compatible with the current VM configuration',
+          result2.stderr);
+    }
 
-    // File exists and is AOT snapshot but was compiled with different flags
-    // (namely --enable-asserts)
+    // File does not exist.
     final result3 = await runHelper(aotRuntime, [
       path.join(dir, 'main.dart.dill.so'),
       path.join(dir, 'does_not_exist.dart.dill.so'),
diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc
index 5d739a0..897c9d7 100644
--- a/runtime/vm/compiler/assembler/assembler_riscv.cc
+++ b/runtime/vm/compiler/assembler/assembler_riscv.cc
@@ -4166,6 +4166,10 @@
   LoadImmediate(dst, target::ToRawSmi(object));
 }
 
+static const RegisterSet kRuntimeCallSavedRegisters(
+    kAbiVolatileCpuRegs | (1 << CALLEE_SAVED_TEMP) | (1 << CALLEE_SAVED_TEMP2),
+    kAbiVolatileFpuRegs);
+
 // Note: leaf call sequence uses some abi callee save registers as scratch
 // so they should be manually preserved.
 void Assembler::EnterCallRuntimeFrame(intptr_t frame_size, bool is_leaf) {
@@ -4186,9 +4190,7 @@
     addi(FP, SP, 2 * target::kWordSize + frame_size);
   }
 
-  const RegisterSet kVolatileRegisterSet(kAbiVolatileCpuRegs,
-                                         kAbiVolatileFpuRegs);
-  PushRegisters(kVolatileRegisterSet);
+  PushRegisters(kRuntimeCallSavedRegisters);
 
   if (!is_leaf) {  // Leaf calling sequence aligns the stack itself.
     ReserveAlignedFrameSpace(0);
@@ -4196,18 +4198,15 @@
 }
 
 void Assembler::LeaveCallRuntimeFrame(bool is_leaf) {
-  const RegisterSet kVolatileRegisterSet(kAbiVolatileCpuRegs,
-                                         kAbiVolatileFpuRegs);
-
   const intptr_t kPushedRegistersSize =
-      kVolatileRegisterSet.CpuRegisterCount() * target::kWordSize +
-      kVolatileRegisterSet.FpuRegisterCount() * kFpuRegisterSize +
+      kRuntimeCallSavedRegisters.CpuRegisterCount() * target::kWordSize +
+      kRuntimeCallSavedRegisters.FpuRegisterCount() * kFpuRegisterSize +
       (target::frame_layout.dart_fixed_frame_size - 2) *
           target::kWordSize;  // From EnterStubFrame (excluding PC / FP)
 
   subi(SP, FP, kPushedRegistersSize);
 
-  PopRegisters(kVolatileRegisterSet);
+  PopRegisters(kRuntimeCallSavedRegisters);
 
   LeaveStubFrame();
 }
diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc
index 1442eed..c9067af 100644
--- a/runtime/vm/compiler/backend/il_riscv.cc
+++ b/runtime/vm/compiler/backend/il_riscv.cc
@@ -4910,17 +4910,10 @@
   } else {
     UNIMPLEMENTED();
   }
-  // PP is a C volatile register.
-  // SP will be aligned to the C stack alignment.
-  __ mv(CALLEE_SAVED_TEMP, PP);
-  __ mv(CALLEE_SAVED_TEMP2, SP);
 
   // Call the function.
   ASSERT(TargetFunction().is_leaf());  // No deopt info needed.
   __ CallRuntime(TargetFunction(), TargetFunction().argument_count());
-
-  __ mv(PP, CALLEE_SAVED_TEMP);
-  __ mv(SP, CALLEE_SAVED_TEMP2);
 }
 
 LocationSummary* MathMinMaxInstr::MakeLocationSummary(Zone* zone,
@@ -5242,17 +5235,9 @@
     UNIMPLEMENTED();
   }
 
-  // PP is a C volatile register.
-  // SP will be aligned to the C stack alignment.
-  __ mv(CALLEE_SAVED_TEMP, PP);
-  __ mv(CALLEE_SAVED_TEMP2, SP);
-
   ASSERT(TargetFunction().is_leaf());  // No deopt info needed.
   __ CallRuntime(TargetFunction(), InputCount());
 
-  __ mv(PP, CALLEE_SAVED_TEMP);
-  __ mv(SP, CALLEE_SAVED_TEMP2);
-
   // TODO(riscv): Special case pow?
 }
 
diff --git a/runtime/vm/runtime_entry_riscv.cc b/runtime/vm/runtime_entry_riscv.cc
index 8dfab67..3987176 100644
--- a/runtime/vm/runtime_entry_riscv.cc
+++ b/runtime/vm/runtime_entry_riscv.cc
@@ -50,10 +50,11 @@
                                 intptr_t argument_count) {
   if (runtime_entry->is_leaf()) {
     ASSERT(argument_count == runtime_entry->argument_count());
-    // Caller is responsible for either using CallRuntimeScore or manually
-    // saving PP (C volatile register) and SP (altered by alignment).
     COMPILE_ASSERT(!IsAbiPreservedRegister(PP));
-
+    // PP is a C volatile register.
+    // SP will be aligned to the C stack alignment.
+    __ mv(CALLEE_SAVED_TEMP, PP);
+    __ mv(CALLEE_SAVED_TEMP2, SP);
     __ lx(TMP2,
           compiler::Address(THR, Thread::OffsetFromThread(runtime_entry)));
     __ sx(TMP2, compiler::Address(THR, Thread::vm_tag_offset()));
@@ -61,6 +62,8 @@
     __ jalr(TMP2);
     __ LoadImmediate(TMP2, VMTag::kDartTagId);
     __ sx(TMP2, compiler::Address(THR, Thread::vm_tag_offset()));
+    __ mv(PP, CALLEE_SAVED_TEMP);
+    __ mv(SP, CALLEE_SAVED_TEMP2);
     // These registers must be preserved by runtime functions, otherwise
     // we'd need to restore them here.
     COMPILE_ASSERT(IsCalleeSavedRegister(THR));
diff --git a/sdk/lib/_internal/js_runtime/lib/async_patch.dart b/sdk/lib/_internal/js_runtime/lib/async_patch.dart
index 4888baf..5f8f5af 100644
--- a/sdk/lib/_internal/js_runtime/lib/async_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/async_patch.dart
@@ -481,7 +481,7 @@
 
   add(event) => controller.add(event);
 
-  addStream(Stream<T> stream) {
+  Future addStream(Stream<T> stream) {
     return controller.addStream(stream, cancelOnError: false);
   }
 
diff --git a/sdk/lib/_internal/js_runtime/lib/js_number.dart b/sdk/lib/_internal/js_runtime/lib/js_number.dart
index 678a61b..f217b53 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_number.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_number.dart
@@ -242,7 +242,7 @@
   static String _handleIEtoString(String result) {
     // Result is probably IE's untraditional format for large numbers,
     // e.g., "8.0000000000008(e+15)" for 0x8000000000000800.toString(16).
-    var match = JS('JSArray|Null',
+    List? match = JS('JSArray|Null',
         r'/^([\da-z]+)(?:\.([\da-z]+))?\(e\+(\d+)\)$/.exec(#)', result);
     if (match == null) {
       // Then we don't know how to handle it at all.
diff --git a/tests/standalone/standalone_precompiled.status b/tests/standalone/standalone_precompiled.status
index 536731b..69da5e9 100644
--- a/tests/standalone/standalone_precompiled.status
+++ b/tests/standalone/standalone_precompiled.status
@@ -15,6 +15,7 @@
 io/file_lock_test: Skip
 io/file_read_special_device_test: Skip
 io/http_client_stays_alive_test: Skip
+io/http_redirect_test: SkipByDesign # Requires dart:mirrors
 io/http_response_deadline_test: Skip
 io/http_server_close_response_after_error_test: Skip
 io/https_unauthorized_test: Skip
diff --git a/tests/standalone_2/standalone_2_precompiled.status b/tests/standalone_2/standalone_2_precompiled.status
index 07c20e7..f7f7eb2 100644
--- a/tests/standalone_2/standalone_2_precompiled.status
+++ b/tests/standalone_2/standalone_2_precompiled.status
@@ -12,6 +12,7 @@
 io/file_lock_test: Skip
 io/file_read_special_device_test: Skip
 io/http_client_stays_alive_test: Skip
+io/http_redirect_test: SkipByDesign # Requires dart:mirrors
 io/http_response_deadline_test: Skip
 io/http_server_close_response_after_error_test: Skip
 io/https_unauthorized_test: Skip
diff --git a/tests/web/48383_test.dart b/tests/web/48383_test.dart
new file mode 100644
index 0000000..3100f74
--- /dev/null
+++ b/tests/web/48383_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2022, 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';
+
+void main() {
+  load();
+  load(cond: true);
+  Expect.equals('side-effect', state);
+}
+
+String state = 'start';
+
+// `cond` can be both true and false.
+// `use` is always be false.
+dynamic load({bool cond = false, bool use = false}) {
+  int? data;
+  if (cond) {
+    data = callWithSideEffects();
+  }
+  return [if (use && cond) data];
+}
+
+@pragma('dart2js:noInline')
+int callWithSideEffects() {
+  state = 'side-effect';
+  return 2;
+}
diff --git a/tests/web_2/48383_test.dart b/tests/web_2/48383_test.dart
new file mode 100644
index 0000000..3100f74
--- /dev/null
+++ b/tests/web_2/48383_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2022, 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';
+
+void main() {
+  load();
+  load(cond: true);
+  Expect.equals('side-effect', state);
+}
+
+String state = 'start';
+
+// `cond` can be both true and false.
+// `use` is always be false.
+dynamic load({bool cond = false, bool use = false}) {
+  int? data;
+  if (cond) {
+    data = callWithSideEffects();
+  }
+  return [if (use && cond) data];
+}
+
+@pragma('dart2js:noInline')
+int callWithSideEffects() {
+  state = 'side-effect';
+  return 2;
+}
diff --git a/tools/VERSION b/tools/VERSION
index f17707f..eeb0ced 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 107
+PRERELEASE 108
 PRERELEASE_PATCH 0
\ No newline at end of file