[vm/kernel/bytecode] Report compile-time errors from constant evaluation while generating bytecode

Change-Id: I931622f5e442a439f5bfa241aade686b1bc91afd
Reviewed-on: https://dart-review.googlesource.com/68222
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/kernel/lib/transformations/constants.dart b/pkg/kernel/lib/transformations/constants.dart
index 54d62ed..d13f8ca 100644
--- a/pkg/kernel/lib/transformations/constants.dart
+++ b/pkg/kernel/lib/transformations/constants.dart
@@ -343,19 +343,11 @@
 
   tryEvaluateWithContext(TreeNode treeContext, Expression node) {
     if (treeContext == node) {
-      try {
-        return constantEvaluator.evaluate(node);
-      } on _AbortCurrentEvaluation catch (_) {
-        return null;
-      }
+      return constantEvaluator.evaluate(node);
     }
 
     return constantEvaluator.runInsideContext(treeContext, () {
-      try {
-        return constantEvaluator.evaluate(node);
-      } on _AbortCurrentEvaluation catch (_) {
-        return null;
-      }
+      return constantEvaluator.evaluate(node);
     });
   }
 }
@@ -387,7 +379,18 @@
         nodeCache = <Node, Constant>{};
 
   /// Evaluates [node] and possibly cache the evaluation result.
+  /// Returns `null` if expression can't be evaluated.
   Constant evaluate(Expression node) {
+    try {
+      return _evaluateSubexpression(node);
+    } on _AbortCurrentEvaluation catch (_) {
+      return null;
+    }
+  }
+
+  /// Evaluates [node] and possibly cache the evaluation result.
+  /// @throws _AbortCurrentEvaluation if expression can't be evaluated.
+  Constant _evaluateSubexpression(Expression node) {
     if (node == null) return nullConstant;
     if (env.isEmpty) {
       // We only try to evaluate the same [node] *once* within an empty
@@ -572,27 +575,30 @@
               function.positionalParameters[i];
           final Constant value = (i < positionalArguments.length)
               ? positionalArguments[i]
-              : evaluate(parameter.initializer);
+              : _evaluateSubexpression(parameter.initializer);
           env.addVariableValue(parameter, value);
         }
         for (final VariableDeclaration parameter in function.namedParameters) {
-          final Constant value =
-              namedArguments[parameter.name] ?? evaluate(parameter.initializer);
+          final Constant value = namedArguments[parameter.name] ??
+              _evaluateSubexpression(parameter.initializer);
           env.addVariableValue(parameter, value);
         }
 
         // Step 2) Run all initializers (including super calls) with environment setup.
         for (final Field field in klass.fields) {
           if (!field.isStatic) {
-            instanceBuilder.setFieldValue(field, evaluate(field.initializer));
+            instanceBuilder.setFieldValue(
+                field, _evaluateSubexpression(field.initializer));
           }
         }
         for (final Initializer init in constructor.initializers) {
           if (init is FieldInitializer) {
-            instanceBuilder.setFieldValue(init.field, evaluate(init.value));
+            instanceBuilder.setFieldValue(
+                init.field, _evaluateSubexpression(init.value));
           } else if (init is LocalInitializer) {
             final VariableDeclaration variable = init.variable;
-            env.addVariableValue(variable, evaluate(variable.initializer));
+            env.addVariableValue(
+                variable, _evaluateSubexpression(variable.initializer));
           } else if (init is SuperInitializer) {
             handleConstructorInvocation(
                 init.target,
@@ -653,7 +659,7 @@
     // We have no support for generic method invocation atm.
     assert(node.arguments.named.isEmpty);
 
-    final Constant receiver = evaluate(node.receiver);
+    final Constant receiver = _evaluateSubexpression(node.receiver);
     final List<Constant> arguments =
         evaluatePositionalArguments(node.arguments);
 
@@ -810,13 +816,13 @@
   }
 
   visitLogicalExpression(LogicalExpression node) {
-    final Constant left = evaluate(node.left);
+    final Constant left = _evaluateSubexpression(node.left);
     switch (node.operator) {
       case '||':
         if (left is BoolConstant) {
           if (left.value) return trueConstant;
 
-          final Constant right = evaluate(node.right);
+          final Constant right = _evaluateSubexpression(node.right);
           if (right is BoolConstant) {
             return right;
           }
@@ -836,7 +842,7 @@
         if (left is BoolConstant) {
           if (!left.value) return falseConstant;
 
-          final Constant right = evaluate(node.right);
+          final Constant right = _evaluateSubexpression(node.right);
           if (right is BoolConstant) {
             return right;
           }
@@ -853,7 +859,9 @@
             contextChain, node, left, '${node.operator}');
         throw const _AbortCurrentEvaluation();
       case '??':
-        return (left is! NullConstant) ? left : evaluate(node.right);
+        return (left is! NullConstant)
+            ? left
+            : _evaluateSubexpression(node.right);
       default:
         errorReporter.invalidMethodInvocation(
             contextChain, node, left, '${node.operator}');
@@ -862,11 +870,11 @@
   }
 
   visitConditionalExpression(ConditionalExpression node) {
-    final Constant constant = evaluate(node.condition);
+    final Constant constant = _evaluateSubexpression(node.condition);
     if (constant == trueConstant) {
-      return evaluate(node.then);
+      return _evaluateSubexpression(node.then);
     } else if (constant == falseConstant) {
-      return evaluate(node.otherwise);
+      return _evaluateSubexpression(node.otherwise);
     } else {
       errorReporter.invalidDartType(
           contextChain, node, constant, typeEnvironment.boolType);
@@ -885,7 +893,7 @@
       throw 'Could not evaluate field get ${node.name} on incomplete instance';
     }
 
-    final Constant receiver = evaluate(node.receiver);
+    final Constant receiver = _evaluateSubexpression(node.receiver);
     if (receiver is StringConstant && node.name.name == 'length') {
       return canonicalize(new IntConstant(receiver.value.length));
     } else if (receiver is InstanceConstant) {
@@ -899,7 +907,8 @@
   }
 
   visitLet(Let node) {
-    env.addVariableValue(node.variable, evaluate(node.variable.initializer));
+    env.addVariableValue(
+        node.variable, _evaluateSubexpression(node.variable.initializer));
     return node.body.accept(this);
   }
 
@@ -920,7 +929,7 @@
       return constant;
     }
     if (variable.isConst) {
-      return evaluate(variable.initializer);
+      return _evaluateSubexpression(variable.initializer);
     }
     throw new Exception('The front-end should ensure we do not encounter a '
         'variable get of a non-const variable.');
@@ -931,7 +940,7 @@
       final Member target = node.target;
       if (target is Field && target.isConst) {
         return runInsideContext(target, () {
-          return evaluate(target.initializer);
+          return _evaluateSubexpression(target.initializer);
         });
       } else if (target is Procedure) {
         if (target.kind == ProcedureKind.Method) {
@@ -1021,7 +1030,7 @@
   }
 
   visitInstantiation(Instantiation node) {
-    final Constant constant = evaluate(node.expression);
+    final Constant constant = _evaluateSubexpression(node.expression);
     if (constant is TearOffConstant) {
       if (node.typeArguments.length ==
           constant.procedure.function.typeParameters.length) {
diff --git a/pkg/kernel/lib/vm/constants_native_effects.dart b/pkg/kernel/lib/vm/constants_native_effects.dart
index 6566547..0de29be 100644
--- a/pkg/kernel/lib/vm/constants_native_effects.dart
+++ b/pkg/kernel/lib/vm/constants_native_effects.dart
@@ -59,7 +59,7 @@
         } else {
           value = new bool.fromEnvironment(name, defaultValue: defaultValue);
         }
-        return new BoolConstant(value);
+        return value != null ? new BoolConstant(value) : new NullConstant();
       case 'Integer_fromEnvironment':
         final String name = (positionalArguments[0] as StringConstant).value;
         final Constant constant = namedArguments['defaultValue'];
diff --git a/pkg/vm/lib/bytecode/assembler.dart b/pkg/vm/lib/bytecode/assembler.dart
index 3ccf658..dcbb60d 100644
--- a/pkg/vm/lib/bytecode/assembler.dart
+++ b/pkg/vm/lib/bytecode/assembler.dart
@@ -6,8 +6,8 @@
 
 import 'dart:typed_data';
 
-import 'package:vm/bytecode/dbc.dart';
-import 'package:vm/bytecode/exceptions.dart' show ExceptionsTable;
+import 'dbc.dart';
+import 'exceptions.dart' show ExceptionsTable;
 
 class Label {
   List<int> _jumps = <int>[];
diff --git a/pkg/vm/lib/bytecode/disassembler.dart b/pkg/vm/lib/bytecode/disassembler.dart
index be1e109..c9b6e46 100644
--- a/pkg/vm/lib/bytecode/disassembler.dart
+++ b/pkg/vm/lib/bytecode/disassembler.dart
@@ -6,8 +6,8 @@
 
 import 'dart:typed_data';
 
-import 'package:vm/bytecode/dbc.dart';
-import 'package:vm/bytecode/exceptions.dart';
+import 'dbc.dart';
+import 'exceptions.dart';
 
 class _Instruction {
   final Opcode opcode;
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 47fad1a..9f2bac4 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -10,18 +10,23 @@
 import 'package:kernel/external_name.dart' show getExternalName;
 import 'package:kernel/library_index.dart' show LibraryIndex;
 import 'package:kernel/transformations/constants.dart'
-    show ConstantEvaluator, ConstantsBackend, EvaluationEnvironment;
+    show
+        ConstantEvaluator,
+        ConstantsBackend,
+        EvaluationEnvironment,
+        ErrorReporter;
 import 'package:kernel/type_algebra.dart'
     show Substitution, containsTypeVariable;
 import 'package:kernel/type_environment.dart' show TypeEnvironment;
 import 'package:kernel/vm/constants_native_effects.dart'
     show VmConstantsBackend;
-import 'package:vm/bytecode/assembler.dart';
-import 'package:vm/bytecode/constant_pool.dart';
-import 'package:vm/bytecode/dbc.dart';
-import 'package:vm/bytecode/exceptions.dart';
-import 'package:vm/bytecode/local_vars.dart' show LocalVariables;
-import 'package:vm/metadata/bytecode.dart';
+import 'assembler.dart';
+import 'constant_pool.dart';
+import 'dbc.dart';
+import 'exceptions.dart';
+import 'local_vars.dart' show LocalVariables;
+import '../constants_error_reporter.dart' show ForwardConstantEvaluationErrors;
+import '../metadata/bytecode.dart';
 
 /// Flag to toggle generation of bytecode in kernel files.
 const bool isKernelBytecodeEnabled = false;
@@ -32,16 +37,20 @@
 void generateBytecode(Component component,
     {bool strongMode: true,
     bool dropAST: false,
-    bool omitSourcePositions: false}) {
+    bool omitSourcePositions: false,
+    Map<String, String> environmentDefines,
+    ErrorReporter errorReporter}) {
   final coreTypes = new CoreTypes(component);
   void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
   final hierarchy = new ClassHierarchy(component,
       onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
   final typeEnvironment =
       new TypeEnvironment(coreTypes, hierarchy, strongMode: strongMode);
-  final constantsBackend = new VmConstantsBackend(null, coreTypes);
+  final constantsBackend =
+      new VmConstantsBackend(environmentDefines, coreTypes);
+  final errorReporter = new ForwardConstantEvaluationErrors(typeEnvironment);
   new BytecodeGenerator(component, coreTypes, hierarchy, typeEnvironment,
-          constantsBackend, strongMode, omitSourcePositions)
+          constantsBackend, strongMode, omitSourcePositions, errorReporter)
       .visitComponent(component);
   if (dropAST) {
     new DropAST().visitComponent(component);
@@ -56,6 +65,7 @@
   final ConstantsBackend constantsBackend;
   final bool strongMode;
   final bool omitSourcePositions;
+  final ErrorReporter errorReporter;
   final BytecodeMetadataRepository metadata = new BytecodeMetadataRepository();
 
   Class enclosingClass;
@@ -78,6 +88,7 @@
   ConstantEmitter constantEmitter;
   BytecodeAssembler asm;
   List<BytecodeAssembler> savedAssemblers;
+  bool hasErrors;
 
   BytecodeGenerator(
       this.component,
@@ -86,7 +97,8 @@
       this.typeEnvironment,
       this.constantsBackend,
       this.strongMode,
-      this.omitSourcePositions) {
+      this.omitSourcePositions,
+      this.errorReporter) {
     component.addMetadataRepository(metadata);
   }
 
@@ -288,8 +300,19 @@
     asm.emitPushConstant(cpIndex);
   }
 
-  void _genPushConstExpr(Expression expr) {
+  Constant _evaluateConstantExpression(Expression expr) {
     final constant = constantEvaluator.evaluate(expr);
+    if (constant == null) {
+      // Compile-time error is already reported. Proceed with compilation
+      // in order to report as many errors as possible.
+      hasErrors = true;
+      return new NullConstant();
+    }
+    return constant;
+  }
+
+  void _genPushConstExpr(Expression expr) {
+    final constant = _evaluateConstantExpression(expr);
     asm.emitPushConstant(constant.accept(constantEmitter));
   }
 
@@ -526,7 +549,7 @@
     if (param.initializer == null) {
       return cp.add(const ConstantNull());
     }
-    final constant = constantEvaluator.evaluate(param.initializer);
+    final constant = _evaluateConstantExpression(param.initializer);
     return constant.accept(constantEmitter);
   }
 
@@ -567,6 +590,7 @@
     enclosingMember = node;
     enclosingFunction = node.function;
     parentFunction = null;
+    hasErrors = false;
     if (node.isInstanceMember ||
         node is Constructor ||
         (node is Procedure && node.isFactory)) {
@@ -595,7 +619,7 @@
     locals = new LocalVariables(node);
     // TODO(alexmarkov): improve caching in ConstantEvaluator and reuse it
     constantEvaluator = new ConstantEvaluator(constantsBackend, typeEnvironment,
-        coreTypes, strongMode, /* enableAsserts = */ true)
+        coreTypes, strongMode, /* enableAsserts = */ true, errorReporter)
       ..env = new EvaluationEnvironment();
     labeledStatements = <LabeledStatement, Label>{};
     switchCases = <SwitchCase, Label>{};
@@ -642,8 +666,10 @@
   }
 
   void end(Member node) {
-    metadata.mapping[node] =
-        new BytecodeMetadata(cp, asm.bytecode, asm.exceptionsTable, closures);
+    if (!hasErrors) {
+      metadata.mapping[node] =
+          new BytecodeMetadata(cp, asm.bytecode, asm.exceptionsTable, closures);
+    }
 
     enclosingClass = null;
     enclosingMember = null;
@@ -665,6 +691,7 @@
     constantEmitter = null;
     asm = null;
     savedAssemblers = null;
+    hasErrors = false;
   }
 
   void _genPrologue(Node node, FunctionNode function) {
@@ -2269,7 +2296,7 @@
   @override
   visitVariableDeclaration(VariableDeclaration node) {
     if (node.isConst) {
-      final Constant constant = constantEvaluator.evaluate(node.initializer);
+      final Constant constant = _evaluateConstantExpression(node.initializer);
       constantEvaluator.env.addVariableValue(node, constant);
     } else {
       final bool isCaptured = locals.isCaptured(node);
diff --git a/pkg/vm/lib/bytecode/local_vars.dart b/pkg/vm/lib/bytecode/local_vars.dart
index 4234beb..04fd726 100644
--- a/pkg/vm/lib/bytecode/local_vars.dart
+++ b/pkg/vm/lib/bytecode/local_vars.dart
@@ -9,7 +9,7 @@
 import 'package:kernel/ast.dart';
 import 'package:kernel/transformations/continuation.dart'
     show ContinuationVariables;
-import 'package:vm/bytecode/dbc.dart';
+import 'dbc.dart';
 
 class LocalVariables {
   final Map<TreeNode, Scope> _scopes = <TreeNode, Scope>{};
diff --git a/pkg/vm/lib/constants_error_reporter.dart b/pkg/vm/lib/constants_error_reporter.dart
new file mode 100644
index 0000000..50d1d6d
--- /dev/null
+++ b/pkg/vm/lib/constants_error_reporter.dart
@@ -0,0 +1,140 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Defines [ForwardConstantEvaluationErrors], an implementation of
+/// [constants.ErrorReporter] which uses package:front_end to report errors.
+library vm.constants_error_reporter;
+
+import 'package:front_end/src/api_prototype/compilation_message.dart'
+    show Severity;
+import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
+import 'package:front_end/src/fasta/fasta_codes.dart' as codes;
+
+import 'package:kernel/ast.dart'
+    show Constant, DartType, FileUriNode, IntConstant, Procedure, TreeNode;
+import 'package:kernel/transformations/constants.dart' as constants;
+import 'package:kernel/type_environment.dart' show TypeEnvironment;
+
+class ForwardConstantEvaluationErrors implements constants.ErrorReporter {
+  // This will get the currently active [CompilerContext] from a zone variable.
+  // If there is no active context, this will throw.
+  final CompilerContext compilerContext = CompilerContext.current;
+
+  final TypeEnvironment typeEnvironment;
+
+  ForwardConstantEvaluationErrors(this.typeEnvironment);
+
+  duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
+    final message = codes.templateConstEvalDuplicateKey.withArguments(key);
+    reportIt(context, message, node);
+  }
+
+  invalidDartType(List<TreeNode> context, TreeNode node, Constant receiver,
+      DartType expectedType) {
+    final message = codes.templateConstEvalInvalidType.withArguments(
+        receiver, expectedType, receiver.getType(typeEnvironment));
+    reportIt(context, message, node);
+  }
+
+  invalidBinaryOperandType(
+      List<TreeNode> context,
+      TreeNode node,
+      Constant receiver,
+      String op,
+      DartType expectedType,
+      DartType actualType) {
+    final message = codes.templateConstEvalInvalidBinaryOperandType
+        .withArguments(op, receiver, expectedType, actualType);
+    reportIt(context, message, node);
+  }
+
+  invalidMethodInvocation(
+      List<TreeNode> context, TreeNode node, Constant receiver, String op) {
+    final message = codes.templateConstEvalInvalidMethodInvocation
+        .withArguments(op, receiver);
+    reportIt(context, message, node);
+  }
+
+  invalidStaticInvocation(
+      List<TreeNode> context, TreeNode node, Procedure target) {
+    final message = codes.templateConstEvalInvalidStaticInvocation
+        .withArguments(target.name.toString());
+    reportIt(context, message, node);
+  }
+
+  invalidStringInterpolationOperand(
+      List<TreeNode> context, TreeNode node, Constant constant) {
+    final message = codes.templateConstEvalInvalidStringInterpolationOperand
+        .withArguments(constant);
+    reportIt(context, message, node);
+  }
+
+  zeroDivisor(
+      List<TreeNode> context, TreeNode node, IntConstant receiver, String op) {
+    final message = codes.templateConstEvalZeroDivisor
+        .withArguments(op, '${receiver.value}');
+    reportIt(context, message, node);
+  }
+
+  negativeShift(List<TreeNode> context, TreeNode node, IntConstant receiver,
+      String op, IntConstant argument) {
+    final message = codes.templateConstEvalNegativeShift
+        .withArguments(op, '${receiver.value}', '${argument.value}');
+    reportIt(context, message, node);
+  }
+
+  nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
+    final message =
+        codes.templateConstEvalNonConstantLiteral.withArguments(klass);
+    reportIt(context, message, node);
+  }
+
+  failedAssertion(List<TreeNode> context, TreeNode node, String string) {
+    final message = string == null
+        ? codes.messageConstEvalFailedAssertion
+        : codes.templateConstEvalFailedAssertionWithMessage
+            .withArguments(string);
+    reportIt(context, message, node);
+  }
+
+  nonConstantVariableGet(
+      List<TreeNode> context, TreeNode node, String variableName) {
+    final message = codes.templateConstEvalNonConstantVariableGet
+        .withArguments(variableName);
+    reportIt(context, message, node);
+  }
+
+  reportIt(List<TreeNode> context, codes.Message message, TreeNode node) {
+    final Uri uri = getFileUri(node);
+    final int fileOffset = getFileOffset(node);
+
+    final contextMessages = <codes.LocatedMessage>[];
+    for (final TreeNode node in context) {
+      final Uri uri = getFileUri(node);
+      final int fileOffset = getFileOffset(node);
+      contextMessages.add(codes.messageConstEvalContext
+          .withLocation(uri, fileOffset, codes.noLength));
+    }
+
+    final locatedMessage =
+        message.withLocation(uri, fileOffset, codes.noLength);
+
+    compilerContext.options
+        .report(locatedMessage, Severity.error, context: contextMessages);
+  }
+
+  getFileUri(TreeNode node) {
+    while (node is! FileUriNode) {
+      node = node.parent;
+    }
+    return (node as FileUriNode).fileUri;
+  }
+
+  getFileOffset(TreeNode node) {
+    while (node?.fileOffset == TreeNode.noOffset) {
+      node = node.parent;
+    }
+    return node == null ? TreeNode.noOffset : node.fileOffset;
+  }
+}
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 286d808..6670254 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -20,23 +20,14 @@
     show Severity;
 import 'package:kernel/type_environment.dart' show TypeEnvironment;
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
-import 'package:kernel/ast.dart'
-    show
-        Component,
-        Constant,
-        DartType,
-        Field,
-        FileUriNode,
-        IntConstant,
-        Procedure,
-        StaticGet,
-        TreeNode;
+import 'package:kernel/ast.dart' show Component, Field, StaticGet;
 import 'package:kernel/core_types.dart' show CoreTypes;
 import 'package:kernel/transformations/constants.dart' as constants;
 import 'package:kernel/vm/constants_native_effects.dart' as vm_constants;
 
 import 'bytecode/gen_bytecode.dart' show generateBytecode;
 
+import 'constants_error_reporter.dart' show ForwardConstantEvaluationErrors;
 import 'transformations/devirtualization.dart' as devirtualization
     show transformComponent;
 import 'transformations/mixin_deduplication.dart' as mixin_deduplication
@@ -82,14 +73,16 @@
         errorDetector);
   }
 
+  if (genBytecode && !errorDetector.hasCompilationErrors && component != null) {
+    await runWithFrontEndCompilerContext(source, options, component, () {
+      generateBytecode(component,
+          strongMode: options.strongMode, dropAST: dropAST);
+    });
+  }
+
   // Restore error handler (in case 'options' are reused).
   options.onProblem = errorDetector.previousErrorHandler;
 
-  if (!errorDetector.hasCompilationErrors && genBytecode && component != null) {
-    generateBytecode(component,
-        strongMode: options.strongMode, dropAST: dropAST);
-  }
-
   return component;
 }
 
@@ -135,6 +128,23 @@
   }
 }
 
+/// Runs given [action] with [CompilerContext]. This is needed to
+/// be able to report compile-time errors.
+Future<T> runWithFrontEndCompilerContext<T>(Uri source,
+    CompilerOptions compilerOptions, Component component, T action()) async {
+  final processedOptions = new ProcessedOptions(compilerOptions, [source]);
+
+  // Run within the context, so we have uri source tokens...
+  return await CompilerContext.runWithOptions(processedOptions,
+      (CompilerContext context) async {
+    // To make the fileUri/fileOffset -> line/column mapping, we need to
+    // pre-fill the map.
+    context.uriToSource.addAll(component.uriToSource);
+
+    return action();
+  });
+}
+
 Future _performConstantEvaluation(
     Uri source,
     CompilerOptions compilerOptions,
@@ -146,15 +156,7 @@
   final vmConstants =
       new vm_constants.VmConstantsBackend(environmentDefines, coreTypes);
 
-  final processedOptions = new ProcessedOptions(compilerOptions, [source]);
-
-  // Run within the context, so we have uri source tokens...
-  await CompilerContext.runWithOptions(processedOptions,
-      (CompilerContext context) async {
-    // To make the fileUri/fileOffset -> line/column mapping, we need to
-    // pre-fill the map.
-    context.uriToSource.addAll(component.uriToSource);
-
+  await runWithFrontEndCompilerContext(source, compilerOptions, component, () {
     final hierarchy = new ClassHierarchy(component);
     final typeEnvironment =
         new TypeEnvironment(coreTypes, hierarchy, strongMode: strongMode);
@@ -166,8 +168,7 @@
         strongMode: true,
         evaluateAnnotations: true,
         enableAsserts: enableAsserts,
-        errorReporter:
-            new ForwardConstantEvaluationErrors(context, typeEnvironment));
+        errorReporter: new ForwardConstantEvaluationErrors(typeEnvironment));
   });
 }
 
@@ -233,126 +234,6 @@
   }
 }
 
-class ForwardConstantEvaluationErrors implements constants.ErrorReporter {
-  final CompilerContext compilerContext;
-  final TypeEnvironment typeEnvironment;
-
-  ForwardConstantEvaluationErrors(this.compilerContext, this.typeEnvironment);
-
-  duplicateKey(List<TreeNode> context, TreeNode node, Constant key) {
-    final message = codes.templateConstEvalDuplicateKey.withArguments(key);
-    reportIt(context, message, node);
-  }
-
-  invalidDartType(List<TreeNode> context, TreeNode node, Constant receiver,
-      DartType expectedType) {
-    final message = codes.templateConstEvalInvalidType.withArguments(
-        receiver, expectedType, receiver.getType(typeEnvironment));
-    reportIt(context, message, node);
-  }
-
-  invalidBinaryOperandType(
-      List<TreeNode> context,
-      TreeNode node,
-      Constant receiver,
-      String op,
-      DartType expectedType,
-      DartType actualType) {
-    final message = codes.templateConstEvalInvalidBinaryOperandType
-        .withArguments(op, receiver, expectedType, actualType);
-    reportIt(context, message, node);
-  }
-
-  invalidMethodInvocation(
-      List<TreeNode> context, TreeNode node, Constant receiver, String op) {
-    final message = codes.templateConstEvalInvalidMethodInvocation
-        .withArguments(op, receiver);
-    reportIt(context, message, node);
-  }
-
-  invalidStaticInvocation(
-      List<TreeNode> context, TreeNode node, Procedure target) {
-    final message = codes.templateConstEvalInvalidStaticInvocation
-        .withArguments(target.name.toString());
-    reportIt(context, message, node);
-  }
-
-  invalidStringInterpolationOperand(
-      List<TreeNode> context, TreeNode node, Constant constant) {
-    final message = codes.templateConstEvalInvalidStringInterpolationOperand
-        .withArguments(constant);
-    reportIt(context, message, node);
-  }
-
-  zeroDivisor(
-      List<TreeNode> context, TreeNode node, IntConstant receiver, String op) {
-    final message = codes.templateConstEvalZeroDivisor
-        .withArguments(op, '${receiver.value}');
-    reportIt(context, message, node);
-  }
-
-  negativeShift(List<TreeNode> context, TreeNode node, IntConstant receiver,
-      String op, IntConstant argument) {
-    final message = codes.templateConstEvalNegativeShift
-        .withArguments(op, '${receiver.value}', '${argument.value}');
-    reportIt(context, message, node);
-  }
-
-  nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
-    final message =
-        codes.templateConstEvalNonConstantLiteral.withArguments(klass);
-    reportIt(context, message, node);
-  }
-
-  failedAssertion(List<TreeNode> context, TreeNode node, String string) {
-    final message = string == null
-        ? codes.messageConstEvalFailedAssertion
-        : codes.templateConstEvalFailedAssertionWithMessage
-            .withArguments(string);
-    reportIt(context, message, node);
-  }
-
-  nonConstantVariableGet(
-      List<TreeNode> context, TreeNode node, String variableName) {
-    final message = codes.templateConstEvalNonConstantVariableGet
-        .withArguments(variableName);
-    reportIt(context, message, node);
-  }
-
-  reportIt(List<TreeNode> context, codes.Message message, TreeNode node) {
-    final Uri uri = getFileUri(node);
-    final int fileOffset = getFileOffset(node);
-
-    final contextMessages = <codes.LocatedMessage>[];
-    for (final TreeNode node in context) {
-      final Uri uri = getFileUri(node);
-      final int fileOffset = getFileOffset(node);
-      contextMessages.add(codes.messageConstEvalContext
-          .withLocation(uri, fileOffset, codes.noLength));
-    }
-
-    final locatedMessage =
-        message.withLocation(uri, fileOffset, codes.noLength);
-
-    compilerContext.options
-        .report(locatedMessage, Severity.error, context: contextMessages);
-  }
-
-  getFileUri(TreeNode node) {
-    while (node is! FileUriNode) {
-      node = node.parent;
-    }
-    return (node as FileUriNode).fileUri;
-  }
-
-  getFileOffset(TreeNode node) {
-    while (node?.fileOffset == TreeNode.noOffset) {
-      node = node.parent;
-    }
-    return node == null ? TreeNode.noOffset : node.fileOffset;
-  }
-}
-
 bool parseCommandLineDefines(
     List<String> dFlags, Map<String, String> environmentDefines, String usage) {
   for (final String dflag in dFlags) {
diff --git a/pkg/vm/lib/metadata/bytecode.dart b/pkg/vm/lib/metadata/bytecode.dart
index 0ba6b84..aae9218 100644
--- a/pkg/vm/lib/metadata/bytecode.dart
+++ b/pkg/vm/lib/metadata/bytecode.dart
@@ -5,9 +5,9 @@
 library vm.metadata.bytecode;
 
 import 'package:kernel/ast.dart';
-import 'package:vm/bytecode/constant_pool.dart' show ConstantPool;
-import 'package:vm/bytecode/disassembler.dart' show BytecodeDisassembler;
-import 'package:vm/bytecode/exceptions.dart' show ExceptionsTable;
+import '../bytecode/constant_pool.dart' show ConstantPool;
+import '../bytecode/disassembler.dart' show BytecodeDisassembler;
+import '../bytecode/exceptions.dart' show ExceptionsTable;
 
 /// Metadata containing bytecode.
 ///
diff --git a/pkg/vm/test/bytecode/gen_bytecode_test.dart b/pkg/vm/test/bytecode/gen_bytecode_test.dart
index 2db1f50..14dd026 100644
--- a/pkg/vm/test/bytecode/gen_bytecode_test.dart
+++ b/pkg/vm/test/bytecode/gen_bytecode_test.dart
@@ -4,10 +4,15 @@
 
 import 'dart:io';
 
+import 'package:front_end/src/api_prototype/compiler_options.dart'
+    show CompilerOptions;
+import 'package:front_end/src/api_prototype/compilation_message.dart'
+    show CompilationMessage;
 import 'package:kernel/ast.dart';
 import 'package:kernel/kernel.dart';
 import 'package:test/test.dart';
 import 'package:vm/bytecode/gen_bytecode.dart' show generateBytecode;
+import 'package:vm/kernel_front_end.dart' show runWithFrontEndCompilerContext;
 
 import '../common_test_utils.dart';
 
@@ -20,9 +25,18 @@
   Component component = await compileTestCaseToKernelProgram(source,
       enableSuperMixins: enableSuperMixins);
 
-  // Need to omit source positions from bytecode as they are different on
-  // Linux and Windows (due to differences in newline characters).
-  generateBytecode(component, strongMode: true, omitSourcePositions: true);
+  final options = new CompilerOptions()
+    ..strongMode = true
+    ..reportMessages = true
+    ..onError = (CompilationMessage error) {
+      fail("Compilation error: ${error}");
+    };
+
+  await runWithFrontEndCompilerContext(source, options, component, () {
+    // Need to omit source positions from bytecode as they are different on
+    // Linux and Windows (due to differences in newline characters).
+    generateBytecode(component, strongMode: true, omitSourcePositions: true);
+  });
 
   final actual = kernelLibraryToString(component.mainMethod.enclosingLibrary);
   compareResultWithExpectationsFile(source, actual);
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index 7e16925..7a1f144 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -369,10 +369,10 @@
 const_constructor_nonconst_field_test/01: MissingCompileTimeError
 const_syntax_test/05: MissingCompileTimeError
 
-# The precomilation configuration uses a kernel2kernel constants evaluator
+# The precomilation and bytecode configurations use a kernel2kernel constants evaluator
 # which is is more correct than fasta/vm in JIT mode (i.e. it catches more
 # compile-time errors).
-[ $compiler != dart2analyzer && $compiler != dartkp && $fasta ]
+[ $compiler != dart2analyzer && $compiler != dartkb && $compiler != dartkp && $fasta ]
 compile_time_constant_o_test/01: MissingCompileTimeError # Issue 32983
 compile_time_constant_o_test/02: MissingCompileTimeError # Issue 32983
 const_dynamic_type_literal_test/02: MissingCompileTimeError # Issue 32983
@@ -381,10 +381,10 @@
 identical_const_test/03: MissingCompileTimeError # Issue 32983
 identical_const_test/04: MissingCompileTimeError # Issue 32983
 
-# The precomilation configuration uses a kernel2kernel constants evaluator
+# The precomilation and bytecode configurations use a kernel2kernel constants evaluator
 # which is is more correct than fasta/vm in JIT mode (i.e. it catches more
 # compile-time errors).
-[ $compiler != dart2analyzer && $compiler != dartkp && $fasta && $strong ]
+[ $compiler != dart2analyzer && $compiler != dartkb && $compiler != dartkp && $fasta && $strong ]
 compile_time_constant_k_test/01: MissingCompileTimeError
 compile_time_constant_k_test/02: MissingCompileTimeError
 compile_time_constant_k_test/03: MissingCompileTimeError
@@ -1382,9 +1382,6 @@
 call_method_implicit_tear_off_implements_function_test/06: RuntimeError
 call_method_must_not_be_field_test/03: RuntimeError # Issue 32265
 call_method_must_not_be_getter_test/03: RuntimeError # Issue 32265
-compile_time_constant_k_test/01: MissingCompileTimeError
-compile_time_constant_k_test/02: MissingCompileTimeError
-compile_time_constant_k_test/03: MissingCompileTimeError
 compile_time_constant_o_test/01: RuntimeError # KernelVM bug: Constant map duplicated key.
 compile_time_constant_o_test/02: RuntimeError # KernelVM bug: Constant map duplicated key.
 compile_time_constant_static5_test/11: CompileTimeError # Issue 31537
@@ -1434,8 +1431,7 @@
 generic_is_check_test: RuntimeError
 generic_no_such_method_dispatcher_simple_test: CompileTimeError # Issue 31533
 generic_no_such_method_dispatcher_test: CompileTimeError # Issue 31533
-generic_tearoff_test: CompileTimeError
-generic_tearoff_test: RuntimeError
+generic_tearoff_test: CompileTimeError, RuntimeError
 if_null_evaluation_order_test: Pass
 instantiate_tearoff_of_call_test: CompileTimeError
 issue18628_2_test/01: MissingCompileTimeError
@@ -1476,8 +1472,7 @@
 redirecting_factory_infinite_steps_test/01: MissingCompileTimeError
 redirecting_factory_malbounded_test/01: MissingCompileTimeError
 regress_22443_test: RuntimeError # KernelVM bug: Deferred loading kernel issue 30273.
-regress_23408_test: RuntimeError
-regress_23408_test: CompileTimeError # KernelVM bug: Deferred loading kernel issue 30273.
+regress_23408_test: RuntimeError, CompileTimeError # KernelVM bug: Deferred loading kernel issue 30273.
 regress_29025_test: CompileTimeError # Issue 31402 (Variable declaration)
 regress_29405_test: CompileTimeError # Issue 31402 (Invocation arguments)
 regress_30339_test: CompileTimeError # Issue 31402 (Variable declaration)