blob: 457bc7c9fa226d606ea20352f6ba214e0203093e [file] [log] [blame]
// Copyright (c) 2019, 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.
// SharedOptions=--enable-experiment=triple-shift
import "package:expect/expect.dart";
// The >>> operator is (again) supported by Dart, and used on `int`.
// This test assumes that the JS implementation of `>>>` uses the JS `>>>`
// operator directly (that is, convert the value to Uint32, shift right,)
main() {
testIntegerShifts();
testNonDoubleShifts();
testConstantShifts();
}
void testIntegerShifts() {
for (int i = -1; i <= 65; i++) {
testShift(0, i);
testShift(1, i);
testShift(2, i);
testShift(3, i);
testShift(-1, i);
testShift(-5, i);
// . . .
testShift(0x7fffffff, i);
testShift(0x55555555, i);
testShift(0xaaaaaaaa, i);
testShift(0x80000000, i);
// . . . . .
testShift(0x7fffffffffffffff, i);
testShift(0xffffffffffffffff, i);
}
// JavaScript numbers may consider Infinity as an integer.
// If so, it is zero when converted to a fixed precision.
if (double.infinity is int) {
int number = (double.infinity as int);
Expect.equals(0, number >> 1);
Expect.throws(() => 1 >>> number); // infinity > 64.
}
}
void testNonDoubleShifts() {
double n = 0.0;
n >>> 1; //# 01: compile-time error
for (dynamic number in [0.0, 1.0, 2.4, -2.4, double.infinity, double.nan]) {
if (number is! int) {
Expect.throws(() => number >>> 1);
Expect.throws(() => 1 >>> number);
}
}
}
int testConstantShifts() {
const c = C();
// >>> is a constant operation on integers.
const c1 = 2 >>> 1;
const c2 = (1 >>> 0) >>> 0;
// >>> is a potentially constant operation independent of type.
// The type must still type-check.
const c3 = false ? 1 : c >>> c;
// It's an error if it doesn't type-check.
const c4 = true || c >>> c; //# 02: compile-time error
const c5 = true || "string" >>> 1; //# 03: compile-time error
// Or if the shift isn't on integers and it is evaluated.
const c6 = c >>> c; //# 04: compile-time error
// Or if shifting throws
const c7 = 1 >>> -1; //# 05: compile-time error
const c8 = 1 >>> 65; //# 06: compile-time error
Expect.isNotNull(c1 + c2 + c3); // Avoid "unused variable" warnings.
}
const bool isJSBitOps = (-1 | 0) > 0;
const String jsFlag = isJSBitOps ? " (JS)" : "";
void testShift(int value, int shift) {
var title = "0x${value.toRadixString(16)} >>> $shift$jsFlag";
if (shift < 0) {
// No platform allows shifting a negative.
Expect.throws(() => value >>> shift, "$title: shift < 0");
return;
}
if (!isJSBitOps && shift > 64) {
// Native 64-bit integers do not allow shifts above 64.
Expect.throws(() => value >>> shift, "$title: shift > 64");
return;
}
var expected;
if (isJSBitOps) {
// TODO: Check that this is the desired behavior for JS >>>.
expected = value.toUnsigned(32) >> shift;
} else if (value < 0) {
if (shift > 0) {
expected = (value >> shift).toUnsigned(64 - shift);
} else {
expected = value;
}
} else {
expected = value >> shift;
}
Expect.equals(expected, value >>> shift, title);
}
class C {
const C();
C operator >>>(C other) => other;
}