[dart2js] Evaluate CFE consts as part of phase 0b (CFE linker).

Constants are current evaluated in a few places during closed world generation, primarily as part of the ScopeModelBuilder. The scope visitor was modifying the AST which meant we had to emit a new dill with these evaluated constants along with the closed world results.

This change instead evaluates the constants directly after linking the Kernel as part of the global transformations. This means we can update the ScopeModelBuilder to not mutate the AST at all as all constants are already simplified.

A potential follow up here is to simplify the ScopeModelBuilder since all nodes should already be simplified if they can be, we should be able to avoid visiting some children.

After this change we only directly create a single ConstantEvaluator, the one in `load_kernel`. The const simplifier also creates one and a follow up CL moves this to to run right after this new transformation.

Note: Alternate versions of this CL tried to make the global transformation simpler by either:
1) Running the const evaluator indiscriminately on all expressions. This didn't work because it lead to exponential computation on constants set up as a DAG (see tests/language/const/constant_dag_test).
2) Only evaluating ConstantExpression nodes to update UnevaluatedConstants. This does not cover all the cases where the ScopeModelBuilder is modifying the tree and lead to a different compiler output.

Change-Id: I746d889b37feddc9ab6c386c6252016dec745e6e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/332601
Reviewed-by: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 7f59d40..ee7c2af 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -26,6 +26,7 @@
 import 'deferred_load/program_split_constraints/nodes.dart' as psc
     show ConstraintData;
 import 'deferred_load/program_split_constraints/parser.dart' as psc show Parser;
+import 'diagnostics/diagnostic_listener.dart';
 import 'diagnostics/messages.dart' show Message;
 import 'dump_info.dart'
     show
@@ -54,7 +55,6 @@
 import 'js_model/js_world.dart';
 import 'js_model/locals.dart';
 import 'kernel/dart2js_target.dart';
-import 'kernel/element_map.dart';
 import 'kernel/front_end_adapter.dart' show CompilerFileSystem;
 import 'kernel/kernel_strategy.dart';
 import 'kernel/kernel_world.dart';
@@ -178,8 +178,8 @@
     _outputProvider = _CompilerOutput(this, outputProvider);
     _reporter = DiagnosticReporter(this);
     kernelFrontEndTask = GenericTask('Front end', measurer);
-    frontendStrategy = KernelFrontendStrategy(
-        kernelFrontEndTask, options, reporter, environment);
+    frontendStrategy =
+        KernelFrontendStrategy(kernelFrontEndTask, options, reporter);
     backendStrategy = createBackendStrategy();
     _impactCache = <Entity, WorldImpact>{};
 
@@ -398,7 +398,12 @@
   Future<load_kernel.Output?> produceKernel() async {
     if (!stage.shouldReadClosedWorld) {
       load_kernel.Output? output = await loadKernel();
-      if (output == null || compilationFailed) return null;
+      if (output == null) return null;
+      if (compilationFailed) {
+        // Some tests still use the component, even if the CFE failed.
+        frontendStrategy.registerComponent(output.component);
+        return null;
+      }
       ir.Component component = output.component;
       if (retainDataForTesting) {
         componentForTesting = component;
@@ -517,7 +522,7 @@
     List<int> closedWorldData =
         strategy.serializeClosedWorld(closedWorld, options, indices);
     final component = closedWorld.elementMap.programEnv.mainComponent;
-    return strategy.deserializeClosedWorld(options, reporter, environment,
+    return strategy.deserializeClosedWorld(options, reporter,
         abstractValueStrategy, component, closedWorldData, indices);
   }
 
@@ -564,7 +569,7 @@
         backendStrategy.registerJClosedWorld(closedWorld);
       }
     } else {
-      closedWorld = await serializationTask.deserializeClosedWorld(environment,
+      closedWorld = await serializationTask.deserializeClosedWorld(
           abstractValueStrategy, component, useDeferredSourceReads, indices);
     }
     if (retainDataForTesting) {
@@ -830,11 +835,17 @@
       DiagnosticMessage diagnosticMessage, api.Diagnostic kind) {
     var span = diagnosticMessage.sourceSpan;
     var message = diagnosticMessage.message;
+    // If the message came from the CFE use the message code as the text
+    // so that tests can determine the cause of the message.
+    final messageText =
+        diagnosticMessage is DiagnosticCfeMessage && options.testMode
+            ? diagnosticMessage.messageCode
+            : '$message';
     if (span.isUnknown) {
-      callUserHandler(message, null, null, null, '$message', kind);
+      callUserHandler(message, null, null, null, messageText, kind);
     } else {
       callUserHandler(
-          message, span.uri, span.begin, span.end, '$message', kind);
+          message, span.uri, span.begin, span.end, messageText, kind);
     }
   }
 
diff --git a/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart b/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
index 55a2b47..ec8c7d0 100644
--- a/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
+++ b/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
@@ -4,6 +4,9 @@
 
 library dart2js.diagnostic_listener;
 
+import 'package:front_end/src/api_unstable/dart2js.dart' as ir
+    show LocatedMessage;
+
 import '../../compiler_api.dart' as api;
 import '../compiler.dart' show Compiler;
 import '../elements/entities.dart';
@@ -41,6 +44,15 @@
     return DiagnosticMessage(span, spannable, message);
   }
 
+  DiagnosticCfeMessage createCfeMessage(
+      Spannable spannable, MessageKind messageKind, String messageCode,
+      [Map<String, String> arguments = const {}]) {
+    SourceSpan span = spanFromSpannable(spannable);
+    MessageTemplate template = MessageTemplate.TEMPLATES[messageKind]!;
+    Message message = template.message(arguments, options);
+    return DiagnosticCfeMessage(span, spannable, message, messageCode);
+  }
+
   void reportError(DiagnosticMessage message,
       [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
     _reportDiagnosticInternal(message, infos, api.Diagnostic.ERROR);
@@ -318,8 +330,37 @@
   DiagnosticMessage(this.sourceSpan, this.spannable, this.message);
 }
 
+/// Message generated by the CFE with an additional CFE-specific [messageCode].
+class DiagnosticCfeMessage extends DiagnosticMessage {
+  final String messageCode;
+
+  DiagnosticCfeMessage(
+      super.sourceSpan, super.spannable, super.message, this.messageCode);
+}
+
 /// Information about suppressed warnings and hints for a given library.
 class SuppressionInfo {
   int warnings = 0;
   int hints = 0;
 }
+
+void reportLocatedMessage(DiagnosticReporter reporter,
+    ir.LocatedMessage message, List<ir.LocatedMessage>? context) {
+  DiagnosticMessage diagnosticMessage =
+      _createDiagnosticMessage(reporter, message);
+  var infos = <DiagnosticMessage>[];
+  if (context != null) {
+    for (ir.LocatedMessage message in context) {
+      infos.add(_createDiagnosticMessage(reporter, message));
+    }
+  }
+  reporter.reportError(diagnosticMessage, infos);
+}
+
+DiagnosticMessage _createDiagnosticMessage(
+    DiagnosticReporter reporter, ir.LocatedMessage message) {
+  var sourceSpan = SourceSpan(
+      message.uri!, message.charOffset, message.charOffset + message.length);
+  return reporter.createCfeMessage(sourceSpan, MessageKind.GENERIC,
+      message.code.name, {'text': message.problemMessage});
+}
diff --git a/pkg/compiler/lib/src/ir/annotations.dart b/pkg/compiler/lib/src/ir/annotations.dart
index b9fb545..70ca9a2 100644
--- a/pkg/compiler/lib/src/ir/annotations.dart
+++ b/pkg/compiler/lib/src/ir/annotations.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:kernel/ast.dart' as ir;
-import 'package:kernel/type_environment.dart' as ir;
 import '../common/names.dart';
 import 'modular.dart';
 
@@ -136,15 +135,12 @@
   IrAnnotationData data = IrAnnotationData();
 
   void processMember(ir.Member member) {
-    ir.StaticTypeContext staticTypeContext = ir.StaticTypeContext(
-        member, modularCore.constantEvaluator.typeEnvironment);
     List<PragmaAnnotationData>? pragmaAnnotations;
     List<String>? createsAnnotations;
     List<String>? returnsAnnotations;
     for (ir.Expression annotation in member.annotations) {
       if (annotation is ir.ConstantExpression) {
-        ir.Constant constant = modularCore.constantEvaluator
-            .evaluate(staticTypeContext, annotation);
+        ir.Constant constant = annotation.constant;
 
         String? jsName = _getJsInteropName(constant);
         if (jsName != null) {
@@ -189,13 +185,9 @@
   }
 
   for (ir.Library library in component.libraries) {
-    ir.StaticTypeContext staticTypeContext =
-        ir.StaticTypeContext.forAnnotations(
-            library, modularCore.constantEvaluator.typeEnvironment);
     for (ir.Expression annotation in library.annotations) {
       if (annotation is ir.ConstantExpression) {
-        ir.Constant constant = modularCore.constantEvaluator
-            .evaluate(staticTypeContext, annotation);
+        ir.Constant constant = annotation.constant;
 
         String? jsName = _getJsInteropName(constant);
         if (jsName != null) {
@@ -206,8 +198,7 @@
     for (ir.Class cls in library.classes) {
       for (ir.Expression annotation in cls.annotations) {
         if (annotation is ir.ConstantExpression) {
-          ir.Constant constant = modularCore.constantEvaluator
-              .evaluate(staticTypeContext, annotation);
+          ir.Constant constant = annotation.constant;
 
           String? nativeClassName = _getNativeClassName(constant);
           if (nativeClassName != null) {
diff --git a/pkg/compiler/lib/src/ir/impact_data.dart b/pkg/compiler/lib/src/ir/impact_data.dart
index 9007ad7..187e1a4 100644
--- a/pkg/compiler/lib/src/ir/impact_data.dart
+++ b/pkg/compiler/lib/src/ir/impact_data.dart
@@ -598,12 +598,7 @@
 
   @override
   void handleConstantExpression(ir.ConstantExpression node) {
-    // Evaluate any [ir.UnevaluatedConstant]s to ensure they are processed for
-    // impacts correctly.
-    // TODO(joshualitt): Remove this when we have CFE constants.
-    if (node.constant is ir.UnevaluatedConstant) {
-      _elementMap.constantEvaluator.evaluate(staticTypeContext, node);
-    }
+    assert(node.constant is! ir.UnevaluatedConstant);
     ir.LibraryDependency? import = getDeferredImport(node);
     ConstantImpactVisitor(this, import, node, staticTypeContext)
         .visitConstant(node.constant);
diff --git a/pkg/compiler/lib/src/ir/modular.dart b/pkg/compiler/lib/src/ir/modular.dart
index c746ded..313a51b 100644
--- a/pkg/compiler/lib/src/ir/modular.dart
+++ b/pkg/compiler/lib/src/ir/modular.dart
@@ -5,12 +5,6 @@
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/type_environment.dart' as ir;
 
-import 'package:front_end/src/api_unstable/dart2js.dart' as ir
-    show LocatedMessage;
-
-import '../diagnostics/diagnostic_listener.dart';
-import '../diagnostics/messages.dart';
-import '../diagnostics/source_span.dart';
 import '../ir/impact_data.dart';
 import '../ir/static_type.dart';
 import '../js_backend/annotations.dart';
@@ -18,7 +12,6 @@
 import '../serialization/serialization.dart';
 import '../util/enumset.dart';
 import 'annotations.dart';
-import 'constants.dart';
 import 'impact.dart';
 import 'scope.dart';
 
@@ -101,28 +94,9 @@
   return ModularMemberData(scopeModel, impactBuilderData);
 }
 
-void reportLocatedMessage(DiagnosticReporter reporter,
-    ir.LocatedMessage message, List<ir.LocatedMessage> context) {
-  DiagnosticMessage diagnosticMessage =
-      _createDiagnosticMessage(reporter, message);
-  var infos = <DiagnosticMessage>[];
-  for (ir.LocatedMessage message in context) {
-    infos.add(_createDiagnosticMessage(reporter, message));
-  }
-  reporter.reportError(diagnosticMessage, infos);
-}
-
-DiagnosticMessage _createDiagnosticMessage(
-    DiagnosticReporter reporter, ir.LocatedMessage message) {
-  var sourceSpan = SourceSpan(
-      message.uri!, message.charOffset, message.charOffset + message.length);
-  return reporter.createMessage(
-      sourceSpan, MessageKind.GENERIC, {'text': message.problemMessage});
-}
-
 class ModularCore {
   final ir.Component component;
-  final Dart2jsConstantEvaluator constantEvaluator;
+  final ir.TypeEnvironment typeEnvironment;
 
-  ModularCore(this.component, this.constantEvaluator);
+  ModularCore(this.component, this.typeEnvironment);
 }
diff --git a/pkg/compiler/lib/src/ir/scope.dart b/pkg/compiler/lib/src/ir/scope.dart
index 2c004c6..01bcc84 100644
--- a/pkg/compiler/lib/src/ir/scope.dart
+++ b/pkg/compiler/lib/src/ir/scope.dart
@@ -3,10 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:kernel/ast.dart' as ir;
+import 'package:kernel/type_environment.dart' as ir;
 import 'closure.dart';
-import 'constants.dart' show Dart2jsConstantEvaluator;
 import 'scope_visitor.dart';
-import 'package:front_end/src/api_prototype/constant_evaluator.dart' as ir;
 
 class ScopeModel {
   final ClosureScopeModel? closureScopeModel;
@@ -20,10 +19,8 @@
 
   /// Inspect members and mark if those members capture any state that needs to
   /// be marked as free variables.
-  factory ScopeModel.from(
-      ir.Member node, ir.ConstantEvaluator constantEvaluator) {
-    ScopeModelBuilder builder =
-        ScopeModelBuilder(constantEvaluator as Dart2jsConstantEvaluator);
+  factory ScopeModel.from(ir.Member node, ir.TypeEnvironment typeEnvironment) {
+    ScopeModelBuilder builder = ScopeModelBuilder(typeEnvironment);
     return builder.computeModel(node);
   }
 }
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index 2f4b26e..463dc88 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -6,7 +6,6 @@
 import 'package:kernel/core_types.dart' as ir;
 import 'package:kernel/type_environment.dart' as ir;
 
-import '../ir/constants.dart';
 import 'closure.dart';
 import 'scope.dart';
 
@@ -16,10 +15,7 @@
 /// variable is being used at any point in the code.
 class ScopeModelBuilder extends ir.VisitorDefault<EvaluationComplexity>
     with VariableCollectorMixin, ir.VisitorThrowingMixin<EvaluationComplexity> {
-  final Dart2jsConstantEvaluator _constantEvaluator;
-  late final ir.StaticTypeContext _staticTypeContext;
-
-  ir.TypeEnvironment get _typeEnvironment => _constantEvaluator.typeEnvironment;
+  final ir.TypeEnvironment _typeEnvironment;
   ir.CoreTypes get _coreTypes => _typeEnvironment.coreTypes;
 
   final ClosureScopeModel _model = ClosureScopeModel();
@@ -86,7 +82,7 @@
   /// type variable usage, such as type argument in method invocations.
   VariableUse? _currentTypeUsage;
 
-  ScopeModelBuilder(this._constantEvaluator);
+  ScopeModelBuilder(this._typeEnvironment);
 
   ScopeModel computeModel(ir.Member node) {
     if (node.isAbstract && !node.isExternal) {
@@ -94,7 +90,6 @@
           initializerComplexity: EvaluationComplexity.lazy());
     }
 
-    _staticTypeContext = ir.StaticTypeContext(node, _typeEnvironment);
     if (node is ir.Constructor) {
       _hasThisLocal = true;
     } else if (node is ir.Procedure && node.kind == ir.ProcedureKind.Factory) {
@@ -136,66 +131,23 @@
     return node.accept(this);
   }
 
-  /// Tries to evaluate [node] as a constant expression.
+  /// Tries to extract the constant expression from a node.
   ///
-  /// If [node] it succeeds, an [EvaluationComplexity] containing the new
+  /// If it succeeds, an [EvaluationComplexity] containing the new
   /// constant is returned. Otherwise a 'lazy' [EvaluationComplexity] is
   /// returned, signaling that [node] is not a constant expression.
   ///
   /// This method should be called in the visit methods of all expressions that
   /// could potentially be constant to bubble up the constness of expressions.
-  ///
-  /// For instance in `var a = 1 + 2` [visitIntLiteral] calls this method
-  /// for `1` and `2` to convert these from int literals to int constants, and
-  /// [visitMethodInvocation] call this method, when seeing that all of its
-  /// subexpressions are constant, and it itself therefore is potentially
-  /// constant, thus computing that `1 + 2` can be replaced by the int constant
-  /// `3`.
-  ///
-  /// Note that [node] is _not_ replaced with a new constant expression. It is
-  /// the responsibility of the caller to do so. This is needed for performance
-  /// reasons since calling `TreeNode.replaceChild` searches linearly through
-  /// the children of the parent node, which lead to a O(n^2) complexity that
-  /// is severe and observable for instance for large list literals.
   EvaluationComplexity _evaluateImplicitConstant(ir.Expression node) {
-    ir.Constant? constant = _constantEvaluator
-        .evaluateOrNull(_staticTypeContext, node, requireConstant: false);
+    ir.Constant? constant =
+        (node is ir.ConstantExpression) ? node.constant : null;
     if (constant != null) {
       return EvaluationComplexity.constant(constant);
     }
     return const EvaluationComplexity.lazy();
   }
 
-  /// The evaluation complexity of the last visited expression.
-  // TODO(48820): Pre-NNBD we gained some benefit from the `null` default
-  // value. It is too painful to add `!` after every access so this is
-  // initialized to a 'harmless' value.  Should we add an invalid value
-  // 'ExpressionComplexity.invalid()` and check in the combiner and other
-  // use-sites that the value is not 'invalid'?
-  EvaluationComplexity _lastExpressionComplexity =
-      const EvaluationComplexity.constant();
-
-  /// Visit [node] and returns the corresponding `ConstantExpression` if [node]
-  /// evaluated to a constant.
-  ///
-  /// This method stores the complexity of [node] in [_lastExpressionComplexity]
-  /// and sets the parent of the created `ConstantExpression` to the parent
-  /// of [node]. The caller must replace [node] within the parent node. This
-  /// is done to avoid calling `Node.replaceChild` which searches linearly
-  /// through the children nodes `node.parent` in order to replace `node` which
-  /// results in O(n^2) complexity of replacing elements in for instance a list
-  /// of `n` elements.
-  ir.Expression _handleExpression(ir.Expression node) {
-    _lastExpressionComplexity = visitNode(node);
-    if (_lastExpressionComplexity.isFreshConstant) {
-      return ir.ConstantExpression(_lastExpressionComplexity.constant!,
-          node.getStaticType(_staticTypeContext))
-        ..fileOffset = node.fileOffset
-        ..parent = node.parent;
-    }
-    return node;
-  }
-
   /// Visit all [nodes] returning the combined complexity.
   EvaluationComplexity visitNodes(List<ir.Node> nodes) {
     EvaluationComplexity complexity = const EvaluationComplexity.constant();
@@ -207,15 +159,14 @@
 
   /// Visit all [nodes] returning the combined complexity.
   ///
-  /// If subexpressions can be evaluated as constants, they are replaced by
-  /// constant expressions in [nodes].
+  /// Assumes that the Kernel AST already contains simplified constant and
+  /// constant-like expressions.
   EvaluationComplexity visitExpressions(List<ir.Expression> nodes) {
     EvaluationComplexity combinedComplexity =
         const EvaluationComplexity.constant();
     for (int i = 0; i < nodes.length; i++) {
-      nodes[i] = _handleExpression(nodes[i]);
-      combinedComplexity =
-          combinedComplexity.combine(_lastExpressionComplexity);
+      final complexity = visitNode(nodes[i]);
+      combinedComplexity = combinedComplexity.combine(complexity);
     }
     return combinedComplexity;
   }
@@ -223,9 +174,8 @@
   EvaluationComplexity visitNamedExpressions(
       List<ir.NamedExpression> named, EvaluationComplexity combinedComplexity) {
     for (int i = 0; i < named.length; i++) {
-      named[i].value = _handleExpression(named[i].value);
-      combinedComplexity =
-          combinedComplexity.combine(_lastExpressionComplexity);
+      final complexity = visitNode(named[i].value);
+      combinedComplexity = combinedComplexity.combine(complexity);
     }
     return combinedComplexity;
   }
@@ -348,7 +298,7 @@
     _mutatedVariables.add(node.variable);
     _markVariableAsUsed(node.variable, VariableUse.explicit);
     visitInContext(node.variable.type, VariableUse.localType);
-    node.value = _handleExpression(node.value);
+    visitNode(node.value);
     registerAssignedVariable(node.variable);
     return const EvaluationComplexity.lazy();
   }
@@ -361,7 +311,7 @@
 
     visitInContext(node.type, usage);
     if (node.initializer != null) {
-      node.initializer = _handleExpression(node.initializer!);
+      visitNode(node.initializer!);
     }
   }
 
@@ -482,7 +432,7 @@
     enterNewScope(node, () {
       visitNode(node.variable);
       visitInVariableScope(node, () {
-        node.iterable = _handleExpression(node.iterable);
+        visitNode(node.iterable);
         visitNode(node.body);
       });
     });
@@ -496,7 +446,7 @@
   EvaluationComplexity visitWhileStatement(ir.WhileStatement node) {
     enterNewScope(node, () {
       visitInVariableScope(node, () {
-        node.condition = _handleExpression(node.condition);
+        visitNode(node.condition);
         visitNode(node.body);
       });
     });
@@ -508,7 +458,7 @@
     enterNewScope(node, () {
       visitInVariableScope(node, () {
         visitNode(node.body);
-        node.condition = _handleExpression(node.condition);
+        visitNode(node.condition);
       });
     });
     return const EvaluationComplexity.lazy();
@@ -539,7 +489,7 @@
       // condition or body are indeed flagged as mutated.
       visitInVariableScope(node, () {
         if (node.condition != null) {
-          node.condition = _handleExpression(node.condition!);
+          visitNode(node.condition!);
         }
         visitNode(node.body);
       });
@@ -589,7 +539,7 @@
     if (_hasThisLocal) {
       _registerNeedsThis(VariableUse.explicit);
     }
-    node.value = _handleExpression(node.value);
+    visitNode(node.value);
     return const EvaluationComplexity.lazy();
   }
 
@@ -676,8 +626,7 @@
     late final EvaluationComplexity complexity;
     visitInvokable(node, () {
       assert(node.initializer != null);
-      node.initializer = _handleExpression(node.initializer!);
-      complexity = _lastExpressionComplexity;
+      complexity = visitNode(node.initializer!);
     });
     _currentTypeUsage = null;
     return complexity;
@@ -822,8 +771,7 @@
 
   @override
   EvaluationComplexity visitIsExpression(ir.IsExpression node) {
-    node.operand = _handleExpression(node.operand);
-    EvaluationComplexity complexity = _lastExpressionComplexity;
+    EvaluationComplexity complexity = visitNode(node.operand);
     visitInContext(node.type, VariableUse.explicit);
     if (complexity.isConstant) {
       return _evaluateImplicitConstant(node);
@@ -833,8 +781,7 @@
 
   @override
   EvaluationComplexity visitAsExpression(ir.AsExpression node) {
-    node.operand = _handleExpression(node.operand);
-    EvaluationComplexity complexity = _lastExpressionComplexity;
+    EvaluationComplexity complexity = visitNode(node.operand);
     visitInContext(node.type,
         node.isTypeError ? VariableUse.implicitCast : VariableUse.explicit);
     if (complexity.isConstant) {
@@ -845,8 +792,7 @@
 
   @override
   EvaluationComplexity visitNullCheck(ir.NullCheck node) {
-    node.operand = _handleExpression(node.operand);
-    EvaluationComplexity complexity = _lastExpressionComplexity;
+    EvaluationComplexity complexity = visitNode(node.operand);
     if (complexity.isConstant) {
       return _evaluateImplicitConstant(node);
     }
@@ -855,13 +801,13 @@
 
   @override
   EvaluationComplexity visitAwaitExpression(ir.AwaitExpression node) {
-    node.operand = _handleExpression(node.operand);
+    visitNode(node.operand);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitYieldStatement(ir.YieldStatement node) {
-    node.expression = _handleExpression(node.expression);
+    visitNode(node.expression);
     return const EvaluationComplexity.lazy();
   }
 
@@ -975,11 +921,8 @@
 
   @override
   EvaluationComplexity visitMapLiteralEntry(ir.MapLiteralEntry node) {
-    node.key = _handleExpression(node.key);
-    EvaluationComplexity keyComplexity = _lastExpressionComplexity;
-
-    node.value = _handleExpression(node.value);
-    EvaluationComplexity valueComplexity = _lastExpressionComplexity;
+    EvaluationComplexity keyComplexity = visitNode(node.key);
+    EvaluationComplexity valueComplexity = visitNode(node.value);
 
     return keyComplexity.combine(valueComplexity);
   }
@@ -1046,7 +989,7 @@
 
   @override
   EvaluationComplexity visitStaticSet(ir.StaticSet node) {
-    node.value = _handleExpression(node.value);
+    visitNode(node.value);
     return const EvaluationComplexity.lazy();
   }
 
@@ -1106,14 +1049,11 @@
   @override
   EvaluationComplexity visitConditionalExpression(
       ir.ConditionalExpression node) {
-    node.condition = _handleExpression(node.condition);
-    EvaluationComplexity conditionComplexity = _lastExpressionComplexity;
+    EvaluationComplexity conditionComplexity = visitNode(node.condition);
 
-    node.then = _handleExpression(node.then);
-    EvaluationComplexity thenComplexity = _lastExpressionComplexity;
+    EvaluationComplexity thenComplexity = visitNode(node.then);
 
-    node.otherwise = _handleExpression(node.otherwise);
-    EvaluationComplexity elseComplexity = _lastExpressionComplexity;
+    EvaluationComplexity elseComplexity = visitNode(node.otherwise);
 
     EvaluationComplexity complexity =
         conditionComplexity.combine(thenComplexity).combine(elseComplexity);
@@ -1126,8 +1066,7 @@
 
   @override
   EvaluationComplexity visitInstanceInvocation(ir.InstanceInvocation node) {
-    node.receiver = _handleExpression(node.receiver);
-    EvaluationComplexity receiverComplexity = _lastExpressionComplexity;
+    EvaluationComplexity receiverComplexity = visitNode(node.receiver);
     if (node.arguments.types.isNotEmpty) {
       ir.TreeNode receiver = node.receiver;
       assert(
@@ -1154,7 +1093,7 @@
   @override
   EvaluationComplexity visitInstanceGetterInvocation(
       ir.InstanceGetterInvocation node) {
-    node.receiver = _handleExpression(node.receiver);
+    visitNode(node.receiver);
     if (node.arguments.types.isNotEmpty) {
       ir.TreeNode receiver = node.receiver;
       assert(
@@ -1171,7 +1110,7 @@
 
   @override
   EvaluationComplexity visitDynamicInvocation(ir.DynamicInvocation node) {
-    node.receiver = _handleExpression(node.receiver);
+    visitNode(node.receiver);
     if (node.arguments.types.isNotEmpty) {
       ir.TreeNode receiver = node.receiver;
       assert(
@@ -1188,7 +1127,7 @@
 
   @override
   EvaluationComplexity visitFunctionInvocation(ir.FunctionInvocation node) {
-    node.receiver = _handleExpression(node.receiver);
+    visitNode(node.receiver);
     if (node.arguments.types.isNotEmpty) {
       assert(
           !(node.receiver is ir.VariableGet &&
@@ -1218,8 +1157,7 @@
 
   @override
   EvaluationComplexity visitEqualsNull(ir.EqualsNull node) {
-    node.expression = _handleExpression(node.expression);
-    EvaluationComplexity receiverComplexity = _lastExpressionComplexity;
+    EvaluationComplexity receiverComplexity = visitNode(node.expression);
     if (receiverComplexity.isConstant) {
       return _evaluateImplicitConstant(node);
     }
@@ -1228,10 +1166,8 @@
 
   @override
   EvaluationComplexity visitEqualsCall(ir.EqualsCall node) {
-    node.left = _handleExpression(node.left);
-    EvaluationComplexity leftComplexity = _lastExpressionComplexity;
-    node.right = _handleExpression(node.right);
-    EvaluationComplexity rightComplexity = _lastExpressionComplexity;
+    EvaluationComplexity leftComplexity = visitNode(node.left);
+    EvaluationComplexity rightComplexity = visitNode(node.right);
     if (leftComplexity.combine(rightComplexity).isConstant) {
       return _evaluateImplicitConstant(node);
     }
@@ -1240,8 +1176,7 @@
 
   @override
   EvaluationComplexity visitInstanceGet(ir.InstanceGet node) {
-    node.receiver = _handleExpression(node.receiver);
-    EvaluationComplexity complexity = _lastExpressionComplexity;
+    EvaluationComplexity complexity = visitNode(node.receiver);
     if (complexity.isConstant && node.name.text == 'length') {
       return _evaluateImplicitConstant(node);
     }
@@ -1250,26 +1185,25 @@
 
   @override
   EvaluationComplexity visitInstanceTearOff(ir.InstanceTearOff node) {
-    node.receiver = _handleExpression(node.receiver);
+    visitNode(node.receiver);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitRecordIndexGet(ir.RecordIndexGet node) {
-    node.receiver = _handleExpression(node.receiver);
+    visitNode(node.receiver);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitRecordNameGet(ir.RecordNameGet node) {
-    node.receiver = _handleExpression(node.receiver);
+    visitNode(node.receiver);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitDynamicGet(ir.DynamicGet node) {
-    node.receiver = _handleExpression(node.receiver);
-    EvaluationComplexity complexity = _lastExpressionComplexity;
+    EvaluationComplexity complexity = visitNode(node.receiver);
     if (complexity.isConstant && node.name.text == 'length') {
       return _evaluateImplicitConstant(node);
     }
@@ -1278,28 +1212,27 @@
 
   @override
   EvaluationComplexity visitFunctionTearOff(ir.FunctionTearOff node) {
-    node.receiver = _handleExpression(node.receiver);
+    visitNode(node.receiver);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitInstanceSet(ir.InstanceSet node) {
-    node.receiver = _handleExpression(node.receiver);
-    node.value = _handleExpression(node.value);
+    visitNode(node.receiver);
+    visitNode(node.value);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitDynamicSet(ir.DynamicSet node) {
-    node.receiver = _handleExpression(node.receiver);
-    node.value = _handleExpression(node.value);
+    visitNode(node.receiver);
+    visitNode(node.value);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitNot(ir.Not node) {
-    node.operand = _handleExpression(node.operand);
-    EvaluationComplexity complexity = _lastExpressionComplexity;
+    EvaluationComplexity complexity = visitNode(node.operand);
     if (complexity.isConstant) {
       return _evaluateImplicitConstant(node);
     }
@@ -1308,11 +1241,9 @@
 
   @override
   EvaluationComplexity visitLogicalExpression(ir.LogicalExpression node) {
-    node.left = _handleExpression(node.left);
-    EvaluationComplexity leftComplexity = _lastExpressionComplexity;
+    EvaluationComplexity leftComplexity = visitNode(node.left);
 
-    node.right = _handleExpression(node.right);
-    EvaluationComplexity rightComplexity = _lastExpressionComplexity;
+    EvaluationComplexity rightComplexity = visitNode(node.right);
 
     EvaluationComplexity complexity = leftComplexity.combine(rightComplexity);
     if (complexity.isConstant) {
@@ -1324,14 +1255,14 @@
   @override
   EvaluationComplexity visitLet(ir.Let node) {
     visitNode(node.variable);
-    node.body = _handleExpression(node.body);
+    visitNode(node.body);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitBlockExpression(ir.BlockExpression node) {
     visitNode(node.body);
-    node.value = _handleExpression(node.value);
+    visitNode(node.value);
     return const EvaluationComplexity.lazy();
   }
 
@@ -1354,8 +1285,7 @@
   EvaluationComplexity visitInstantiation(ir.Instantiation node) {
     EvaluationComplexity typeArgumentsComplexity = visitNodesInContext(
         node.typeArguments, VariableUse.instantiationTypeArgument(node));
-    node.expression = _handleExpression(node.expression);
-    EvaluationComplexity expressionComplexity = _lastExpressionComplexity;
+    EvaluationComplexity expressionComplexity = visitNode(node.expression);
 
     EvaluationComplexity complexity =
         typeArgumentsComplexity.combine(expressionComplexity);
@@ -1367,7 +1297,7 @@
 
   @override
   EvaluationComplexity visitThrow(ir.Throw node) {
-    node.expression = _handleExpression(node.expression);
+    visitNode(node.expression);
     return const EvaluationComplexity.lazy();
   }
 
@@ -1384,9 +1314,9 @@
   @override
   EvaluationComplexity visitAssertStatement(ir.AssertStatement node) {
     visitInVariableScope(node, () {
-      node.condition = _handleExpression(node.condition);
+      visitNode(node.condition);
       if (node.message != null) {
-        node.message = _handleExpression(node.message!);
+        visitNode(node.message!);
       }
     });
     return const EvaluationComplexity.lazy();
@@ -1395,7 +1325,7 @@
   @override
   EvaluationComplexity visitReturnStatement(ir.ReturnStatement node) {
     if (node.expression != null) {
-      node.expression = _handleExpression(node.expression!);
+      visitNode(node.expression!);
     }
     return const EvaluationComplexity.lazy();
   }
@@ -1407,13 +1337,13 @@
 
   @override
   EvaluationComplexity visitExpressionStatement(ir.ExpressionStatement node) {
-    node.expression = _handleExpression(node.expression);
+    visitNode(node.expression);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitSwitchStatement(ir.SwitchStatement node) {
-    node.expression = _handleExpression(node.expression);
+    visitNode(node.expression);
     visitInVariableScope(node, () {
       visitNodes(node.cases);
     });
@@ -1446,14 +1376,14 @@
 
   @override
   EvaluationComplexity visitFieldInitializer(ir.FieldInitializer node) {
-    node.value = _handleExpression(node.value);
+    visitNode(node.value);
     return const EvaluationComplexity.lazy();
   }
 
   @override
   EvaluationComplexity visitLocalInitializer(ir.LocalInitializer node) {
     if (node.variable.initializer != null) {
-      node.variable.initializer = _handleExpression(node.variable.initializer!);
+      visitNode(node.variable.initializer!);
     }
     return const EvaluationComplexity.lazy();
   }
@@ -1498,9 +1428,7 @@
 
   @override
   EvaluationComplexity visitConstantExpression(ir.ConstantExpression node) {
-    if (node.constant is ir.UnevaluatedConstant) {
-      node.constant = _constantEvaluator.evaluate(_staticTypeContext, node);
-    }
+    assert(node.constant is! ir.UnevaluatedConstant);
     return const EvaluationComplexity.constant();
   }
 
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 a1ce910..5ea8aaa 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -2,8 +2,6 @@
 // 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:front_end/src/api_unstable/dart2js.dart' as ir;
-
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart' as ir;
 import 'package:kernel/core_types.dart' as ir;
@@ -22,10 +20,8 @@
 import '../elements/entity_map.dart';
 import '../elements/names.dart';
 import '../elements/types.dart';
-import '../environment.dart';
 import '../ir/cached_static_type.dart';
 import '../ir/closure.dart';
-import '../ir/constants.dart';
 import '../ir/element_map.dart';
 import '../ir/types.dart';
 import '../ir/visitors.dart';
@@ -72,7 +68,6 @@
   final CompilerOptions options;
   @override
   final DiagnosticReporter reporter;
-  final Environment _environment;
   late final JCommonElements _commonElements;
   late final JsElementEnvironment _elementEnvironment;
   late final DartTypeConverter _typeConverter;
@@ -124,7 +119,6 @@
 
   JsKernelToElementMap(
       this.reporter,
-      this._environment,
       KernelToElementMap _elementMap,
       Map<MemberEntity, MemberUsage> liveMemberUsage,
       Iterable<MemberEntity> liveAbstractMembers,
@@ -248,7 +242,7 @@
   }
 
   JsKernelToElementMap.readFromDataSource(this.options, this.reporter,
-      this._environment, ir.Component component, DataSourceReader source) {
+      ir.Component component, DataSourceReader source) {
     _elementEnvironment = JsElementEnvironment(this);
     _typeConverter = DartTypeConverter(this);
     _types = KernelDartTypes(this, options);
@@ -1076,16 +1070,6 @@
     return ir.StaticTypeContext(node, typeEnvironment);
   }
 
-  late final Dart2jsConstantEvaluator constantEvaluator =
-      Dart2jsConstantEvaluator(programEnv.mainComponent, typeEnvironment,
-          (ir.LocatedMessage message, List<ir.LocatedMessage>? context) {
-    reportLocatedMessage(reporter, message, context);
-  },
-          environment: _environment,
-          evaluationMode: options.useLegacySubtyping
-              ? ir.EvaluationMode.weak
-              : ir.EvaluationMode.strong);
-
   @override
   StaticTypeProvider getStaticTypeProvider(MemberEntity member) {
     MemberDefinition memberDefinition =
@@ -1399,29 +1383,12 @@
       return NullConstantValue();
     } else if (node is ir.ConstantExpression) {
       return _constantValuefier.visitConstant(node.constant);
+    } else if (requireConstant) {
+      throw UnsupportedError(
+          'No constant for ${DebugPrinter.prettyPrint(node)}');
     } else {
-      // TODO(johnniwinther,sigmund): Effectively constant expressions should
-      // be replaced in the scope visitor as part of the initializer complexity
-      // computation.
-      ir.StaticTypeContext staticTypeContext =
-          getStaticTypeContext(memberContext!);
-      ir.Constant? constant = constantEvaluator.evaluateOrNull(
-          staticTypeContext, node,
-          requireConstant: requireConstant);
-      if (constant == null) {
-        if (requireConstant) {
-          throw UnsupportedError(
-              'No constant for ${DebugPrinter.prettyPrint(node)}');
-        }
-      } else {
-        ConstantValue value = _constantValuefier.visitConstant(constant);
-        if (!value.isConstant && !requireConstant) {
-          return null;
-        }
-        return value;
-      }
+      return null;
     }
-    return null;
   }
 
   @override
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index c84eba8..11b3b44 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -166,7 +166,6 @@
     KernelFrontendStrategy strategy = _compiler.frontendStrategy;
     _elementMap = JsKernelToElementMap(
         _compiler.reporter,
-        _compiler.environment,
         strategy.elementMap,
         closedWorld.liveMemberUsage,
         closedWorld.liveAbstractInstanceMembers,
diff --git a/pkg/compiler/lib/src/js_model/js_world.dart b/pkg/compiler/lib/src/js_model/js_world.dart
index fd955ef..d8ea1ca 100644
--- a/pkg/compiler/lib/src/js_model/js_world.dart
+++ b/pkg/compiler/lib/src/js_model/js_world.dart
@@ -16,7 +16,6 @@
 import '../elements/entity_utils.dart' as utils;
 import '../elements/names.dart';
 import '../elements/types.dart';
-import '../environment.dart';
 import '../inferrer/abstract_value_domain.dart';
 import '../inferrer/abstract_value_strategy.dart';
 import '../js_emitter/sorter.dart';
@@ -149,14 +148,13 @@
   factory JClosedWorld.readFromDataSource(
       CompilerOptions options,
       DiagnosticReporter reporter,
-      Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
       DataSourceReader source) {
     source.begin(tag);
 
     JsKernelToElementMap elementMap = JsKernelToElementMap.readFromDataSource(
-        options, reporter, environment, component, source);
+        options, reporter, component, source);
     ClassHierarchy classHierarchy =
         ClassHierarchy.readFromDataSource(source, elementMap.commonElements);
     NativeData nativeData =
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 49ed873..360a581 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -2,7 +2,6 @@
 // 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:front_end/src/api_unstable/dart2js.dart' as ir;
 import 'package:js_shared/synced/embedded_names.dart' show JsGetName;
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart' as ir;
@@ -19,9 +18,7 @@
 import '../elements/entity_map.dart';
 import '../elements/names.dart';
 import '../elements/types.dart';
-import '../environment.dart';
 import '../ir/annotations.dart';
-import '../ir/constants.dart';
 import '../ir/element_map.dart';
 import '../ir/impact.dart';
 import '../ir/impact_data.dart';
@@ -62,7 +59,6 @@
   final CompilerOptions options;
   @override
   final DiagnosticReporter reporter;
-  final Environment _environment;
   final NativeBasicDataBuilder nativeBasicDataBuilder =
       NativeBasicDataBuilder();
   NativeBasicData? _nativeBasicData;
@@ -73,7 +69,6 @@
   ir.CoreTypes? _coreTypes;
   ir.TypeEnvironment? _typeEnvironment;
   ir.ClassHierarchy? _classHierarchy;
-  Dart2jsConstantEvaluator? _constantEvaluator;
   late final ConstantValuefier _constantValuefier;
 
   /// Library environment. Used for fast lookup.
@@ -115,7 +110,7 @@
   Map<JMember, Map<ir.Expression, TypeMap>>? typeMapsForTesting;
   Map<ir.Member, ImpactData>? impactDataForTesting;
 
-  KernelToElementMap(this.reporter, this._environment, this.options) {
+  KernelToElementMap(this.reporter, this.options) {
     _elementEnvironment = KernelElementEnvironment(this);
     _typeConverter = DartTypeConverter(this);
     _types = KernelDartTypes(this, options);
@@ -819,18 +814,6 @@
         getMemberNode(member as JMember), typeEnvironment);
   }
 
-  Dart2jsConstantEvaluator get constantEvaluator {
-    return _constantEvaluator ??=
-        Dart2jsConstantEvaluator(env.mainComponent, typeEnvironment,
-            (ir.LocatedMessage message, List<ir.LocatedMessage>? context) {
-      reportLocatedMessage(reporter, message, context);
-    },
-            environment: _environment,
-            evaluationMode: options.useLegacySubtyping
-                ? ir.EvaluationMode.weak
-                : ir.EvaluationMode.strong);
-  }
-
   @override
   Name getName(ir.Name name, {bool setter = false}) {
     return Name(name.text, name.isPrivate ? name.library!.importUri : null,
@@ -1111,9 +1094,7 @@
       }
       return NullConstantValue();
     }
-    ir.Constant? constant = constantEvaluator.evaluateOrNull(
-        staticTypeContext, node,
-        requireConstant: requireConstant);
+    final constant = node is ir.ConstantExpression ? node.constant : null;
     if (constant == null) {
       if (requireConstant) {
         throw UnsupportedError(
@@ -2101,25 +2082,6 @@
   }
 }
 
-DiagnosticMessage _createDiagnosticMessage(
-    DiagnosticReporter reporter, ir.LocatedMessage message) {
-  SourceSpan sourceSpan = SourceSpan(
-      message.uri!, message.charOffset, message.charOffset + message.length);
-  return reporter.createMessage(
-      sourceSpan, MessageKind.GENERIC, {'text': message.problemMessage});
-}
-
-void reportLocatedMessage(DiagnosticReporter reporter,
-    ir.LocatedMessage message, List<ir.LocatedMessage>? context) {
-  DiagnosticMessage diagnosticMessage =
-      _createDiagnosticMessage(reporter, message);
-  List<DiagnosticMessage> infos = [];
-  for (ir.LocatedMessage message in context ?? const []) {
-    infos.add(_createDiagnosticMessage(reporter, message));
-  }
-  reporter.reportError(diagnosticMessage, infos);
-}
-
 ForeignKind getForeignKindFromName(String name) {
   switch (name) {
     case Identifiers.JS:
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index cd08bd0..33982b9 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -15,7 +15,6 @@
 import '../deferred_load/deferred_load.dart' show DeferredLoadTask;
 import '../elements/entities.dart';
 import '../enqueue.dart';
-import '../environment.dart' as env;
 import '../ir/annotations.dart';
 import '../ir/closure.dart' show ClosureScopeModel;
 import '../ir/impact.dart';
@@ -85,9 +84,9 @@
   /// Support for classifying `noSuchMethod` implementations.
   late NoSuchMethodRegistry noSuchMethodRegistry;
 
-  KernelFrontendStrategy(this._compilerTask, this._options,
-      DiagnosticReporter reporter, env.Environment environment)
-      : _elementMap = KernelToElementMap(reporter, environment, _options) {
+  KernelFrontendStrategy(
+      this._compilerTask, this._options, DiagnosticReporter reporter)
+      : _elementMap = KernelToElementMap(reporter, _options) {
     _modularStrategy = KernelModularStrategy(_compilerTask, _elementMap);
     noSuchMethodRegistry =
         NoSuchMethodRegistry(commonElements, NoSuchMethodResolver(_elementMap));
@@ -213,11 +212,16 @@
         annotationsData);
   }
 
+  /// Registers a component with this strategy.
+  void registerComponent(ir.Component component) {
+    _elementMap.addComponent(component);
+  }
+
   /// Registers a set of loaded libraries with this strategy.
   void registerLoadedLibraries(ir.Component component, List<Uri> libraries) {
-    _elementMap.addComponent(component);
-    _irAnnotationData = processAnnotations(
-        ModularCore(component, _elementMap.constantEvaluator));
+    registerComponent(component);
+    _irAnnotationData =
+        processAnnotations(ModularCore(component, _elementMap.typeEnvironment));
     _annotationProcessor = KernelAnnotationProcessor(
         elementMap, elementMap.nativeBasicDataBuilder, _irAnnotationData);
     for (Uri uri in libraries) {
@@ -433,7 +437,7 @@
   ModularMemberData getModularMemberData(
       ir.Member node, EnumSet<PragmaAnnotation> annotations) {
     ScopeModel scopeModel = _compilerTask.measureSubtask(
-        'closures', () => ScopeModel.from(node, _elementMap.constantEvaluator));
+        'closures', () => ScopeModel.from(node, _elementMap.typeEnvironment));
     return _compilerTask.measureSubtask('worldImpact', () {
       return computeModularMemberData(
           _elementMap, node, scopeModel, annotations);
@@ -464,7 +468,7 @@
       ir.Member node, EnumSet<PragmaAnnotation> annotations) {
     // TODO(joshualitt): serialize scope model too.
     var scopeModel = _compilerTask.measureSubtask(
-        'closures', () => ScopeModel.from(node, _elementMap.constantEvaluator));
+        'closures', () => ScopeModel.from(node, _elementMap.typeEnvironment));
     var impactBuilderData = _cache[node];
     if (impactBuilderData == null) {
       throw 'missing modular analysis data for $node';
diff --git a/pkg/compiler/lib/src/kernel/transformations/global/constant_transformer.dart b/pkg/compiler/lib/src/kernel/transformations/global/constant_transformer.dart
new file mode 100644
index 0000000..57511f5
--- /dev/null
+++ b/pkg/compiler/lib/src/kernel/transformations/global/constant_transformer.dart
@@ -0,0 +1,282 @@
+import 'package:front_end/src/api_unstable/dart2js.dart'
+    show TryConstantEvaluator;
+import 'package:kernel/ast.dart';
+import 'package:kernel/type_environment.dart';
+
+class ConstantTransformer extends Transformer {
+  final TryConstantEvaluator constantEvaluator;
+  late StaticTypeContext _staticTypeContext;
+
+  ConstantTransformer(this.constantEvaluator);
+
+  Constant _evaluate(Expression node) =>
+      constantEvaluator.evaluate(_staticTypeContext, node);
+
+  Constant? _evaluateOrNull(Expression node) => constantEvaluator
+      .evaluateOrNull(_staticTypeContext, node, requireConstant: false);
+
+  ConstantExpression _evaluateAndWrap(Expression node) {
+    return ConstantExpression(
+        _evaluate(node), node.getStaticType(_staticTypeContext))
+      ..fileOffset = node.fileOffset;
+  }
+
+  Expression _evaluateAndWrapOrNode(Expression node) {
+    final constantOrNull = _evaluateOrNull(node);
+    if (constantOrNull == null) return node;
+    return ConstantExpression(
+        constantOrNull, node.getStaticType(_staticTypeContext))
+      ..fileOffset = node.fileOffset;
+  }
+
+  bool _isConstant(Expression node) => node is ConstantExpression;
+  bool _isConstantArguments(Arguments arguments) =>
+      arguments.positional.every(_isConstant) &&
+      arguments.named.every((e) => _isConstant(e.value));
+
+  @override
+  TreeNode visitLibrary(Library node) {
+    _staticTypeContext = StaticTypeContext.forAnnotations(
+        node, constantEvaluator.typeEnvironment);
+    node.transformChildren(this);
+    return node;
+  }
+
+  @override
+  TreeNode defaultMember(Member node) {
+    _setupTypeContextForMember(node);
+    node.transformChildren(this);
+    return node;
+  }
+
+  void _setupTypeContextForMember(Member node) {
+    _staticTypeContext =
+        StaticTypeContext(node, constantEvaluator.typeEnvironment);
+  }
+
+  @override
+  TreeNode visitFieldInitializer(FieldInitializer node) {
+    node.transformChildren(this);
+    final newInitializer = _evaluateAndWrapOrNode(node.value);
+    if (_isConstant(newInitializer)) {
+      node.value = newInitializer;
+      newInitializer.parent = node;
+    } else if (newInitializer is VariableGet) {
+      VariableDeclaration parameter = newInitializer.variable;
+      final parameterInitializer = parameter.initializer;
+      if (parameterInitializer != null) {
+        Expression newParameterInitializer =
+            _evaluateAndWrapOrNode(parameterInitializer);
+
+        newParameterInitializer.parent = parameter;
+        parameter.initializer = newParameterInitializer;
+      }
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitField(Field node) {
+    _setupTypeContextForMember(node);
+    node.transformChildren(this);
+    final initializer = node.initializer;
+    if (initializer != null) {
+      node.initializer = _evaluateAndWrapOrNode(initializer)..parent = node;
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitSwitchCase(SwitchCase node) {
+    node.transformChildren(this);
+    for (int i = 0; i < node.expressions.length; i++) {
+      node.expressions[i] = _evaluateAndWrap(node.expressions[i])
+        ..parent = node;
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitConstantExpression(ConstantExpression node) {
+    return _evaluateAndWrap(node);
+  }
+
+  @override
+  TreeNode visitIsExpression(IsExpression node) {
+    node.transformChildren(this);
+    if (_isConstant(node.operand)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitAsExpression(AsExpression node) {
+    node.transformChildren(this);
+    if (_isConstant(node.operand)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitNullCheck(NullCheck node) {
+    node.transformChildren(this);
+    if (_isConstant(node.operand)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitTypeLiteral(TypeLiteral node) => _evaluateAndWrapOrNode(node);
+
+  @override
+  TreeNode visitRecordLiteral(RecordLiteral node) {
+    node.transformChildren(this);
+    if (node.positional.every(_isConstant) &&
+        node.named.every((e) => _isConstant(e.value))) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitNullLiteral(NullLiteral node) => _evaluateAndWrap(node);
+
+  @override
+  TreeNode visitStringLiteral(StringLiteral node) => _evaluateAndWrap(node);
+
+  @override
+  TreeNode visitIntLiteral(IntLiteral node) => _evaluateAndWrap(node);
+
+  @override
+  TreeNode visitDoubleLiteral(DoubleLiteral node) => _evaluateAndWrap(node);
+
+  @override
+  TreeNode visitSymbolLiteral(SymbolLiteral node) => _evaluateAndWrap(node);
+
+  @override
+  TreeNode visitBoolLiteral(BoolLiteral node) => _evaluateAndWrap(node);
+
+  @override
+  TreeNode visitStringConcatenation(StringConcatenation node) {
+    node.transformChildren(this);
+    if (node.expressions.every(_isConstant)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitStaticGet(StaticGet node) {
+    node.transformChildren(this);
+    final target = node.target;
+    if (target is Procedure && target.kind == ProcedureKind.Method) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitStaticTearOff(StaticTearOff node) {
+    node.transformChildren(this);
+    return _evaluateAndWrapOrNode(node);
+  }
+
+  @override
+  TreeNode visitStaticInvocation(StaticInvocation node) {
+    node.transformChildren(this);
+    if (_isConstantArguments(node.arguments) &&
+        node.target == constantEvaluator.coreTypes.identicalProcedure) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitConditionalExpression(ConditionalExpression node) {
+    node.transformChildren(this);
+    if (_isConstant(node.condition) &&
+        _isConstant(node.then) &&
+        _isConstant(node.otherwise)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitInstanceInvocation(InstanceInvocation node) {
+    node.transformChildren(this);
+    final interfaceTarget = node.interfaceTarget;
+    if (_isConstantArguments(node.arguments) &&
+        _isConstant(node.receiver) &&
+        interfaceTarget.kind == ProcedureKind.Operator) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitEqualsNull(EqualsNull node) {
+    node.transformChildren(this);
+    if (_isConstant(node.expression)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitEqualsCall(EqualsCall node) {
+    node.transformChildren(this);
+    if (_isConstant(node.left) && _isConstant(node.right)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitInstanceGet(InstanceGet node) {
+    node.transformChildren(this);
+    if (_isConstant(node.receiver) && node.name.text == 'length') {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitDynamicGet(DynamicGet node) {
+    node.transformChildren(this);
+    if (_isConstant(node.receiver) && node.name.text == 'length') {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitNot(Not node) {
+    node.transformChildren(this);
+    if (_isConstant(node.operand)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitLogicalExpression(LogicalExpression node) {
+    node.transformChildren(this);
+    if (_isConstant(node.left) && _isConstant(node.right)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitInstantiation(Instantiation node) {
+    node.transformChildren(this);
+    if (_isConstant(node.expression)) {
+      return _evaluateAndWrapOrNode(node);
+    }
+    return node;
+  }
+}
diff --git a/pkg/compiler/lib/src/kernel/transformations/global/js_get_flag_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/global/js_get_flag_lowering.dart
index e4834b7..2623174 100644
--- a/pkg/compiler/lib/src/kernel/transformations/global/js_get_flag_lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/global/js_get_flag_lowering.dart
@@ -30,7 +30,7 @@
 
   JsGetFlagLowering(this._coreTypes, this._options);
 
-  TreeNode transformStaticInvocation(StaticInvocation node) {
+  Expression transformStaticInvocation(StaticInvocation node) {
     if (node.target != _coreTypes.jsGetFlag) return node;
     final argument = node.arguments.positional.single;
 
diff --git a/pkg/compiler/lib/src/kernel/transformations/global/transform.dart b/pkg/compiler/lib/src/kernel/transformations/global/transform.dart
index 983714d..72708c4 100644
--- a/pkg/compiler/lib/src/kernel/transformations/global/transform.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/global/transform.dart
@@ -2,23 +2,39 @@
 // 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:front_end/src/api_unstable/dart2js.dart'
+    show TryConstantEvaluator;
 import 'package:kernel/ast.dart';
 import 'package:kernel/core_types.dart';
 
 import '../../../options.dart';
 import 'clone_mixin_methods_with_super.dart' as transformMixins;
+import 'constant_transformer.dart';
 import 'js_get_flag_lowering.dart';
 
 void transformLibraries(
-    List<Library> libraries, CoreTypes coreTypes, CompilerOptions options) {
-  final transformer = _GlobalTransformer(coreTypes, options);
+    List<Library> libraries,
+    TryConstantEvaluator constantEvaluator,
+    CoreTypes coreTypes,
+    CompilerOptions options) {
+  final transformer = _GlobalTransformer(constantEvaluator, coreTypes, options);
   libraries.forEach(transformer.visitLibrary);
 }
 
-class _GlobalTransformer extends Transformer {
+/// Responsible for performing global transformations on the Kernel AST.
+///
+/// Transformations facilitated by this class include:
+/// 1) Invoke [JsGetFlagLowering] which replaces static invocations of the form
+/// `JS_GET_FLAG(VALUE)` with the appropriate flag value.
+/// 2) Invoke [transformMixins.transformClass] which clones mixin methods that
+/// use `super`.
+/// 3) Evaluate unevaluated constants using the program environment and replace
+/// some simple constant-like expressions with the equivalent constant.
+class _GlobalTransformer extends ConstantTransformer {
   final JsGetFlagLowering _jsGetFlagLowering;
 
-  _GlobalTransformer(CoreTypes coreTypes, CompilerOptions options)
+  _GlobalTransformer(
+      super.constantEvaluator, CoreTypes coreTypes, CompilerOptions options)
       : _jsGetFlagLowering = JsGetFlagLowering(coreTypes, options);
 
   @override
@@ -30,7 +46,8 @@
 
   @override
   TreeNode visitStaticInvocation(StaticInvocation node) {
-    node.transformChildren(this);
-    return _jsGetFlagLowering.transformStaticInvocation(node);
+    final newNode = super.visitStaticInvocation(node);
+    if (newNode is! StaticInvocation) return node;
+    return _jsGetFlagLowering.transformStaticInvocation(newNode);
   }
 }
diff --git a/pkg/compiler/lib/src/phase/load_kernel.dart b/pkg/compiler/lib/src/phase/load_kernel.dart
index d678507..135fb6c 100644
--- a/pkg/compiler/lib/src/phase/load_kernel.dart
+++ b/pkg/compiler/lib/src/phase/load_kernel.dart
@@ -4,25 +4,28 @@
 
 import 'dart:async';
 
+import 'package:_js_interop_checks/src/transformations/static_interop_class_eraser.dart';
 import 'package:collection/collection.dart';
+import 'package:front_end/src/api_unstable/dart2js.dart' as fe;
 import 'package:front_end/src/fasta/kernel/utils.dart';
 import 'package:kernel/ast.dart' as ir;
-import 'package:kernel/core_types.dart' as ir;
 import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
-
-import 'package:front_end/src/api_unstable/dart2js.dart' as fe;
+import 'package:kernel/class_hierarchy.dart' as ir;
+import 'package:kernel/core_types.dart' as ir;
 import 'package:kernel/kernel.dart' hide LibraryDependency, Combinator;
 import 'package:kernel/target/targets.dart' hide DiagnosticReporter;
-
-import 'package:_js_interop_checks/src/transformations/static_interop_class_eraser.dart';
+import 'package:kernel/type_environment.dart' as ir;
 import 'package:kernel/verifier.dart';
 
 import '../../compiler_api.dart' as api;
 import '../commandline_options.dart';
 import '../common.dart';
-import '../kernel/front_end_adapter.dart';
+import '../diagnostics/diagnostic_listener.dart';
+import '../environment.dart';
+import '../ir/constants.dart';
 import '../kernel/dart2js_target.dart'
     show Dart2jsTarget, implicitlyUsedLibraries;
+import '../kernel/front_end_adapter.dart';
 import '../kernel/transformations/global/transform.dart' as globalTransforms;
 import '../options.dart';
 
@@ -112,11 +115,24 @@
 
 // Perform any backend-specific transforms here that can be done on both
 // serialized components and components from source.
-void _doTransformsOnKernelLoad(Component component, CompilerOptions options) {
+void _doTransformsOnKernelLoad(
+    Component component, CompilerOptions options, DiagnosticReporter reporter) {
   if (options.stage.shouldRunGlobalTransforms) {
     ir.CoreTypes coreTypes = ir.CoreTypes(component);
-    globalTransforms.transformLibraries(
-        component.libraries, coreTypes, options);
+    // Ignore ambiguous supertypes.
+    final classHierarchy = ir.ClassHierarchy(component, coreTypes,
+        onAmbiguousSupertypes: (_, __, ___) {});
+    ir.TypeEnvironment typeEnvironment =
+        ir.TypeEnvironment(coreTypes, classHierarchy);
+    final constantsEvaluator = Dart2jsConstantEvaluator(
+        component,
+        typeEnvironment,
+        (fe.LocatedMessage message, List<fe.LocatedMessage>? context) =>
+            reportLocatedMessage(reporter, message, context),
+        environment: Environment(options.environment),
+        evaluationMode: options.useLegacySubtyping
+            ? fe.EvaluationMode.weak
+            : fe.EvaluationMode.strong);
     // referenceFromIndex is only necessary in the case where a module
     // containing a stub definition is invalidated, and then reloaded, because
     // we need to keep existing references to that stub valid. Here, we have the
@@ -124,11 +140,16 @@
     StaticInteropClassEraser(coreTypes, null,
             additionalCoreLibraries: {'_js_types', 'js_interop'})
         .visitComponent(component);
+    globalTransforms.transformLibraries(
+        component.libraries, constantsEvaluator, coreTypes, options);
   }
 }
 
-Future<_LoadFromKernelResult> _loadFromKernel(CompilerOptions options,
-    api.CompilerInput compilerInput, String targetName) async {
+Future<_LoadFromKernelResult> _loadFromKernel(
+    CompilerOptions options,
+    api.CompilerInput compilerInput,
+    String targetName,
+    DiagnosticReporter reporter) async {
   Library? entryLibrary;
   var resolvedUri = options.compilationTarget;
   ir.Component component = ir.Component();
@@ -174,7 +195,7 @@
     component.setMainMethodAndMode(mainMethod, true, component.mode);
   }
 
-  _doTransformsOnKernelLoad(component, options);
+  _doTransformsOnKernelLoad(component, options, reporter);
   registerSources(component, compilerInput);
   return _LoadFromKernelResult(component, entryLibrary);
 }
@@ -273,7 +294,7 @@
       return true;
     }());
 
-    _doTransformsOnKernelLoad(component, options);
+    _doTransformsOnKernelLoad(component, options, reporter);
 
     registerSources(component, compilerInput);
   }
@@ -353,7 +374,7 @@
       input.initializedCompilerState;
   if (options.stage.shouldLoadFromDill) {
     _LoadFromKernelResult result =
-        await _loadFromKernel(options, compilerInput, targetName);
+        await _loadFromKernel(options, compilerInput, targetName, reporter);
     component = result.component;
     entryLibrary = result.entryLibrary;
   } else {
diff --git a/pkg/compiler/lib/src/serialization/strategies.dart b/pkg/compiler/lib/src/serialization/strategies.dart
index 167c626..b7017e1 100644
--- a/pkg/compiler/lib/src/serialization/strategies.dart
+++ b/pkg/compiler/lib/src/serialization/strategies.dart
@@ -60,7 +60,6 @@
   JClosedWorld deserializeClosedWorld(
       CompilerOptions options,
       DiagnosticReporter reporter,
-      Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
       List<T> data,
@@ -124,7 +123,6 @@
   JClosedWorld deserializeClosedWorld(
       CompilerOptions options,
       DiagnosticReporter reporter,
-      Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
       List<int> data,
@@ -132,8 +130,8 @@
     DataSourceReader source = DataSourceReader(
         BinaryDataSource(data), options, indices,
         useDataKinds: useDataKinds);
-    var closedWorld = deserializeClosedWorldFromSource(options, reporter,
-        environment, abstractValueStrategy, component, source);
+    var closedWorld = deserializeClosedWorldFromSource(
+        options, reporter, abstractValueStrategy, component, source);
     return closedWorld;
   }
 }
@@ -197,7 +195,6 @@
   JClosedWorld deserializeClosedWorld(
       CompilerOptions options,
       DiagnosticReporter reporter,
-      Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
       List<int> data,
@@ -205,8 +202,8 @@
     DataSourceReader source = DataSourceReader(
         BinaryDataSource(data), options, indices,
         useDataKinds: useDataKinds);
-    var closedWorld = deserializeClosedWorldFromSource(options, reporter,
-        environment, abstractValueStrategy, component, source);
+    var closedWorld = deserializeClosedWorldFromSource(
+        options, reporter, abstractValueStrategy, component, source);
     return closedWorld;
   }
 }
@@ -266,7 +263,6 @@
   JClosedWorld deserializeClosedWorld(
       CompilerOptions options,
       DiagnosticReporter reporter,
-      Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
       List<Object> data,
@@ -274,8 +270,8 @@
     DataSourceReader source = DataSourceReader(
         ObjectDataSource(data), options, indices,
         useDataKinds: useDataKinds);
-    var closedWorld = deserializeClosedWorldFromSource(options, reporter,
-        environment, abstractValueStrategy, component, source);
+    var closedWorld = deserializeClosedWorldFromSource(
+        options, reporter, abstractValueStrategy, component, source);
     return closedWorld;
   }
 }
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index e6ba818..a5de3dd 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -122,7 +122,6 @@
   }
 
   Future<JClosedWorld> deserializeClosedWorld(
-      Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
       bool useDeferredSourceReads,
@@ -138,8 +137,8 @@
           indices,
           interner: _valueInterner,
           useDeferredStrategy: useDeferredSourceReads);
-      var closedWorld = deserializeClosedWorldFromSource(_options, _reporter,
-          environment, abstractValueStrategy, component, source);
+      var closedWorld = deserializeClosedWorldFromSource(
+          _options, _reporter, abstractValueStrategy, component, source);
       return closedWorld;
     });
   }
@@ -346,10 +345,9 @@
 JClosedWorld deserializeClosedWorldFromSource(
     CompilerOptions options,
     DiagnosticReporter reporter,
-    Environment environment,
     AbstractValueStrategy abstractValueStrategy,
     ir.Component component,
     DataSourceReader source) {
   return JClosedWorld.readFromDataSource(
-      options, reporter, environment, abstractValueStrategy, component, source);
+      options, reporter, abstractValueStrategy, component, source);
 }
diff --git a/pkg/compiler/lib/src/util/diagnostic_helper.dart b/pkg/compiler/lib/src/util/diagnostic_helper.dart
index 4b02796..022314b 100644
--- a/pkg/compiler/lib/src/util/diagnostic_helper.dart
+++ b/pkg/compiler/lib/src/util/diagnostic_helper.dart
@@ -63,6 +63,10 @@
     return filterMessagesByKinds([api.Diagnostic.CRASH]);
   }
 
+  Iterable<CollectedMessage> get contexts {
+    return filterMessagesByKinds([api.Diagnostic.CONTEXT]);
+  }
+
   Iterable<CollectedMessage> get verboseInfos {
     return filterMessagesByKinds([api.Diagnostic.VERBOSE_INFO]);
   }
diff --git a/pkg/compiler/lib/src/util/memory_compiler.dart b/pkg/compiler/lib/src/util/memory_compiler.dart
index d34de0a..cadba4b 100644
--- a/pkg/compiler/lib/src/util/memory_compiler.dart
+++ b/pkg/compiler/lib/src/util/memory_compiler.dart
@@ -91,6 +91,7 @@
     api.CompilerDiagnostics? diagnosticHandler,
     api.CompilerOutput? outputProvider,
     List<String> options = const <String>[],
+    Map<String, String>? environment,
     bool showDiagnostics = true,
     Uri? librariesSpecificationUri,
     Uri? packageConfig,
@@ -105,6 +106,7 @@
       diagnosticHandler: diagnosticHandler,
       outputProvider: outputProvider,
       options: options,
+      environment: environment,
       showDiagnostics: showDiagnostics,
       librariesSpecificationUri: librariesSpecificationUri,
       packageConfig: packageConfig,
@@ -125,6 +127,7 @@
     api.CompilerDiagnostics? diagnosticHandler,
     api.CompilerOutput? outputProvider,
     List<String> options = const <String>[],
+    Map<String, String>? environment,
     bool showDiagnostics = true,
     Uri? librariesSpecificationUri,
     Uri? packageConfig,
@@ -199,7 +202,7 @@
       platformBinaries: options.contains(Flags.noSoundNullSafety)
           ? buildPlatformBinariesUri
           : null)
-    ..environment = {}
+    ..environment = environment ?? {}
     ..packageConfig = packageConfig;
 
   compilerOptions.setDefaultOutputUriForTesting();
diff --git a/pkg/compiler/test/analyses/analysis_helper.dart b/pkg/compiler/test/analyses/analysis_helper.dart
index 7b169dc..19b1fe0 100644
--- a/pkg/compiler/test/analyses/analysis_helper.dart
+++ b/pkg/compiler/test/analyses/analysis_helper.dart
@@ -10,7 +10,6 @@
 import 'package:async_helper/async_helper.dart';
 import 'package:compiler/src/common.dart';
 import 'package:compiler/src/compiler.dart';
-import 'package:compiler/src/ir/constants.dart';
 import 'package:compiler/src/ir/scope.dart';
 import 'package:compiler/src/ir/static_type.dart';
 import 'package:compiler/src/ir/util.dart';
@@ -78,8 +77,6 @@
   VariableScopeModel get variableScopeModel => _variableScopeModel!;
   VariableScopeModel? _variableScopeModel;
 
-  late final Dart2jsConstantEvaluator _constantEvaluator;
-
   @override
   ir.StaticTypeContext get staticTypeContext => _staticTypeContext!;
   set staticTypeContext(ir.StaticTypeContext context) {
@@ -92,11 +89,7 @@
       ir.Component component, ir.ClassHierarchy classHierarchy,
       {required ir.EvaluationMode evaluationMode})
       : super(ir.TypeEnvironment(new ir.CoreTypes(component), classHierarchy),
-            classHierarchy, StaticTypeCacheImpl()) {
-    _constantEvaluator = Dart2jsConstantEvaluator(
-        component, typeEnvironment, const ir.SimpleErrorReporter().report,
-        evaluationMode: evaluationMode);
-  }
+            classHierarchy, StaticTypeCacheImpl());
 
   @override
   bool get useAsserts => false;
@@ -112,7 +105,7 @@
     }
     _staticTypeContext = ir.StaticTypeContext(node, typeEnvironment);
     _variableScopeModel =
-        ScopeModel.from(node, _constantEvaluator).variableScopeModel;
+        ScopeModel.from(node, typeEnvironment).variableScopeModel;
     final result = super.visitProcedure(node);
     _variableScopeModel = null;
     _staticTypeContext = null;
@@ -123,7 +116,7 @@
   ir.DartType visitField(ir.Field node) {
     _staticTypeContext = ir.StaticTypeContext(node, typeEnvironment);
     _variableScopeModel =
-        ScopeModel.from(node, _constantEvaluator).variableScopeModel;
+        ScopeModel.from(node, typeEnvironment).variableScopeModel;
     final result = super.visitField(node);
     _variableScopeModel = null;
     _staticTypeContext = null;
@@ -134,7 +127,7 @@
   ir.DartType visitConstructor(ir.Constructor node) {
     _staticTypeContext = ir.StaticTypeContext(node, typeEnvironment);
     _variableScopeModel =
-        ScopeModel.from(node, _constantEvaluator).variableScopeModel;
+        ScopeModel.from(node, typeEnvironment).variableScopeModel;
     final result = super.visitConstructor(node);
     _variableScopeModel = null;
     _staticTypeContext = null;
diff --git a/pkg/compiler/test/model/cfe_constant_evaluation_test.dart b/pkg/compiler/test/model/cfe_constant_evaluation_test.dart
index 98cdc3a..3dccc8d 100644
--- a/pkg/compiler/test/model/cfe_constant_evaluation_test.dart
+++ b/pkg/compiler/test/model/cfe_constant_evaluation_test.dart
@@ -23,6 +23,8 @@
 import 'package:kernel/type_environment.dart' as ir;
 import 'package:compiler/src/util/memory_compiler.dart';
 
+const emptyEnv = <String, String>{};
+
 class TestData {
   final String name;
 
@@ -44,12 +46,11 @@
   /// a [ConstantResult].
   final expectedResults;
 
-  /// A [String] or a list of [String]s containing the code names for the error
-  /// messages expected as the result of evaluating the constant under the empty
-  /// environment.
-  final expectedErrors;
+  /// A [String] containing the code name for the error message expected as the
+  /// result of evaluating the constant under the empty environment.
+  final String? expectedError;
 
-  const ConstantData(this.code, this.expectedResults, {this.expectedErrors});
+  const ConstantData(this.code, this.expectedResults, {this.expectedError});
 }
 
 const List<TestData> DATA = [
@@ -92,24 +93,24 @@
     ConstantData('Object', 'TypeConstant(Object)'),
     ConstantData('null ?? 0', 'IntConstant(0)'),
     ConstantData('const <int, int>{0: 1, 0: 2}', 'NonConstant',
-        expectedErrors: 'ConstEvalDuplicateKey'),
+        expectedError: 'ConstEvalDuplicateKey'),
     ConstantData(
         'const bool.fromEnvironment("foo", defaultValue: false)',
         <Map<String, String>, String>{
-          {}: 'BoolConstant(false)',
-          {'foo': 'true'}: 'BoolConstant(true)'
+          emptyEnv: 'BoolConstant(false)',
+          const {'foo': 'true'}: 'BoolConstant(true)'
         }),
     ConstantData(
         'const int.fromEnvironment("foo", defaultValue: 42)',
         <Map<String, String>, String>{
-          {}: 'IntConstant(42)',
-          {'foo': '87'}: 'IntConstant(87)'
+          emptyEnv: 'IntConstant(42)',
+          const {'foo': '87'}: 'IntConstant(87)'
         }),
     ConstantData(
         'const String.fromEnvironment("foo", defaultValue: "bar")',
         <Map<String, String>, String>{
-          {}: 'StringConstant("bar")',
-          {'foo': 'foo'}: 'StringConstant("foo")'
+          emptyEnv: 'StringConstant("bar")',
+          const {'foo': 'foo'}: 'StringConstant("foo")'
         }),
     ConstantData(
         'const [0, 1]', 'ListConstant(<int*>[IntConstant(0), IntConstant(1)])'),
@@ -175,9 +176,9 @@
         'ConstructedConstant(C(field1=IntConstant(87),'
             'field2=IntConstant(87)))'),
     ConstantData('const C(field1: a, field2: b)', <Map<String, String>, String>{
-      {}: 'ConstructedConstant(C(field1=BoolConstant(true),'
+      emptyEnv: 'ConstructedConstant(C(field1=BoolConstant(true),'
           'field2=IntConstant(42)))',
-      {'foo': 'false', 'bar': '87'}:
+      const {'foo': 'false', 'bar': '87'}:
           'ConstructedConstant(C(field1=BoolConstant(false),'
               'field2=IntConstant(87)))',
     }),
@@ -229,13 +230,13 @@
 }
 ''', [
     ConstantData('const A(c, d)', <Map<String, String>, String>{
-      {}: 'ConstructedConstant(A(field=IntConstant(15)))',
-      {'foo': '7', 'bar': '11'}:
+      emptyEnv: 'ConstructedConstant(A(field=IntConstant(15)))',
+      const {'foo': '7', 'bar': '11'}:
           'ConstructedConstant(A(field=IntConstant(18)))',
     }),
     ConstantData('const B(d)', <Map<String, String>, String>{
-      {}: 'ConstructedConstant(B(field=IntConstant(30)))',
-      {'bar': '42'}: 'ConstructedConstant(B(field=IntConstant(126)))',
+      emptyEnv: 'ConstructedConstant(B(field=IntConstant(30)))',
+      const {'bar': '42'}: 'ConstructedConstant(B(field=IntConstant(126)))',
     }),
   ]),
   TestData('construct', '''
@@ -317,89 +318,89 @@
     ConstantData(
         r'"$integer $string $boolean"', 'StringConstant("5 baz false")'),
     ConstantData('integer ? true : false', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData(r'"${deprecated}"', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidStringInterpolationOperand'),
+        expectedError: 'ConstEvalInvalidStringInterpolationOperand'),
     ConstantData('0 + string', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('string + 0', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidBinaryOperandType'),
+        expectedError: 'ConstEvalInvalidBinaryOperandType'),
     ConstantData('boolean + string', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidMethodInvocation'),
+        expectedError: 'ConstEvalInvalidMethodInvocation'),
     ConstantData('boolean + false', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidMethodInvocation'),
+        expectedError: 'ConstEvalInvalidMethodInvocation'),
     ConstantData('0 * string', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('0 % string', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('0 << string', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('1 ~/ zero', 'NonConstant',
-        expectedErrors: 'ConstEvalZeroDivisor'),
+        expectedError: 'ConstEvalZeroDivisor'),
     ConstantData('1 % zero', 'NonConstant',
-        expectedErrors: 'ConstEvalZeroDivisor'),
+        expectedError: 'ConstEvalZeroDivisor'),
     ConstantData('1 << minus_one', 'NonConstant',
-        expectedErrors: 'ConstEvalNegativeShift'),
+        expectedError: 'ConstEvalNegativeShift'),
     ConstantData('1 >> minus_one', 'NonConstant',
-        expectedErrors: 'ConstEvalNegativeShift'),
+        expectedError: 'ConstEvalNegativeShift'),
     ConstantData('const bool.fromEnvironment(integer)', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('const bool.fromEnvironment("baz", defaultValue: integer)',
         'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('const int.fromEnvironment(integer)', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData(
         'const int.fromEnvironment("baz", defaultValue: string)', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('const String.fromEnvironment(integer)', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('const String.fromEnvironment("baz", defaultValue: integer)',
         'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('false || integer', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('integer || true', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('integer && true', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('!integer', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('!string', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('-(string)', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidMethodInvocation'),
+        expectedError: 'ConstEvalInvalidMethodInvocation'),
     ConstantData('not_string.length', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidPropertyGet'),
+        expectedError: 'ConstEvalInvalidPropertyGet'),
     ConstantData('const Class1()', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidPropertyGet'),
+        expectedError: 'ConstEvalInvalidPropertyGet'),
     ConstantData('const Class2()', 'NonConstant',
-        expectedErrors: 'ConstEvalFailedAssertion'),
+        expectedError: 'ConstEvalFailedAssertion'),
     ConstantData('const Class2.redirect()', 'NonConstant',
-        expectedErrors: 'ConstEvalFailedAssertion'),
+        expectedError: 'ConstEvalFailedAssertion'),
     ConstantData('const Class3()', 'NonConstant',
-        expectedErrors: 'ConstEvalFailedAssertionWithMessage'),
+        expectedError: 'ConstEvalFailedAssertionWithMessage'),
     ConstantData('const Class3.fact()', 'NonConstant',
-        expectedErrors: 'ConstEvalFailedAssertion'),
+        expectedError: 'ConstEvalFailedAssertion'),
     ConstantData('const Class4()', 'NonConstant',
-        expectedErrors: 'ConstEvalFailedAssertion'),
+        expectedError: 'ConstEvalFailedAssertion'),
     ConstantData('const Class5(0)', 'NonConstant',
-        expectedErrors: 'ConstEvalFailedAssertionWithMessage'),
+        expectedError: 'ConstEvalFailedAssertionWithMessage'),
     ConstantData('const Class5(1)', 'ConstructedConstant(Class5())'),
     ConstantData('const Class6(1)', 'NonConstant',
-        expectedErrors: 'ConstEvalFailedAssertionWithMessage'),
+        expectedError: 'ConstEvalFailedAssertionWithMessage'),
     ConstantData('const Class6(2)', 'ConstructedConstant(Class6())'),
     ConstantData('const Class7()', 'ConstructedConstant(Class7())'),
     ConstantData('const Class7() == const Class7()', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidEqualsOperandType'),
+        expectedError: 'ConstEvalInvalidEqualsOperandType'),
     ConstantData('const Class7() != const Class7()', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidEqualsOperandType'),
+        expectedError: 'ConstEvalInvalidEqualsOperandType'),
     ConstantData('const Class8(not_string.length)', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidPropertyGet'),
+        expectedError: 'ConstEvalInvalidPropertyGet'),
     ConstantData(
         'const Class9()', 'ConstructedConstant(Class9(field=NullConstant))'),
     ConstantData('const Class10()', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
   ]),
   TestData('assert', '''
     const true_ = bool.fromEnvironment('x') ? null : true;
@@ -517,11 +518,11 @@
 }
 ''', [
     ConstantData('const Class<B>.redirect(C())', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('const Class<A>.method(A())', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidMethodInvocation'),
+        expectedError: 'ConstEvalInvalidMethodInvocation'),
     ConstantData('const Subclass<B>(C())', 'NonConstant',
-        expectedErrors: 'ConstEvalInvalidType'),
+        expectedError: 'ConstEvalInvalidType'),
     ConstantData('const Class<A>(A())', 'ConstructedConstant(Class<A*>())'),
     ConstantData(
         'const Class<B>.redirect(B())', 'ConstructedConstant(Class<B*>())'),
@@ -556,18 +557,20 @@
     bool.fromEnvironment("baz") ? int.parse : null,
   )''',
       <Map<String, String>, String>{
-        {}: 'ConstructedConstant(Foo(_foo=FunctionConstant(bar)))',
-        {'baz': 'true'}:
+        emptyEnv: 'ConstructedConstant(Foo(_foo=FunctionConstant(bar)))',
+        const {'baz': 'true'}:
             'ConstructedConstant(Foo(_foo=FunctionConstant(int.parse)))'
       },
     ),
     ConstantData(
       '''String.fromEnvironment(String.fromEnvironment(String.fromEnvironment("foo")))''',
       <Map<String, String>, String>{
-        {}: 'StringConstant("")',
-        {'foo': 'bar', 'bar': 'baz'}: 'StringConstant("")',
-        {'foo': 'bar', 'bar': 'baz', 'baz': 'hello'}: 'StringConstant("hello")',
-        {'foo': 'bar', 'bar': 'baz', 'baz': 'world'}: 'StringConstant("world")',
+        emptyEnv: 'StringConstant("")',
+        const {'foo': 'bar', 'bar': 'baz'}: 'StringConstant("")',
+        const {'foo': 'bar', 'bar': 'baz', 'baz': 'hello'}:
+            'StringConstant("hello")',
+        const {'foo': 'bar', 'bar': 'baz', 'baz': 'world'}:
+            'StringConstant("world")',
       },
     ),
   ]),
@@ -583,108 +586,125 @@
 }
 
 Future testData(TestData data) async {
-  StringBuffer sb = StringBuffer();
-  sb.writeln('${data.declarations}');
-  Map<String, ConstantData> constants = {};
-  List<String> names = <String>[];
+  // Group tests by environment and then by expected error so that we can
+  // distinguish which constant triggered which errors in the CFE. There
+  // are too many constants to compile each individually, this test would
+  // timeout.
+  Map<Map<String, String>, Map<String?, List<(ConstantData, String)>>>
+      constants = {};
   data.constants.forEach((ConstantData constantData) {
-    String name = 'c${constants.length}';
-    names.add(name);
-    // Encode the constants as part of a from-environment conditional to force
-    // CFE to create unevaluated constants.
-    sb.writeln('const $name = bool.fromEnvironment("x") ? '
-        'null : ${constantData.code};');
-    constants[name] = constantData;
-  });
-  sb.writeln('main() {');
-  for (String name in names) {
-    sb.writeln('  print($name);');
-  }
-  sb.writeln('}');
-  String source = sb.toString();
-  print("--source '${data.name}'---------------------------------------------");
-  print(source);
-
-  Future runTest() async {
-    CompilationResult result = await runCompiler(
-        memorySourceFiles: {'main.dart': source},
-        options: [Flags.enableAsserts]);
-    Compiler compiler = result.compiler;
-    KernelFrontendStrategy frontEndStrategy = compiler.frontendStrategy;
-    KernelToElementMap elementMap = frontEndStrategy.elementMap;
-    DartTypes dartTypes = elementMap.types;
-    ir.TypeEnvironment typeEnvironment = elementMap.typeEnvironment;
-    KElementEnvironment elementEnvironment =
-        compiler.frontendStrategy.elementEnvironment;
-    ConstantValuefier constantValuefier = ConstantValuefier(elementMap);
-    LibraryEntity library = elementEnvironment.mainLibrary!;
-    constants.forEach((String name, ConstantData data) {
-      final field = elementEnvironment.lookupLibraryMember(library, name)!;
-      compiler.reporter.withCurrentElement(field, () {
-        var expectedResults = data.expectedResults;
-        if (expectedResults is String) {
-          expectedResults = <Map<String, String>, String>{
-            const <String, String>{}: expectedResults
-          };
-        }
-        final node = elementMap.getMemberNode(field) as ir.Field;
-        final initializer = node.initializer as ir.ConstantExpression;
-        print('-- testing $field = ${data.code} --');
-        expectedResults
-            .forEach((Map<String, String> environment, String expectedText) {
-          List<String> errors = [];
-          Dart2jsConstantEvaluator evaluator = Dart2jsConstantEvaluator(
-              elementMap.env.mainComponent, elementMap.typeEnvironment,
-              (ir.LocatedMessage message, List<ir.LocatedMessage>? context) {
-            // TODO(johnniwinther): Assert that `message.uri != null`. Currently
-            // all unevaluated constants have no uri.
-            // The actual message is a "constant errors starts here" message,
-            // the "real error message" is the first in the context.
-            errors.add(context!.first.code.name);
-            reportLocatedMessage(elementMap.reporter, message, context);
-          },
-              environment: Environment(environment),
-              supportReevaluationForTesting: true,
-              evaluationMode: compiler.options.useLegacySubtyping
-                  ? ir.EvaluationMode.weak
-                  : ir.EvaluationMode.strong);
-          ir.Constant evaluatedConstant = evaluator.evaluate(
-              ir.StaticTypeContext(node, typeEnvironment), initializer);
-
-          ConstantValue? value = evaluatedConstant is! ir.UnevaluatedConstant
-              ? constantValuefier.visitConstant(evaluatedConstant)
-              : null;
-
-          String valueText =
-              value?.toStructuredText(dartTypes) ?? 'NonConstant';
-          Expect.equals(
-              expectedText,
-              valueText,
-              "Unexpected value '${valueText}' for field $field = "
-              "`${data.code}` in env $environment, "
-              "expected '${expectedText}'.");
-
-          var expectedErrors = data.expectedErrors;
-          if (expectedErrors != null) {
-            if (expectedErrors is! List) {
-              expectedErrors = [expectedErrors];
-            }
-            Expect.listEquals(
-                expectedErrors,
-                errors,
-                "Error mismatch for `$field = ${data.code}`:\n"
-                "Expected: ${data.expectedErrors},\n"
-                "Found: ${errors}.");
-          } else {
-            Expect.isTrue(
-                errors.isEmpty,
-                "Unexpected errors for `$field = ${data.code}`:\n"
-                "Found: ${errors}.");
-          }
-        });
+    final expectedResult = constantData.expectedResults;
+    if (expectedResult is String) {
+      ((constants[emptyEnv] ??= {})[constantData.expectedError] ??= [])
+          .add((constantData, expectedResult));
+    } else if (expectedResult is Map<Map<String, String>, String>) {
+      expectedResult.forEach((env, expectedString) {
+        ((constants[env] ??= {})[constantData.expectedError] ??= [])
+            .add((constantData, expectedString));
       });
+    }
+  });
+
+  for (final env in constants.keys) {
+    final expectations = constants[env]!;
+    for (final errorString in expectations.keys) {
+      StringBuffer sb = StringBuffer();
+      sb.writeln('${data.declarations}');
+      final constantEntries = expectations[errorString]!;
+      final envData = <(String, (ConstantData, String))>[];
+      for (final constantEntry in constantEntries) {
+        final name = 'c${envData.length}';
+        final code = constantEntry.$1.code;
+        envData.add((name, constantEntry));
+        // Encode the constants as part of a from-environment conditional to
+        // force CFE to create unevaluated constants.
+        sb.writeln('const $name = bool.fromEnvironment("x") ? null : $code;');
+      }
+      sb.writeln('main() {');
+      for (final (name, _) in envData) {
+        sb.writeln('  print($name);');
+      }
+      sb.writeln('}');
+      String source = sb.toString();
+      print(
+          "--source '${data.name}'-------------------------------------------");
+      print("Compiling with env: $env");
+      print(source);
+      await runEnvTest(env, source, envData, errorString);
+    }
+  }
+}
+
+Future<void> runEnvTest(
+    Map<String, String> env,
+    String source,
+    List<(String, (ConstantData, String))> envData,
+    String? expectedError) async {
+  final diagnosticCollector = DiagnosticCollector();
+  CompilationResult result = await runCompiler(
+      memorySourceFiles: {'main.dart': source},
+      options: [Flags.enableAsserts, Flags.testMode],
+      environment: {...env},
+      diagnosticHandler: diagnosticCollector);
+  Compiler compiler = result.compiler;
+  KernelFrontendStrategy frontEndStrategy = compiler.frontendStrategy;
+  KernelToElementMap elementMap = frontEndStrategy.elementMap;
+  DartTypes dartTypes = elementMap.types;
+  ir.TypeEnvironment typeEnvironment = elementMap.typeEnvironment;
+  KElementEnvironment elementEnvironment =
+      compiler.frontendStrategy.elementEnvironment;
+  ConstantValuefier constantValuefier = ConstantValuefier(elementMap);
+  LibraryEntity library = elementEnvironment.mainLibrary!;
+  for (final (name, (data, expectedText)) in envData) {
+    final field = elementEnvironment.lookupLibraryMember(library, name)!;
+    compiler.reporter.withCurrentElement(field, () {
+      final node = elementMap.getMemberNode(field) as ir.Field;
+      final initializer = node.initializer as ir.ConstantExpression;
+      print('-- testing $field = ${data.code} --');
+      Dart2jsConstantEvaluator evaluator = Dart2jsConstantEvaluator(
+          elementMap.env.mainComponent, elementMap.typeEnvironment,
+          (ir.LocatedMessage message, List<ir.LocatedMessage>? context) {
+        // Constants should be fully evaluated by this point so there should be
+        // no new messages.
+        throw StateError('There should be no unevaluated errors in the AST.');
+      },
+          environment: Environment(env),
+          supportReevaluationForTesting: true,
+          evaluationMode: compiler.options.useLegacySubtyping
+              ? ir.EvaluationMode.weak
+              : ir.EvaluationMode.strong);
+      ir.Constant evaluatedConstant = evaluator.evaluate(
+          ir.StaticTypeContext(node, typeEnvironment), initializer);
+
+      ConstantValue? value = evaluatedConstant is! ir.UnevaluatedConstant
+          ? constantValuefier.visitConstant(evaluatedConstant)
+          : null;
+
+      String valueText = value?.toStructuredText(dartTypes) ?? 'NonConstant';
+      Expect.equals(
+          expectedText,
+          valueText,
+          "Unexpected value '${valueText}' for field $field = "
+          "`${data.code}` in env $env, "
+          "expected '${expectedText}'.");
+
+      var expectedError = data.expectedError;
+      final errors = diagnosticCollector.contexts.map((e) => e.text).toList();
+      if (expectedError != null) {
+        // There should be 2 errors per constant in this test group, 1 for the
+        // declaration and another for the print.
+        Expect.equals(envData.length * 2, errors.length);
+        Expect.isTrue(
+            errors.every((e) => e == expectedError),
+            "Error mismatch for `$field = ${data.code}`:\n"
+            "Expected: ${expectedError},\n"
+            "Found: ${errors}.");
+      } else {
+        Expect.isTrue(
+            diagnosticCollector.contexts.isEmpty,
+            "Unexpected errors for `$field = ${data.code}`:\n"
+            "Found: ${errors}.");
+      }
     });
   }
-
-  await runTest();
 }
diff --git a/pkg/compiler/test/serialization/serialization_test_helper.dart b/pkg/compiler/test/serialization/serialization_test_helper.dart
index 062a6fa..ddb3513 100644
--- a/pkg/compiler/test/serialization/serialization_test_helper.dart
+++ b/pkg/compiler/test/serialization/serialization_test_helper.dart
@@ -282,7 +282,6 @@
   var newClosedWorld = strategy.deserializeClosedWorld(
       compiler.options,
       compiler.reporter,
-      compiler.environment,
       compiler.abstractValueStrategy,
       newComponent,
       closedWorldData,
@@ -310,7 +309,6 @@
   var newClosedWorld = strategy.deserializeClosedWorld(
       compiler.options,
       compiler.reporter,
-      compiler.environment,
       compiler.abstractValueStrategy,
       newComponent,
       closedWorldData,