// 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(Oddball, Smi) { goto Label3(Null, 7); } macro TestConstexpr1() { check(FromConstexpr(IsFastElementsKind(PACKED_SMI_ELEMENTS))); } macro TestConstexprIf() { check(ElementsKindTestHelper1(UINT8_ELEMENTS)); check(ElementsKindTestHelper1(UINT16_ELEMENTS)); check(!ElementsKindTestHelper1(UINT32_ELEMENTS)); } macro TestConstexprReturn() { check(FromConstexpr(ElementsKindTestHelper3(UINT8_ELEMENTS))); check(FromConstexpr(ElementsKindTestHelper3(UINT16_ELEMENTS))); check(!FromConstexpr(ElementsKindTestHelper3(UINT32_ELEMENTS))); check(FromConstexpr(!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(o: Oddball, smi: Smi) { check(o == Null); check(smi == 7); return True; } } builtin GenericBuiltinTest(c: Context, param: T): Object { return Null; } GenericBuiltinTest(c: Context, param: Object): Object { return param; } macro TestBuiltinSpecialization(c: Context) { check(GenericBuiltinTest(c, 0) == Null); check(GenericBuiltinTest(c, 1) == Null); check(GenericBuiltinTest(c, Undefined) == Undefined); check(GenericBuiltinTest(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(param: T): Object { return Undefined; } GenericMacroTest(param2: Object): Object { return param2; } macro GenericMacroTestWithLabels(param: T): Object labels X { return Undefined; } GenericMacroTestWithLabels(param2: Object): Object labels Y { return Cast(param2) otherwise Y; } macro TestMacroSpecialization() { try { const smi0: Smi = 0; check(GenericMacroTest(0) == Undefined); check(GenericMacroTest(1) == Undefined); check(GenericMacroTest(Null) == Null); check(GenericMacroTest(False) == False); check(GenericMacroTest(True) == True); check((GenericMacroTestWithLabels(0) otherwise Fail) == Undefined); check((GenericMacroTestWithLabels(0) otherwise Fail) == Undefined); try { GenericMacroTestWithLabels(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(implicit 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(implicit context: Context)(): Boolean { let var1: int31 = FromConstexpr(42 == 0) ? 0 : 1; let var2: int31 = FromConstexpr(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; let fptr2: builtin(Context, Object) => Object = GenericBuiltinTest; check(fptr1(c, 0) == Null); check(fptr1(c, 1) == Null); check(fptr2(c, Undefined) == Undefined); check(fptr2(c, Undefined) == Undefined); } type ObjectToObject = builtin(Context, Object) => Object; macro TestTypeAlias(x: ObjectToObject): BuiltinPtr { return x; } macro TestUnsafeCast(implicit context: Context)(n: Number): Boolean { if (TaggedIsSmi(n)) { let m: Smi = UnsafeCast(n); check(TestHelperPlus1(context, m) == 11); return True; } return False; } macro TestHexLiteral() { check(Convert(0xffff) + 1 == 0x10000); check(Convert(-0xffff) == -65535); } macro TestLargeIntegerLiterals(implicit 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(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(implicit context: Context)(): TestStructA { return TestStructA{ indexes: UnsafeCast(kEmptyFixedArray), i: 27, k: 31 }; } macro TestStruct3(implicit context: Context)(): TestStructA { let a: TestStructA = TestStructA{indexes: UnsafeCast(kEmptyFixedArray), i: 13, k: 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{x: a, y: 7}; let e: TestStructA = d.x; let f: Smi = TestStructA{ indexes: UnsafeCast(kEmptyFixedArray), i: 27, k: 31 }.i; f = TestStruct2().i; return a; } struct TestStructC { x: TestStructA; y: TestStructA; } macro TestStruct4(implicit context: Context)(): TestStructC { return TestStructC{x: TestStruct2(), y: TestStruct2()}; } macro TestStructInLabel(implicit context: Context)(): never labels Foo(TestStructA) { goto Foo(TestStruct2()); } macro CallTestStructInLabel(implicit context: Context)() { try { TestStructInLabel() otherwise Foo; } label Foo(s: TestStructA) {} } // 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); } // Test if we can handle uninitialized values on the stack. let i: Smi; for (let j: Smi = 0; j < 10; ++j) { } } macro TestSubtyping(x: Smi) { const foo: Object = x; } macro IncrementIfSmi(x: A): A { typeswitch (x) { case (x: Smi): { return x + 1; } case (o: A): { return o; } } } type NumberOrFixedArray = Number | FixedArray; macro TypeswitchExample(implicit context: Context)(x: NumberOrFixedArray): int32 { let result: int32 = 0; typeswitch (IncrementIfSmi(x)) { case (x: FixedArray): { result = result + 1; } case (Number): { result = result + 2; } } result = result * 10; typeswitch (IncrementIfSmi(x)) { case (x: Smi): { result = result + Convert(x); } case (a: FixedArray): { result = result + Convert(a.length); } case (x: HeapNumber): { result = result + 7; } } return result; } macro TestTypeswitch(implicit context: Context)() { check(TypeswitchExample(FromConstexpr(5)) == 26); const a: FixedArray = AllocateZeroedFixedArray(3); check(TypeswitchExample(a) == 13); check(TypeswitchExample(FromConstexpr(0.5)) == 27); } macro TestTypeswitchAsanLsanFailure(implicit context: Context)(obj: Object) { typeswitch (obj) { case (o: Smi): { } case (o: JSTypedArray): { } case (o: JSReceiver): { } case (o: HeapObject): { } } } macro ExampleGenericOverload(o: Object): A { return o; } macro ExampleGenericOverload(o: Smi): A { return o + 1; } macro TestGenericOverload(implicit context: Context)() { const xSmi: Smi = 5; const xObject: Object = xSmi; check(ExampleGenericOverload(xSmi) == 6); check(UnsafeCast(ExampleGenericOverload(xObject)) == 5); } macro TestEquality(implicit context: Context)() { const notEqual: bool = AllocateHeapNumberWithValue(0.5) != AllocateHeapNumberWithValue(0.5); check(!notEqual); const equal: bool = AllocateHeapNumberWithValue(0.5) == AllocateHeapNumberWithValue(0.5); check(equal); } 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(implicit context: Context)() { let s: Smi = 0; check(!array::IsJSArray(s)); } macro TestCatch1(implicit context: Context)(): Smi { let r: Smi = 0; try { ThrowTypeError(kInvalidArrayLength); } catch (e) { r = 1; return r; } } macro TestCatch2Wrapper(implicit context: Context)(): never { ThrowTypeError(kInvalidArrayLength); } macro TestCatch2(implicit context: Context)(): Smi { let r: Smi = 0; try { TestCatch2Wrapper(); } catch (e) { r = 2; return r; } } macro TestCatch3WrapperWithLabel(implicit context: Context)(): never labels Abort { ThrowTypeError(kInvalidArrayLength); } macro TestCatch3(implicit context: Context)(): Smi { let r: Smi = 0; try { TestCatch3WrapperWithLabel() 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 {} } macro TestFrame1(implicit context: Context)() { const f: Frame = LoadFramePointer(); const frameType: FrameType = Cast(f.context_or_frame_type) otherwise unreachable; assert(frameType == STUB_FRAME); assert(f.caller == LoadParentFramePointer()); typeswitch (f) { case (f: StandardFrame): { unreachable; } case (f: ArgumentsAdaptorFrame): { unreachable; } case (f: StubFrame): { } } } macro TestNew(implicit context: Context)() { const f: JSArray = NewJSArray(); assert(f.IsEmpty()); f.length = 0; } struct TestInner { SetX(newValue: int32) { this.x = newValue; } GetX(): int32 { return this.x; } x: int32; y: int32; } struct TestOuter { a: int32; b: TestInner; c: int32; } macro TestStructConstructor(implicit context: Context)() { // Test default constructor let a: TestOuter = TestOuter{a: 5, b: TestInner{x: 6, y: 7}, c: 8}; assert(a.a == 5); assert(a.b.x == 6); assert(a.b.y == 7); assert(a.c == 8); a.b.x = 1; assert(a.b.x == 1); a.b.SetX(2); assert(a.b.x == 2); assert(a.b.GetX() == 2); } @noVerifier extern class TestClassWithAllTypes extends JSObject { a: int8; b: uint8; b2: uint8; b3: uint8; c: int16; d: uint16; e: int32; f: uint32; g: RawPtr; h: intptr; i: uintptr; } // This class should throw alignment errors if @ifdef decorators aren't // working. @noVerifier extern class PreprocessingTest extends JSObject { @ifdef(FALSE_FOR_TESTING) a: int8; @ifdef(TRUE_FOR_TESTING) a: int16; b: int16; d: int32; } macro TestClassWithAllTypesLoadsAndStores( t: TestClassWithAllTypes, r: RawPtr, v1: int8, v2: uint8, v3: int16, v4: uint16) { t.a = v1; t.b = v2; t.c = v3; t.d = v4; t.e = 0; t.f = 0; t.g = r; t.h = 0; t.i = 0; t.a = t.a; t.b = t.b; t.c = t.c; t.d = t.d; t.e = t.e; t.f = t.f; t.g = t.g; t.h = t.h; t.i = t.i; } class InternalClass { Flip() labels NotASmi { const tmp = Cast(this.b) otherwise NotASmi; this.b = this.a; this.a = tmp; } a: Smi; b: Number; } macro NewInternalClass(x: Smi): InternalClass { return new InternalClass{a: x, b: x + 1}; } macro TestInternalClass(implicit context: Context)() { const o = NewInternalClass(5); o.Flip() otherwise unreachable; check(o.a == 6); check(o.b == 5); } struct StructWithConst { TestMethod1(): int32 { return this.b; } TestMethod2(): Object { return this.a; } a: Object; const b: int32; } macro TestConstInStructs() { const x = StructWithConst{a: Null, b: 1}; let y = StructWithConst{a: Null, b: 1}; y.a = Undefined; const copy = x; } struct TestIterator { Next(): Object labels NoMore { if (this.count-- == 0) goto NoMore; return Hole; } count: Smi; } macro TestNewFixedArrayFromSpread(implicit context: Context)(): Object { const i = TestIterator{count: 5}; return new FixedArray{map: kFixedArrayMap, length: 5, objects: ...i}; } class SmiPair { GetA():&Smi { return & this.a; } a: Smi; b: Smi; } macro Swap(a:&T, b:&T) { const tmp = * a; * a = * b; * b = tmp; } macro TestReferences() { const array = new SmiPair{a: 7, b: 2}; const ref:&Smi = & array.a; * ref = 3 + * ref; -- * ref; Swap(& array.b, array.GetA()); check(array.a == 2); check(array.b == 9); } type Baztype = Foo | FooType; @noVerifier extern class Foo extends JSObject { fooField: FooType; } @noVerifier extern class Bar extends Foo { barField: Bartype; bazfield: Baztype; } type Bartype = FooType; type FooType = Smi | Bar; }