[VM] Catch errors for integer operations in kernel2kernel constant evaluator

Issue https://github.com/dart-lang/sdk/issues/33481

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

Change-Id: I7ca9825a0aa5f062732a759b4dd116e716a0d1b3
Reviewed-on: https://dart-review.googlesource.com/60580
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index e4aecb4..5aea704 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -949,6 +949,34 @@
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
     Message Function(
+        String string,
+        String string2,
+        String
+            string3)> templateConstEvalNegativeShift = const Template<
+        Message Function(String string, String string2, String string3)>(
+    messageTemplate:
+        r"""Binary operator '#string' on '#string2' requires non-negative operand, but was '#string3'.""",
+    withArguments: _withArgumentsConstEvalNegativeShift);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string, String string2, String string3)>
+    codeConstEvalNegativeShift =
+    const Code<Message Function(String string, String string2, String string3)>(
+        "ConstEvalNegativeShift", templateConstEvalNegativeShift,
+        dart2jsCode: "INVALID_CONSTANT_SHIFT");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalNegativeShift(
+    String string, String string2, String string3) {
+  return new Message(codeConstEvalNegativeShift,
+      message:
+          """Binary operator '${string}' on '${string2}' requires non-negative operand, but was '${string3}'.""",
+      arguments: {'string': string, 'string2': string2, 'string3': string3});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
         String
             string)> templateConstEvalNonConstantLiteral = const Template<
         Message Function(String string)>(
@@ -971,6 +999,33 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String string,
+        String
+            string2)> templateConstEvalZeroDivisor = const Template<
+        Message Function(String string, String string2)>(
+    messageTemplate:
+        r"""Binary operator '#string' on '#string2' requires non-zero divisor, but divisor was '0'.""",
+    withArguments: _withArgumentsConstEvalZeroDivisor);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string, String string2)>
+    codeConstEvalZeroDivisor =
+    const Code<Message Function(String string, String string2)>(
+        "ConstEvalZeroDivisor", templateConstEvalZeroDivisor,
+        analyzerCode: "CONST_EVAL_THROWS_IDBZE",
+        dart2jsCode: "INVALID_CONSTANT_DIV");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalZeroDivisor(String string, String string2) {
+  return new Message(codeConstEvalZeroDivisor,
+      message:
+          """Binary operator '${string}' on '${string2}' requires non-zero divisor, but divisor was '0'.""",
+      arguments: {'string': string, 'string2': string2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeConstFactory = messageConstFactory;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 8249a81..7820263 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -80,8 +80,11 @@
 ConstEvalInvalidStringInterpolationOperand/example: Fail
 ConstEvalInvalidType/analyzerCode: Fail # CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH / CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH / CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH / ...
 ConstEvalInvalidType/example: Fail
+ConstEvalNegativeShift/analyzerCode: Fail # http://dartbug.com/33481
+ConstEvalNegativeShift/example: Fail
 ConstEvalNonConstantLiteral/dart2jsCode: Fail
 ConstEvalNonConstantLiteral/example: Fail
+ConstEvalZeroDivisor/example: Fail
 ConstFieldWithoutInitializer/example: Fail
 ConstructorNotFound/analyzerCode: Fail
 ConstructorNotFound/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 356902d..ebaf317 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -102,6 +102,15 @@
 ConstEvalInvalidBinaryOperandType:
   template: "Binary operator '#string' on '#constant' requires operand of type '#type', but was of type '#type2'."
 
+ConstEvalZeroDivisor:
+  template: "Binary operator '#string' on '#string2' requires non-zero divisor, but divisor was '0'."
+  dart2jsCode: INVALID_CONSTANT_DIV
+  analyzerCode: CONST_EVAL_THROWS_IDBZE
+
+ConstEvalNegativeShift:
+  template: "Binary operator '#string' on '#string2' requires non-negative operand, but was '#string3'."
+  dart2jsCode: INVALID_CONSTANT_SHIFT
+
 ConstEvalInvalidMethodInvocation:
   template: "The method '#string' can't be invoked on '#constant' within a const context."
   analyzerCode: UNDEFINED_OPERATOR
diff --git a/pkg/kernel/lib/transformations/constants.dart b/pkg/kernel/lib/transformations/constants.dart
index cf18494..4e62d58 100644
--- a/pkg/kernel/lib/transformations/constants.dart
+++ b/pkg/kernel/lib/transformations/constants.dart
@@ -719,8 +719,14 @@
         }
       } else if (arguments.length == 1) {
         final Constant other = arguments[0];
+        final op = node.name.name;
         if (other is IntConstant) {
-          switch (node.name.name) {
+          if ((op == '<<' || op == '>>') && other.value < 0) {
+            errorReporter.negativeShift(contextChain,
+                node.arguments.positional.first, receiver, op, other);
+            throw const _AbortCurrentEvaluation();
+          }
+          switch (op) {
             case '|':
               return canonicalize(
                   new IntConstant(receiver.value | other.value));
@@ -739,12 +745,18 @@
           }
         }
 
-        if (other is IntConstant || other is DoubleConstant) {
-          final num value = (other is IntConstant)
-              ? other.value
-              : (other as DoubleConstant).value;
+        if (other is IntConstant) {
+          if (other.value == 0 && (op == '%' || op == '~/')) {
+            errorReporter.zeroDivisor(
+                contextChain, node.arguments.positional.first, receiver, op);
+            throw const _AbortCurrentEvaluation();
+          }
+
           return evaluateBinaryNumericOperation(
-              node.name.name, receiver.value, value, node);
+              node.name.name, receiver.value, other.value, node);
+        } else if (other is DoubleConstant) {
+          return evaluateBinaryNumericOperation(
+              node.name.name, receiver.value, other.value, node);
         }
 
         errorReporter.invalidBinaryOperandType(
@@ -1150,9 +1162,6 @@
     }
     return value;
   }
-
-  static const kMaxInt64 = (1 << 63) - 1;
-  static const kMinInt64 = -(1 << 63);
 }
 
 /// Holds the necessary information for a constant object, namely
@@ -1256,6 +1265,10 @@
       List<TreeNode> context, TreeNode node, Procedure target);
   invalidStringInterpolationOperand(
       List<TreeNode> context, TreeNode node, Constant constant);
+  zeroDivisor(
+      List<TreeNode> context, TreeNode node, IntConstant receiver, String op);
+  negativeShift(List<TreeNode> context, TreeNode node, IntConstant receiver,
+      String op, IntConstant argument);
   nonConstLiteral(List<TreeNode> context, TreeNode node, String klass);
   duplicateKey(List<TreeNode> context, TreeNode node, Constant key);
   failedAssertion(List<TreeNode> context, TreeNode node, String message);
@@ -1323,6 +1336,24 @@
         node);
   }
 
+  zeroDivisor(
+      List<TreeNode> context, TreeNode node, IntConstant receiver, String op) {
+    report(
+        context,
+        "Binary operator '$op' on '${receiver.value}' requires non-zero "
+        "divisor, but divisor was '0'.",
+        node);
+  }
+
+  negativeShift(List<TreeNode> context, TreeNode node, IntConstant receiver,
+      String op, IntConstant argument) {
+    report(
+        context,
+        "Binary operator '$op' on '${receiver.value}' requires non-negative "
+        "operand, but was '${argument.value}'.",
+        node);
+  }
+
   nonConstLiteral(List<TreeNode> context, TreeNode node, String klass) {
     report(
         context,
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 928cac8..162890e 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -27,6 +27,7 @@
         DartType,
         Field,
         FileUriNode,
+        IntConstant,
         Procedure,
         StaticGet,
         TreeNode;
@@ -283,6 +284,20 @@
     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);
diff --git a/tests/language_2/language_2_analyzer.status b/tests/language_2/language_2_analyzer.status
index b22bdfb..671b37d 100644
--- a/tests/language_2/language_2_analyzer.status
+++ b/tests/language_2/language_2_analyzer.status
@@ -142,6 +142,9 @@
 vm/lazy_deopt_with_exception_test: Pass
 vm/reflect_core_vm_test: CompileTimeError
 vm/regress_27201_test: SkipByDesign # Loads bad library, so will always crash.
+vm/regress_33469_test/01: Crash # http://dartbug.com/33481
+vm/regress_33469_test/02: Crash # http://dartbug.com/33481
+vm/regress_33469_test/03: MissingCompileTimeError # http://dartbug.com/33481
 void_type_override_test/00: MissingCompileTimeError
 void_type_override_test/00b: MissingCompileTimeError
 void_type_override_test/01: MissingCompileTimeError
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index 686ef58..d5c998a 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -212,6 +212,10 @@
 implicit_creation/implicit_const_not_default_values_test/e5: MissingCompileTimeError
 implicit_creation/implicit_const_not_default_values_test/e7: MissingCompileTimeError
 implicit_creation/implicit_const_not_default_values_test/e8: MissingCompileTimeError
+vm/regress_33469_test/01: MissingCompileTimeError
+vm/regress_33469_test/02: MissingCompileTimeError
+vm/regress_33469_test/03: MissingCompileTimeError
+vm/regress_33469_test/04: MissingCompileTimeError
 
 [ $fasta ]
 abstract_override_adds_optional_args_concrete_subclass_test: MissingCompileTimeError # Issue 32014.
@@ -805,8 +809,8 @@
 named_constructor_test/01: MissingRuntimeError # Fasta bug: Bad compilation of constructor reference.
 named_parameters_default_eq_test/none: RuntimeError
 nested_generic_closure_test: RuntimeError
-no_main_test/01: Skip
 no_main_test/01: DartkCrash
+no_main_test/01: Skip
 no_such_method_mock_test: RuntimeError # Issue 31426
 null_no_such_method_test: CompileTimeError # Issue 31533
 override_inheritance_field_test/04: CompileTimeError # Issue 31616
diff --git a/tests/language_2/vm/regress_33469_test.dart b/tests/language_2/vm/regress_33469_test.dart
new file mode 100644
index 0000000..a18a6ad
--- /dev/null
+++ b/tests/language_2/vm/regress_33469_test.dart
@@ -0,0 +1,15 @@
+// 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.
+
+class X {
+  final x;
+  const X(this.x);
+}
+
+void main() {
+  print(const X(1 << -1).x);  /// 01: compile-time error
+  print(const X(1 >> -1).x);  /// 02: compile-time error
+  print(const X(1 % 0).x);    /// 03: compile-time error
+  print(const X(1 ~/ 0).x);   /// 04: compile-time error
+}