From 59cdc65dc7d65e0e730cd147a0acca3f57c43460 Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Thu, 9 Nov 2017 16:58:38 +0000 Subject: [PATCH] [VM] Relax constraints for integer literals according to int64 spec Handling of integer literals is corrected according to the informal spec for the fixed-size integers: https://github.com/dart-lang/sdk/blob/master/docs/language/informal/int64.md This change allows unsigned 64-bit hexadecimal literals and MIN_INT64 to be used when running on the VM with --limit-ints-to-64-bits option. Change-Id: Ied7a7688768bcf4ff40ab5b499dd71622d2c3948 Reviewed-on: https://dart-review.googlesource.com/19400 Reviewed-by: Siva Annamalai Commit-Queue: Alexander Markov --- runtime/lib/double.cc | 2 -- .../tests/vm/dart/truncating_ints_test.dart | 12 +++++++++ runtime/vm/object.cc | 25 ++++++++++++++++--- runtime/vm/os_android.cc | 8 +++++- runtime/vm/os_fuchsia.cc | 8 +++++- runtime/vm/os_linux.cc | 8 +++++- runtime/vm/os_macos.cc | 8 +++++- runtime/vm/os_win.cc | 8 +++++- 8 files changed, 68 insertions(+), 11 deletions(-) diff --git a/runtime/lib/double.cc b/runtime/lib/double.cc index ac9350c8fa8..9787af0e26a 100644 --- a/runtime/lib/double.cc +++ b/runtime/lib/double.cc @@ -75,8 +75,6 @@ static RawInteger* DoubleToInteger(double val, const char* error_msg) { Exceptions::ThrowByType(Exceptions::kUnsupported, args); } if (FLAG_limit_ints_to_64_bits) { - // TODO(alexmarkov): decide on the double-to-integer conversion semantics - // in truncating mode. int64_t ival = 0; if (val <= static_cast(kMinInt64)) { ival = kMinInt64; diff --git a/runtime/tests/vm/dart/truncating_ints_test.dart b/runtime/tests/vm/dart/truncating_ints_test.dart index 59524a14f71..c464457253b 100644 --- a/runtime/tests/vm/dart/truncating_ints_test.dart +++ b/runtime/tests/vm/dart/truncating_ints_test.dart @@ -167,6 +167,17 @@ test_shl(var v2, var v3, var v8, var v40) { } } +test_literals() { + Expect.equals(0x7fffffffffffffff, 9223372036854775807); + Expect.equals(0x8000000000000000, -9223372036854775808); + Expect.equals(0x8000000000000000, -0x8000000000000000); + Expect.equals(0x8000000000000001, -0x7fffffffffffffff); + Expect.equals(0xabcdef0123456789, -0x543210FEDCBA9877); + Expect.equals(0xffffffffffffffff, -1); + Expect.equals(-9223372036854775808, -0x8000000000000000); + Expect.equals(9223372036854775807 + 1, -9223372036854775808); +} + main() { var v2 = 2; // smi var v3 = 3; // smi @@ -181,4 +192,5 @@ main() { test_sub(v2, v3, v3fxx, v5fxx, v7fxx, n60xx); test_mul(v2, v3, v3fxx, v5fxx, v7fxx, n60xx); test_shl(v2, v3, v8, v40); + test_literals(); } diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 9dce69d2bbe..8faa7da2960 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -18313,12 +18313,22 @@ const char* Integer::ToCString() const { return "NULL Integer"; } +// String representation of kMaxInt64 + 1. +static const char* kMaxInt64Plus1 = "9223372036854775808"; + RawInteger* Integer::New(const String& str, Heap::Space space) { // We are not supposed to have integers represented as two byte strings. ASSERT(str.IsOneByteString()); - int64_t value; - if (!OS::StringToInt64(str.ToCString(), &value)) { + int64_t value = 0; + const char* cstr = str.ToCString(); + if (!OS::StringToInt64(cstr, &value)) { if (FLAG_limit_ints_to_64_bits) { + if (strcmp(cstr, kMaxInt64Plus1) == 0) { + // Allow MAX_INT64 + 1 integer literal as it can be used as an argument + // of unary minus to produce MIN_INT64 value. The value is automatically + // wrapped to MIN_INT64. + return Integer::New(kMinInt64, space); + } // Out of range. return Integer::null(); } @@ -18334,9 +18344,16 @@ RawInteger* Integer::New(const String& str, Heap::Space space) { RawInteger* Integer::NewCanonical(const String& str) { // We are not supposed to have integers represented as two byte strings. ASSERT(str.IsOneByteString()); - int64_t value; - if (!OS::StringToInt64(str.ToCString(), &value)) { + int64_t value = 0; + const char* cstr = str.ToCString(); + if (!OS::StringToInt64(cstr, &value)) { if (FLAG_limit_ints_to_64_bits) { + if (strcmp(cstr, kMaxInt64Plus1) == 0) { + // Allow MAX_INT64 + 1 integer literal as it can be used as an argument + // of unary minus to produce MIN_INT64 value. The value is automatically + // wrapped to MIN_INT64. + return Mint::NewCanonical(kMinInt64); + } // Out of range. return Integer::null(); } diff --git a/runtime/vm/os_android.cc b/runtime/vm/os_android.cc index 5c573b820ec..01e40003b4d 100644 --- a/runtime/vm/os_android.cc +++ b/runtime/vm/os_android.cc @@ -353,7 +353,13 @@ bool OS::StringToInt64(const char* str, int64_t* value) { base = 16; } errno = 0; - *value = strtoll(str, &endptr, base); + if (FLAG_limit_ints_to_64_bits && (base == 16)) { + // Unsigned 64-bit hexadecimal integer literals are allowed but + // immediately interpreted as signed 64-bit integers. + *value = static_cast(strtoull(str, &endptr, base)); + } else { + *value = strtoll(str, &endptr, base); + } return ((errno == 0) && (endptr != str) && (*endptr == 0)); } diff --git a/runtime/vm/os_fuchsia.cc b/runtime/vm/os_fuchsia.cc index 94609b26a3c..becd0b8e617 100644 --- a/runtime/vm/os_fuchsia.cc +++ b/runtime/vm/os_fuchsia.cc @@ -233,7 +233,13 @@ bool OS::StringToInt64(const char* str, int64_t* value) { base = 16; } errno = 0; - *value = strtoll(str, &endptr, base); + if (FLAG_limit_ints_to_64_bits && (base == 16)) { + // Unsigned 64-bit hexadecimal integer literals are allowed but + // immediately interpreted as signed 64-bit integers. + *value = static_cast(strtoull(str, &endptr, base)); + } else { + *value = strtoll(str, &endptr, base); + } return ((errno == 0) && (endptr != str) && (*endptr == 0)); } diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc index 3722ff07760..e1b16216360 100644 --- a/runtime/vm/os_linux.cc +++ b/runtime/vm/os_linux.cc @@ -339,7 +339,13 @@ bool OS::StringToInt64(const char* str, int64_t* value) { base = 16; } errno = 0; - *value = strtoll(str, &endptr, base); + if (FLAG_limit_ints_to_64_bits && (base == 16)) { + // Unsigned 64-bit hexadecimal integer literals are allowed but + // immediately interpreted as signed 64-bit integers. + *value = static_cast(strtoull(str, &endptr, base)); + } else { + *value = strtoll(str, &endptr, base); + } return ((errno == 0) && (endptr != str) && (*endptr == 0)); } diff --git a/runtime/vm/os_macos.cc b/runtime/vm/os_macos.cc index 84c5b0dce63..740407e10a3 100644 --- a/runtime/vm/os_macos.cc +++ b/runtime/vm/os_macos.cc @@ -328,7 +328,13 @@ bool OS::StringToInt64(const char* str, int64_t* value) { base = 16; } errno = 0; - *value = strtoll(str, &endptr, base); + if (FLAG_limit_ints_to_64_bits && (base == 16)) { + // Unsigned 64-bit hexadecimal integer literals are allowed but + // immediately interpreted as signed 64-bit integers. + *value = static_cast(strtoull(str, &endptr, base)); + } else { + *value = strtoll(str, &endptr, base); + } return ((errno == 0) && (endptr != str) && (*endptr == 0)); } diff --git a/runtime/vm/os_win.cc b/runtime/vm/os_win.cc index 68f77f6e451..75c224521a2 100644 --- a/runtime/vm/os_win.cc +++ b/runtime/vm/os_win.cc @@ -355,7 +355,13 @@ bool OS::StringToInt64(const char* str, int64_t* value) { base = 16; } errno = 0; - *value = _strtoi64(str, &endptr, base); + if (FLAG_limit_ints_to_64_bits && (base == 16)) { + // Unsigned 64-bit hexadecimal integer literals are allowed but + // immediately interpreted as signed 64-bit integers. + *value = static_cast(_strtoui64(str, &endptr, base)); + } else { + *value = _strtoi64(str, &endptr, base); + } return ((errno == 0) && (endptr != str) && (*endptr == 0)); }