v8/test/torque/test-torque.tq

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

672 lines
16 KiB
Plaintext
Raw Normal View History

// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace test {
macro ElementsKindTestHelper1(kind: constexpr ElementsKind): bool {
if constexpr ((kind == UINT8_ELEMENTS) || (kind == UINT16_ELEMENTS)) {
return true;
} else {
return false;
}
}
macro ElementsKindTestHelper2(kind: constexpr ElementsKind): bool {
return ((kind == UINT8_ELEMENTS) || (kind == UINT16_ELEMENTS));
}
macro ElementsKindTestHelper3(kind: constexpr ElementsKind): constexpr bool {
return ((kind == UINT8_ELEMENTS) || (kind == UINT16_ELEMENTS));
}
macro LabelTestHelper1(): never
labels Label1 {
goto Label1;
}
macro LabelTestHelper2(): never
labels Label2(Smi) {
goto Label2(42);
}
macro LabelTestHelper3(): never
labels Label3(String, Smi) {
goto Label3('foo', 7);
}
macro TestConstexpr1() {
check(FromConstexpr<bool>(IsFastElementsKind(PACKED_SMI_ELEMENTS)));
}
macro TestConstexprIf() {
check(ElementsKindTestHelper1(UINT8_ELEMENTS));
check(ElementsKindTestHelper1(UINT16_ELEMENTS));
check(!ElementsKindTestHelper1(UINT32_ELEMENTS));
}
macro TestConstexprReturn() {
check(FromConstexpr<bool>(ElementsKindTestHelper3(UINT8_ELEMENTS)));
check(FromConstexpr<bool>(ElementsKindTestHelper3(UINT16_ELEMENTS)));
check(!FromConstexpr<bool>(ElementsKindTestHelper3(UINT32_ELEMENTS)));
check(FromConstexpr<bool>(!ElementsKindTestHelper3(UINT32_ELEMENTS)));
}
macro TestGotoLabel(): Boolean {
try {
LabelTestHelper1() otherwise Label1;
}
label Label1 {
return True;
}
}
macro TestGotoLabelWithOneParameter(): Boolean {
try {
LabelTestHelper2() otherwise Label2;
}
label Label2(smi: Smi) {
check(smi == 42);
return True;
}
}
macro TestGotoLabelWithTwoParameters(): Boolean {
try {
LabelTestHelper3() otherwise Label3;
}
label Label3(str: String, smi: Smi) {
check(str == 'foo');
check(smi == 7);
return True;
}
}
builtin GenericBuiltinTest<T: type>(c: Context, param: T): Object {
return Null;
}
GenericBuiltinTest<Object>(c: Context, param: Object): Object {
return param;
}
macro TestBuiltinSpecialization(c: Context) {
check(GenericBuiltinTest<Smi>(c, 0) == Null);
check(GenericBuiltinTest<Smi>(c, 1) == Null);
check(GenericBuiltinTest<Object>(c, Undefined) == Undefined);
check(GenericBuiltinTest<Object>(c, Undefined) == Undefined);
}
macro LabelTestHelper4(flag: constexpr bool): never
labels Label4, Label5 {
if constexpr (flag) {
goto Label4;
} else {
goto Label5;
}
}
macro CallLabelTestHelper4(flag: constexpr bool): bool {
try {
LabelTestHelper4(flag) otherwise Label4, Label5;
}
label Label4 {
return true;
}
label Label5 {
return false;
}
}
macro TestPartiallyUnusedLabel(): Boolean {
let r1: bool = CallLabelTestHelper4(true);
let r2: bool = CallLabelTestHelper4(false);
if (r1 && !r2) {
return True;
} else {
return False;
}
}
macro GenericMacroTest<T: type>(param: T): Object {
return Undefined;
}
GenericMacroTest<Object>(param2: Object): Object {
return param2;
}
macro GenericMacroTestWithLabels<T: type>(param: T): Object
labels X {
return Undefined;
}
GenericMacroTestWithLabels<Object>(param2: Object): Object
labels Y {
return Cast<Smi>(param2) otherwise Y;
}
macro TestMacroSpecialization() {
try {
const smi0: Smi = 0;
check(GenericMacroTest<Smi>(0) == Undefined);
check(GenericMacroTest<Smi>(1) == Undefined);
check(GenericMacroTest<Object>(Null) == Null);
check(GenericMacroTest<Object>(False) == False);
check(GenericMacroTest<Object>(True) == True);
check((GenericMacroTestWithLabels<Smi>(0) otherwise Fail) == Undefined);
check((GenericMacroTestWithLabels<Smi>(0) otherwise Fail) == Undefined);
check((GenericMacroTestWithLabels<Object>(smi0) otherwise Fail) == smi0);
try {
GenericMacroTestWithLabels<Object>(False) otherwise Expected;
}
label Expected {}
}
label Fail {
unreachable;
}
}
builtin TestHelperPlus1(context: Context, x: Smi): Smi {
return x + 1;
}
builtin TestHelperPlus2(context: Context, x: Smi): Smi {
return x + 2;
}
macro TestFunctionPointers(context: Context): Boolean {
let fptr: builtin(Context, Smi) => Smi = TestHelperPlus1;
check(fptr(context, 42) == 43);
fptr = TestHelperPlus2;
check(fptr(context, 42) == 44);
return True;
}
macro TestVariableRedeclaration(context: Context): Boolean {
let var1: int31 = FromConstexpr<bool>(42 == 0) ? 0 : 1;
let var2: int31 = FromConstexpr<bool>(42 == 0) ? 1 : 0;
return True;
}
macro TestTernaryOperator(x: Smi): Smi {
let b: bool = x < 0 ? true : false;
return b ? x - 10 : x + 100;
}
macro TestFunctionPointerToGeneric(c: Context) {
let fptr1: builtin(Context, Smi) => Object = GenericBuiltinTest<Smi>;
let fptr2: builtin(Context, Object) => Object = GenericBuiltinTest<Object>;
check(fptr1(c, 0) == Null);
check(fptr1(c, 1) == Null);
check(fptr2(c, Undefined) == Undefined);
check(fptr2(c, Undefined) == Undefined);
}
[torque] cleanup generics and scopes - Name lookup in module scopes has namespace semantics now: All overloads from all parent modules are combined before overload resolution. - Allow overloads of different callables: runtime-functions, macros, builtins, and generics. - The duplication between the DeclarationVisitor and the ImplementationVisitor is removed: The DeclarationVisitor creates declarables for everything except for implicit generic specializations. The ImplementationVisitor iterates over declarables. The DeclarationVisitor only looks at the header of declarations, not at the body. - Modules become Declarable's, which will enable them to be nested. - Modules replace the existing Scope chain mechanism, which will make it easier to inline macros. - The DeclarationVisitor and Declarations become stateless. All state is moved to contextual variables and the GlobalContext. - Implicit specializations are created directly from the ImplementationVisitor. This will enable template parameter inference. - As a consequence, the list of all builtins is only available after the ImplementationVisitor has run. Thus GenerateBuiltinDefinitions has to move to the ImplementationVisitor. Also, this makes it necessary to resolve the link from function pointer types to example builtins only at this point. Bug: v8:7793 Change-Id: I61cef2fd3e954ab148c252974344a6e38ee2d01d Reviewed-on: https://chromium-review.googlesource.com/c/1304294 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#57231}
2018-11-05 10:37:49 +00:00
type ObjectToObject = builtin(Context, Object) => Object;
macro TestTypeAlias(x: ObjectToObject): Code {
return x;
}
macro TestUnsafeCast(c: Context, n: Number): Boolean {
if (TaggedIsSmi(n)) {
let m: Smi = UnsafeCast<Smi>(n);
check(TestHelperPlus1(c, m) == 11);
return True;
}
return False;
}
macro TestHexLiteral() {
check(Convert<intptr>(0xffff) + 1 == 0x10000);
check(Convert<intptr>(-0xffff) == -65535);
}
macro TestLargeIntegerLiterals(c: Context) {
let x: int32 = 0x40000000;
let y: int32 = 0x7fffffff;
}
macro TestMultilineAssert() {
let someVeryLongVariableNameThatWillCauseLineBreaks: Smi = 5;
check(
someVeryLongVariableNameThatWillCauseLineBreaks > 0 &&
someVeryLongVariableNameThatWillCauseLineBreaks < 10);
}
macro TestNewlineInString() {
Print('Hello, World!\n');
}
const kConstexprConst: constexpr int31 = 5;
const kIntptrConst: intptr = 4;
const kSmiConst: Smi = 3;
macro TestModuleConstBindings() {
check(kConstexprConst == Int32Constant(5));
check(kIntptrConst == 4);
check(kSmiConst == 3);
}
macro TestLocalConstBindings() {
const x: constexpr int31 = 3;
const xSmi: Smi = x;
{
const x: Smi = x + FromConstexpr<Smi>(1);
check(x == xSmi + 1);
const xSmi: Smi = x;
check(x == xSmi);
check(x == 4);
}
check(xSmi == 3);
check(x == xSmi);
}
struct TestStructA {
indexes: FixedArray;
i: Smi;
k: Number;
}
struct TestStructB {
x: TestStructA;
y: Smi;
}
macro TestStruct1(i: TestStructA): Smi {
return i.i;
}
macro TestStruct2(): TestStructA {
return TestStructA{UnsafeCast<FixedArray>(kEmptyFixedArray), 27, 31};
}
macro TestStruct3(): TestStructA {
let a: TestStructA =
TestStructA{UnsafeCast<FixedArray>(kEmptyFixedArray), 13, 5};
let b: TestStructA = a;
let c: TestStructA = TestStruct2();
a.i = TestStruct1(c);
a.k = a.i;
let d: TestStructB;
d.x = a;
d = TestStructB{a, 7};
let e: TestStructA = d.x;
let f: Smi =
TestStructA{UnsafeCast<FixedArray>(kEmptyFixedArray), 27, 31}.i;
f = TestStruct2().i;
return a;
}
struct TestStructC {
x: TestStructA;
y: TestStructA;
}
macro TestStruct4(): TestStructC {
return TestStructC{TestStruct2(), TestStruct2()};
}
// This macro tests different versions of the for-loop where some parts
// are (not) present.
macro TestForLoop() {
let sum: Smi = 0;
for (let i: Smi = 0; i < 5; ++i) sum += i;
check(sum == 10);
sum = 0;
let j: Smi = 0;
for (; j < 5; ++j) sum += j;
check(sum == 10);
sum = 0;
j = 0;
for (; j < 5;) sum += j++;
check(sum == 10);
// Check that break works. No test expression.
sum = 0;
for (let i: Smi = 0;; ++i) {
if (i == 5) break;
sum += i;
}
check(sum == 10);
sum = 0;
j = 0;
for (;;) {
if (j == 5) break;
sum += j;
j++;
}
check(sum == 10);
// The following tests are the same as above, but use continue to skip
// index 3.
sum = 0;
for (let i: Smi = 0; i < 5; ++i) {
if (i == 3) continue;
sum += i;
}
check(sum == 7);
sum = 0;
j = 0;
for (; j < 5; ++j) {
if (j == 3) continue;
sum += j;
}
check(sum == 7);
sum = 0;
j = 0;
for (; j < 5;) {
if (j == 3) {
j++;
continue;
}
sum += j;
j++;
}
check(sum == 7);
sum = 0;
for (let i: Smi = 0;; ++i) {
if (i == 3) continue;
if (i == 5) break;
sum += i;
}
check(sum == 7);
sum = 0;
j = 0;
for (;;) {
if (j == 3) {
j++;
continue;
}
if (j == 5) break;
sum += j;
j++;
}
check(sum == 7);
j = 0;
try {
for (;;) {
if (++j == 10) goto Exit;
}
}
label Exit {
check(j == 10);
}
}
macro TestSubtyping(x: Smi) {
const foo: Object = x;
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
}
macro IncrementIfSmi<A: type>(x: A): A {
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
typeswitch (x) {
case (x: Smi): {
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
return x + 1;
}
case (o: A): {
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
return o;
}
}
}
macro TypeswitchExample(x: Number | FixedArray): int32 {
let result: int32 = 0;
typeswitch (IncrementIfSmi(x)) {
case (x: FixedArray): {
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
result = result + 1;
}
case (Number): {
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
result = result + 2;
}
}
result = result * 10;
typeswitch (IncrementIfSmi(x)) {
case (x: Smi): {
result = result + Convert<int32>(x);
}
case (a: FixedArray): {
result = result + Convert<int32>(a.length);
}
case (x: HeapNumber): {
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
result = result + 7;
}
}
return result;
}
macro TestTypeswitch() {
check(TypeswitchExample(FromConstexpr<Smi>(5)) == 26);
const a: FixedArray = AllocateZeroedFixedArray(3);
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
check(TypeswitchExample(a) == 13);
check(TypeswitchExample(FromConstexpr<Number>(0.5)) == 27);
[torque] add typeswitch statement This adds a typeswitch statement typeswitch (e) case (x1 : Type1) { ... } case (x2 : Type2) { } ... ... case (xn : TypeN) { ... } This checks to which of the given types the result of evaluating e can be cast, in the order in which they are listed. So if an earlier type matches, a value of this type won't reach a later case. The type-checks are performed by calling the cast<T>() macro. The type of the argument passed to the cast macro is dependent on the case and excludes all types checked earlier. For example, in const x : Object = ... typeswitch (x) case (x : Smi) { ... } case (x : HeapNumber) { ... } case (x : HeapObject) { ... } there will be calls to cast<Smi>(Object) and cast<HeapNumber>(HeapObject), because after the Smi check we know that x has to be a HeapObject. With the refactored base.tq definition of cast, this will generate efficient code and avoid repeating the Smi check in the second case. The type system ensures that all cases are reachable and that the type given to the last case is safe without a runtime check (in other words, the union of all checked types covers the type of e). The cases can also be written as case (Type) { ... } , in which case the switched value is not re-bound with the checked type. Bug: v8:7793 Change-Id: Iea4aed7465d62b445e3ae0d33f52921912e095e3 Reviewed-on: https://chromium-review.googlesource.com/1156506 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Cr-Commit-Position: refs/heads/master@{#54958}
2018-08-07 21:57:19 +00:00
}
macro ExampleGenericOverload<A: type>(o: Object): A {
return o;
}
macro ExampleGenericOverload<A: type>(o: Smi): A {
return o + 1;
}
macro TestGenericOverload() {
const xSmi: Smi = 5;
const xObject: Object = xSmi;
check(ExampleGenericOverload<Smi>(xSmi) == 6);
check(UnsafeCast<Smi>(ExampleGenericOverload<Object>(xObject)) == 5);
}
macro BoolToBranch(x: bool): never
labels Taken, NotTaken {
if (x) {
goto Taken;
} else {
goto NotTaken;
}
}
macro TestOrAnd1(x: bool, y: bool, z: bool): bool {
return BoolToBranch(x) || y && z ? true : false;
}
macro TestOrAnd2(x: bool, y: bool, z: bool): bool {
return x || BoolToBranch(y) && z ? true : false;
}
macro TestOrAnd3(x: bool, y: bool, z: bool): bool {
return x || y && BoolToBranch(z) ? true : false;
}
macro TestAndOr1(x: bool, y: bool, z: bool): bool {
return BoolToBranch(x) && y || z ? true : false;
}
macro TestAndOr2(x: bool, y: bool, z: bool): bool {
return x && BoolToBranch(y) || z ? true : false;
}
macro TestAndOr3(x: bool, y: bool, z: bool): bool {
return x && y || BoolToBranch(z) ? true : false;
}
macro TestLogicalOperators() {
check(TestAndOr1(true, true, true));
check(TestAndOr2(true, true, true));
check(TestAndOr3(true, true, true));
check(TestAndOr1(true, true, false));
check(TestAndOr2(true, true, false));
check(TestAndOr3(true, true, false));
check(TestAndOr1(true, false, true));
check(TestAndOr2(true, false, true));
check(TestAndOr3(true, false, true));
check(!TestAndOr1(true, false, false));
check(!TestAndOr2(true, false, false));
check(!TestAndOr3(true, false, false));
check(TestAndOr1(false, true, true));
check(TestAndOr2(false, true, true));
check(TestAndOr3(false, true, true));
check(!TestAndOr1(false, true, false));
check(!TestAndOr2(false, true, false));
check(!TestAndOr3(false, true, false));
check(TestAndOr1(false, false, true));
check(TestAndOr2(false, false, true));
check(TestAndOr3(false, false, true));
check(!TestAndOr1(false, false, false));
check(!TestAndOr2(false, false, false));
check(!TestAndOr3(false, false, false));
check(TestOrAnd1(true, true, true));
check(TestOrAnd2(true, true, true));
check(TestOrAnd3(true, true, true));
check(TestOrAnd1(true, true, false));
check(TestOrAnd2(true, true, false));
check(TestOrAnd3(true, true, false));
check(TestOrAnd1(true, false, true));
check(TestOrAnd2(true, false, true));
check(TestOrAnd3(true, false, true));
check(TestOrAnd1(true, false, false));
check(TestOrAnd2(true, false, false));
check(TestOrAnd3(true, false, false));
check(TestOrAnd1(false, true, true));
check(TestOrAnd2(false, true, true));
check(TestOrAnd3(false, true, true));
check(!TestOrAnd1(false, true, false));
check(!TestOrAnd2(false, true, false));
check(!TestOrAnd3(false, true, false));
check(!TestOrAnd1(false, false, true));
check(!TestOrAnd2(false, false, true));
check(!TestOrAnd3(false, false, true));
check(!TestOrAnd1(false, false, false));
check(!TestOrAnd2(false, false, false));
check(!TestOrAnd3(false, false, false));
}
macro TestCall(i: Smi): Smi
labels A {
if (i < 5) return i;
goto A;
}
macro TestOtherwiseWithCode1() {
let v: Smi = 0;
let s: Smi = 1;
try {
TestCall(10) otherwise goto B(++s);
}
label B(v1: Smi) {
v = v1;
}
assert(v == 2);
}
macro TestOtherwiseWithCode2() {
let s: Smi = 0;
for (let i: Smi = 0; i < 10; ++i) {
TestCall(i) otherwise break;
++s;
}
assert(s == 5);
}
macro TestOtherwiseWithCode3() {
let s: Smi = 0;
for (let i: Smi = 0; i < 10; ++i) {
s += TestCall(i) otherwise break;
}
assert(s == 10);
}
macro TestForwardLabel() {
try {
goto A;
}
label A {
goto B(5);
}
label B(b: Smi) {
assert(b == 5);
}
}
macro TestQualifiedAccess() {
let s: Smi = 0;
check(!array::IsJSArray(s));
}
macro TestCatch1(context: Context): Smi {
let r: Smi = 0;
try {
ThrowTypeError(context, kInvalidArrayLength);
} catch (e) {
r = 1;
return r;
}
}
macro TestCatch2Wrapper(context: Context): never {
ThrowTypeError(context, kInvalidArrayLength);
}
macro TestCatch2(context: Context): Smi {
let r: Smi = 0;
try {
TestCatch2Wrapper(context);
} catch (e) {
r = 2;
return r;
}
}
macro TestCatch3WrapperWithLabel(context: Context): never
labels Abort {
ThrowTypeError(context, kInvalidArrayLength);
}
macro TestCatch3(context: Context): Smi {
let r: Smi = 0;
try {
TestCatch3WrapperWithLabel(context) otherwise Abort;
}
label Abort {
return -1;
}
catch (e) {
r = 2;
return r;
}
}
// This test doesn't actually test the functionality of iterators,
// it's only purpose is to make sure tha the CSA macros in the
// IteratorBuiltinsAssembler match the signatures provided in
// iterator.tq.
macro TestIterator(implicit context: Context)(o: Object, map: Map) {
try {
const t1: Object = iterator::GetIteratorMethod(o);
const t2: iterator::IteratorRecord = iterator::GetIterator(o);
const t3: Object = iterator::IteratorStep(t2) otherwise Fail;
const t4: Object = iterator::IteratorStep(t2, map) otherwise Fail;
const t5: Object = iterator::IteratorValue(t4);
const t6: Object = iterator::IteratorValue(t4, map);
const t7: JSArray = iterator::IterableToList(t1, t1);
iterator::IteratorCloseOnException(t2, t5);
}
label Fail {}
}
}