Move all int literal parsing and checking code together.

Report error for negated hex literals > 2^63.

Optimized common case of web int literal exactness check.

Fixes https://github.com/dart-lang/sdk/issues/33311

Change-Id: Ib72b2dbb21e42489ee2d06b17302daf83d560df1
Reviewed-on: https://dart-review.googlesource.com/72802
Commit-Queue: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 92aeaeb..87570fa 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -75,7 +75,7 @@
         IncompletePropertyAccessGenerator,
         IncompleteSendGenerator,
         IndexedAccessGenerator,
-        LargeIntAccessGenerator,
+        IntAccessGenerator,
         LoadLibraryGenerator,
         ParenthesizedExpressionGenerator,
         PrefixUseGenerator,
@@ -136,8 +136,6 @@
 
   final bool stringExpectedAfterNative;
 
-  final bool errorOnUnexactWebIntLiterals;
-
   /// Whether to ignore an unresolved reference to `main` within the body of
   /// `_getMainClosure` when compiling the current library.
   ///
@@ -231,8 +229,6 @@
             library.loader.target.backendTarget.enableNative(library.uri),
         stringExpectedAfterNative =
             library.loader.target.backendTarget.nativeExtensionExpectsString,
-        errorOnUnexactWebIntLiterals =
-            library.loader.target.backendTarget.errorOnUnexactWebIntLiterals,
         ignoreMainInGetMainClosure = library.uri.scheme == 'dart' &&
             (library.uri.path == "_builtin" || library.uri.path == "ui"),
         needsImplicitSuperInitializer =
@@ -1806,15 +1802,7 @@
   @override
   void handleLiteralInt(Token token) {
     debugEvent("LiteralInt");
-    int value = int.parse(token.lexeme, onError: (_) => null);
-    if (value == null) {
-      push(new LargeIntAccessGenerator(this, token));
-    } else {
-      if (errorOnUnexactWebIntLiterals) {
-        checkWebIntLiteralsErrorIfUnexact(value, token);
-      }
-      push(forest.literalInt(value, token));
-    }
+    push(IntAccessGenerator.parseIntLiteral(this, token));
   }
 
   @override
@@ -2757,14 +2745,8 @@
       if (optional("-", token)) {
         operator = "unary-";
 
-        if (receiver is LargeIntAccessGenerator) {
-          int value = int.tryParse("-" + receiver.token.lexeme);
-          if (value != null) {
-            receiverValue = forest.literalInt(value, receiver.token);
-            if (errorOnUnexactWebIntLiterals) {
-              checkWebIntLiteralsErrorIfUnexact(value, token);
-            }
-          }
+        if (receiver is IntAccessGenerator) {
+          receiverValue = receiver.buildNegatedRead();
         }
       }
       bool isSuper = false;
@@ -4489,26 +4471,6 @@
         forest.isErroneousNode(node);
   }
 
-  void checkWebIntLiteralsErrorIfUnexact(int value, Token token) {
-    BigInt asInt = new BigInt.from(value).toUnsigned(64);
-    BigInt asDouble = new BigInt.from(asInt.toDouble());
-    if (asInt != asDouble) {
-      String nearest;
-      if (token.lexeme.startsWith("0x") || token.lexeme.startsWith("0X")) {
-        nearest = '0x${asDouble.toRadixString(16)}';
-      } else {
-        nearest = '$asDouble';
-      }
-      library.addProblem(
-          fasta.templateWebLiteralCannotBeRepresentedExactly
-              .withArguments(token.lexeme, nearest),
-          token.charOffset,
-          token.charCount,
-          uri,
-          wasHandled: true);
-    }
-  }
-
   @override
   DartType buildDartType(UnresolvedType<KernelTypeBuilder> unresolvedType,
       {bool nonInstanceAccessIsError: false}) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index f81b7ef..ba11d9f 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -25,7 +25,8 @@
         templateMissingExplicitTypeArguments,
         templateNotAPrefixInTypeAnnotation,
         templateNotAType,
-        templateUnresolvedPrefixInTypeAnnotation;
+        templateUnresolvedPrefixInTypeAnnotation,
+        templateWebLiteralCannotBeRepresentedExactly;
 
 import '../names.dart'
     show
@@ -663,10 +664,9 @@
   String get debugName => "ReadOnlyAccessGenerator";
 }
 
-abstract class LargeIntAccessGenerator implements Generator {
-  factory LargeIntAccessGenerator(
-      ExpressionGeneratorHelper helper, Token token) {
-    return helper.forest.largeIntAccessGenerator(helper, token);
+abstract class IntAccessGenerator implements Generator {
+  factory IntAccessGenerator(ExpressionGeneratorHelper helper, Token token) {
+    return helper.forest.intAccessGenerator(helper, token);
   }
 
   // TODO(ahe): This should probably be calling unhandled.
@@ -674,7 +674,66 @@
   String get plainNameForRead => null;
 
   @override
-  String get debugName => "LargeIntAccessGenerator";
+  String get debugName => "IntAccessGenerator";
+
+  static void checkWebIntLiteralsErrorIfUnexact(
+      ExpressionGeneratorHelper helper, int value, Token token) {
+    if (value >= 0 && value <= (1 << 53)) return;
+    if (!helper.library.loader.target.backendTarget
+        .errorOnUnexactWebIntLiterals) return;
+    BigInt asInt = new BigInt.from(value).toUnsigned(64);
+    BigInt asDouble = new BigInt.from(asInt.toDouble());
+    if (asInt != asDouble) {
+      String nearest;
+      if (token.lexeme.startsWith("0x") || token.lexeme.startsWith("0X")) {
+        nearest = '0x${asDouble.toRadixString(16)}';
+      } else {
+        nearest = '$asDouble';
+      }
+      helper.addProblem(
+          templateWebLiteralCannotBeRepresentedExactly.withArguments(
+              token.lexeme, nearest),
+          token.charOffset,
+          token.charCount);
+    }
+  }
+
+  static Object parseIntLiteral(ExpressionGeneratorHelper helper, Token token) {
+    int value = int.tryParse(token.lexeme);
+    // Postpone parsing of literals resulting in a negative value
+    // (hex literals >= 2^63). These are only allowed when not negated.
+    if (value == null || value < 0) {
+      return new IntAccessGenerator(helper, token);
+    } else {
+      checkWebIntLiteralsErrorIfUnexact(helper, value, token);
+      return helper.forest.literalInt(value, token);
+    }
+  }
+
+  Expression parseOrError(String literal, Token token) {
+    int value = int.tryParse(literal);
+    if (value != null) {
+      checkWebIntLiteralsErrorIfUnexact(helper, value, token);
+      return helper.forest.literalInt(value, token);
+    } else {
+      return buildError();
+    }
+  }
+
+  @override
+  Expression buildSimpleRead() {
+    // Called when literal that previously failed to parse, or resulted in
+    // a negative value (hex literals >= 2^63), is not negated.
+    // Try parsing again, this time accepting negative values.
+    return parseOrError(token.lexeme, token);
+  }
+
+  Expression buildNegatedRead() {
+    // Called when literal that previously failed to parse, or resulted in
+    // a negative value (hex literals >= 2^63), is negated.
+    // Try parsing with a '-' in front.
+    return parseOrError("-" + token.lexeme, token);
+  }
 
   SyntheticExpressionJudgment buildError() {
     return helper.buildProblem(
diff --git a/pkg/front_end/lib/src/fasta/kernel/fangorn.dart b/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
index fd18029..f0f16e5 100644
--- a/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
@@ -44,7 +44,7 @@
         KernelDelayedAssignment,
         KernelDelayedPostfixIncrement,
         KernelIndexedAccessGenerator,
-        KernelLargeIntAccessGenerator,
+        KernelIntAccessGenerator,
         KernelLoadLibraryGenerator,
         KernelNullAwarePropertyAccessGenerator,
         KernelPrefixUseGenerator,
@@ -697,9 +697,9 @@
   }
 
   @override
-  KernelLargeIntAccessGenerator largeIntAccessGenerator(
+  KernelIntAccessGenerator intAccessGenerator(
       ExpressionGeneratorHelper helper, Token token) {
-    return new KernelLargeIntAccessGenerator(helper, token);
+    return new KernelIntAccessGenerator(helper, token);
   }
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/kernel/forest.dart b/pkg/front_end/lib/src/fasta/kernel/forest.dart
index 86f4e31..ffb30fa 100644
--- a/pkg/front_end/lib/src/fasta/kernel/forest.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/forest.dart
@@ -386,7 +386,7 @@
   Generator readOnlyAccessGenerator(ExpressionGeneratorHelper helper,
       Token location, Expression expression, String plainNameForRead);
 
-  Generator largeIntAccessGenerator(
+  Generator intAccessGenerator(
       ExpressionGeneratorHelper helper, Token location);
 
   Generator unresolvedNameGenerator(
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
index 403b63e..a3de284 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
@@ -45,7 +45,7 @@
         ExpressionGenerator,
         Generator,
         IndexedAccessGenerator,
-        LargeIntAccessGenerator,
+        IntAccessGenerator,
         LoadLibraryGenerator,
         NullAwarePropertyAccessGenerator,
         PrefixUseGenerator,
@@ -1390,9 +1390,8 @@
   }
 }
 
-class KernelLargeIntAccessGenerator extends KernelGenerator
-    with LargeIntAccessGenerator {
-  KernelLargeIntAccessGenerator(ExpressionGeneratorHelper helper, Token token)
+class KernelIntAccessGenerator extends KernelGenerator with IntAccessGenerator {
+  KernelIntAccessGenerator(ExpressionGeneratorHelper helper, Token token)
       : super(helper, token);
 
   @override
diff --git a/pkg/front_end/test/fasta/generator_to_string_test.dart b/pkg/front_end/test/fasta/generator_to_string_test.dart
index b8baa33..cd77f74 100644
--- a/pkg/front_end/test/fasta/generator_to_string_test.dart
+++ b/pkg/front_end/test/fasta/generator_to_string_test.dart
@@ -59,7 +59,7 @@
         KernelDelayedAssignment,
         KernelDelayedPostfixIncrement,
         KernelIndexedAccessGenerator,
-        KernelLargeIntAccessGenerator,
+        KernelIntAccessGenerator,
         KernelLoadLibraryGenerator,
         KernelNullAwarePropertyAccessGenerator,
         KernelPrefixUseGenerator,
@@ -222,8 +222,8 @@
         "ReadOnlyAccessGenerator(offset: 4, expression: expression,"
         " plainNameForRead: foo, value: null)",
         new KernelReadOnlyAccessGenerator(helper, token, expression, "foo"));
-    check("LargeIntAccessGenerator(offset: 4, lexeme: myToken)",
-        new KernelLargeIntAccessGenerator(helper, token));
+    check("IntAccessGenerator(offset: 4, lexeme: myToken)",
+        new KernelIntAccessGenerator(helper, token));
     check(
         "ParenthesizedExpressionGenerator(offset: 4, expression: expression,"
         " plainNameForRead: null, value: null)",
diff --git a/runtime/tests/vm/dart/byte_array_optimized_test.dart b/runtime/tests/vm/dart/byte_array_optimized_test.dart
index 827b93c..ca3b432 100644
--- a/runtime/tests/vm/dart/byte_array_optimized_test.dart
+++ b/runtime/tests/vm/dart/byte_array_optimized_test.dart
@@ -899,9 +899,9 @@
     Expect.isTrue(copy is Uint64List);
     Expect.equals(4, region.length);
     Expect.listEquals([3, 4, 5, 6], region);
-    array.setRange(3, 7, [-0x8000000000000001, 0, 1, 0xFFFFFFFFFFFFFFFF]);
+    array.setRange(3, 7, [-0x8000000000000000, 0, 1, 0xFFFFFFFFFFFFFFFF]);
     Expect.listEquals(
-        [0, 1, 2, -0x8000000000000001, 0, 1, 0xFFFFFFFFFFFFFFFF, 7, 8, 9],
+        [0, 1, 2, -0x8000000000000000, 0, 1, 0xFFFFFFFFFFFFFFFF, 7, 8, 9],
         array);
   }
 
diff --git a/runtime/tests/vm/dart/byte_array_test.dart b/runtime/tests/vm/dart/byte_array_test.dart
index d3dc4b2..a476301 100644
--- a/runtime/tests/vm/dart/byte_array_test.dart
+++ b/runtime/tests/vm/dart/byte_array_test.dart
@@ -1022,9 +1022,9 @@
     Expect.isTrue(copy is Uint64List);
     Expect.equals(4, region.length);
     Expect.listEquals([3, 4, 5, 6], region);
-    array.setRange(3, 7, [-0x8000000000000001, 0, 1, 0xFFFFFFFFFFFFFFFF]);
+    array.setRange(3, 7, [-0x8000000000000000, 0, 1, 0xFFFFFFFFFFFFFFFF]);
     Expect.listEquals(
-        [0, 1, 2, -0x8000000000000001, 0, 1, 0xFFFFFFFFFFFFFFFF, 7, 8, 9],
+        [0, 1, 2, -0x8000000000000000, 0, 1, 0xFFFFFFFFFFFFFFFF, 7, 8, 9],
         array);
   }
 
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 3fd79b0..9170ada 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -35,10 +35,6 @@
 dart/redirection_type_shuffling_test/none: RuntimeError
 dart/snapshot_version_test: RuntimeError
 
-[ $compiler == dart2analyzer ]
-dart/byte_array_optimized_test: CompileTimeError # int64
-dart/byte_array_test: CompileTimeError # int64
-
 [ $compiler == dart2js ]
 dart/byte_array_optimized_test: Skip # compilers not aware of byte arrays
 dart/byte_array_test: Skip # compilers not aware of byte arrays
diff --git a/tests/co19/co19-kernel.status b/tests/co19/co19-kernel.status
index 8d25bec..e276dd0 100644
--- a/tests/co19/co19-kernel.status
+++ b/tests/co19/co19-kernel.status
@@ -1318,6 +1318,7 @@
 LibTest/core/AssertionError/AssertionError_A01_t01: CompileTimeError
 LibTest/core/AssertionError/message_A01_t01: CompileTimeError
 LibTest/core/AssertionError/toString_A01_t01: CompileTimeError
+LibTest/core/Duration/Duration_A02_t01: CompileTimeError
 LibTest/core/FallThroughError/toString_A01_t02: CompileTimeError
 LibTest/core/Invocation/isAccessor_A01_t01: CompileTimeError
 LibTest/core/Invocation/isAccessor_A01_t02: CompileTimeError
diff --git a/tests/language_2/int64_literal_test.dart b/tests/language_2/int64_literal_test.dart
index 9c0cb77..5c54736 100644
--- a/tests/language_2/int64_literal_test.dart
+++ b/tests/language_2/int64_literal_test.dart
@@ -3,22 +3,30 @@
 const String realMaxInt64Value = '9223372036854775807';
 const String realMinInt64Value = '-9223372036854775808';
 
+const int i21 = 2097152;
+
 main() {
-  int minInt64Value = (-9223372036854775807) - 1;
-  minInt64Value = -(1 << 63);               /// 01: ok
+  int minInt64Value = -1 * i21 * i21 * i21;
+  minInt64Value = -9223372036854775807 - 1; /// 01: ok
   minInt64Value = -9223372036854775808;     /// 02: ok
   minInt64Value = -(9223372036854775808);   /// 03: compile-time error
   minInt64Value = -(0x8000000000000000);    /// 04: ok
   minInt64Value = 0x8000000000000000;       /// 05: ok
+  minInt64Value = -0x8000000000000000;      /// 06: ok
 
   Expect.equals('$minInt64Value', realMinInt64Value);
   Expect.equals('${minInt64Value - 1}', realMaxInt64Value);
 
-  int maxInt64Value = 9223372036854775807;
-  maxInt64Value = (1 << 63) - 1;            /// 10: ok
-  maxInt64Value = 9223372036854775807;      /// 20: ok
-  maxInt64Value = 9223372036854775808 - 1;  /// 30: compile-time error
-  maxInt64Value = 0x8000000000000000 - 1;   /// 40: ok
+  int maxInt64Value = 1 * i21 * i21 * i21 - 1;
+  maxInt64Value = 9223372036854775807;      /// 11: ok
+  maxInt64Value = 9223372036854775807;      /// 12: ok
+  maxInt64Value = 9223372036854775808 - 1;  /// 13: compile-time error
+  maxInt64Value = -9223372036854775808 - 1; /// 14: ok
+  maxInt64Value = -9223372036854775809;     /// 15: compile-time error
+  maxInt64Value = 0x8000000000000000 - 1;   /// 16: ok
+  maxInt64Value = -0x8000000000000000 - 1;  /// 17: ok
+  maxInt64Value = -0x8000000000000001;      /// 18: compile-time error
+  maxInt64Value = -(0x8000000000000001);    /// 19: ok
 
   Expect.equals('$maxInt64Value', realMaxInt64Value);
   Expect.equals('${maxInt64Value + 1}', realMinInt64Value);
diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status
index 708b760..228a5bd 100644
--- a/tests/language_2/language_2_dart2js.status
+++ b/tests/language_2/language_2_dart2js.status
@@ -36,13 +36,17 @@
 instantiate_tearoff_of_call_test: RuntimeError
 int2_test: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
 int64_literal_test/01: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
-int64_literal_test/02: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
-int64_literal_test/04: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
-int64_literal_test/05: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
-int64_literal_test/10: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
-int64_literal_test/20: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
-int64_literal_test/40: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
-int64_literal_test/none: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/02: RuntimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/04: RuntimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/05: RuntimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/06: RuntimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/11: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/12: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/14: RuntimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/16: RuntimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/17: RuntimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/19: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
+int64_literal_test/none: RuntimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
 issue23244_test: RuntimeError # Isolates - enum canonicalization - Issue 23244
 issue32353_test: CompileTimeError
 issue34404_flutter_test: CompileTimeError # --supermixin not supported
@@ -589,14 +593,6 @@
 infinity_test: RuntimeError # non JS number semantics - Issue 4984
 instance_creation_in_function_annotation_test: RuntimeError
 instantiate_tearoff_of_call_test: CompileTimeError
-int64_literal_test/01: RuntimeError
-int64_literal_test/02: RuntimeError
-int64_literal_test/04: RuntimeError
-int64_literal_test/05: RuntimeError
-int64_literal_test/10: RuntimeError
-int64_literal_test/20: RuntimeError
-int64_literal_test/40: RuntimeError
-int64_literal_test/none: RuntimeError
 integer_division_by_zero_test: RuntimeError # Issue 8301
 internal_library_test/02: Crash
 invocation_mirror_invoke_on2_test: RuntimeError