// 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. // Test line comment /* Test mulitline comment */ /*multiline_without_whitespace*/ namespace test { macro ElementsKindTestHelper1(kind: constexpr ElementsKind): bool { if constexpr ( kind == ElementsKind::UINT8_ELEMENTS || kind == ElementsKind::UINT16_ELEMENTS) { return true; } else { return false; } } macro ElementsKindTestHelper2(kind: constexpr ElementsKind): constexpr bool { return kind == ElementsKind::UINT8_ELEMENTS || kind == ElementsKind::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); } @export macro TestConstexpr1(): void { check(FromConstexpr( IsFastElementsKind(ElementsKind::PACKED_SMI_ELEMENTS))); } @export macro TestConstexprIf(): void { check(ElementsKindTestHelper1(ElementsKind::UINT8_ELEMENTS)); check(ElementsKindTestHelper1(ElementsKind::UINT16_ELEMENTS)); check(!ElementsKindTestHelper1(ElementsKind::UINT32_ELEMENTS)); } @export macro TestConstexprReturn(): void { check(FromConstexpr( ElementsKindTestHelper2(ElementsKind::UINT8_ELEMENTS))); check(FromConstexpr( ElementsKindTestHelper2(ElementsKind::UINT16_ELEMENTS))); check(!FromConstexpr( ElementsKindTestHelper2(ElementsKind::UINT32_ELEMENTS))); check(FromConstexpr( !ElementsKindTestHelper2(ElementsKind::UINT32_ELEMENTS))); } @export macro TestGotoLabel(): Boolean { try { LabelTestHelper1() otherwise Label1; } label Label1 { return True; } } @export macro TestGotoLabelWithOneParameter(): Boolean { try { LabelTestHelper2() otherwise Label2; } label Label2(smi: Smi) { check(smi == 42); return True; } } @export macro TestGotoLabelWithTwoParameters(): Boolean { try { LabelTestHelper3() otherwise Label3; } label Label3(o: Oddball, smi: Smi) { check(o == Null); check(smi == 7); return True; } } builtin GenericBuiltinTest(_param: T): JSAny { return Null; } GenericBuiltinTest(param: JSAny): JSAny { return param; } @export macro TestBuiltinSpecialization(): void { check(GenericBuiltinTest(0) == Null); check(GenericBuiltinTest(1) == Null); check(GenericBuiltinTest(Undefined) == Undefined); check(GenericBuiltinTest(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; } } @export macro TestPartiallyUnusedLabel(): Boolean { const r1: bool = CallLabelTestHelper4(true); const 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; } @export macro TestMacroSpecialization(): void { 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(x: Smi): Smi { return x + 1; } builtin TestHelperPlus2(x: Smi): Smi { return x + 2; } @export macro TestFunctionPointers(implicit context: Context)(): Boolean { let fptr: builtin(Smi) => Smi = TestHelperPlus1; check(fptr(42) == 43); fptr = TestHelperPlus2; check(fptr(42) == 44); return True; } @export macro TestVariableRedeclaration(implicit context: Context)(): Boolean { let _var1: int31 = FromConstexpr(42 == 0) ? 0 : 1; let _var2: int31 = FromConstexpr(42 == 0) ? 1 : 0; return True; } @export macro TestTernaryOperator(x: Smi): Smi { const b: bool = x < 0 ? true : false; return b ? x - 10 : x + 100; } @export macro TestFunctionPointerToGeneric(): void { const fptr1: builtin(Smi) => JSAny = GenericBuiltinTest; const fptr2: builtin(JSAny) => JSAny = GenericBuiltinTest; check(fptr1(0) == Null); check(fptr1(1) == Null); check(fptr2(Undefined) == Undefined); check(fptr2(Undefined) == Undefined); } type ObjectToObject = builtin(Context, JSAny) => JSAny; @export macro TestTypeAlias(x: ObjectToObject): BuiltinPtr { return x; } @export macro TestUnsafeCast(implicit context: Context)(n: Number): Boolean { if (TaggedIsSmi(n)) { const m: Smi = UnsafeCast(n); check(TestHelperPlus1(m) == 11); return True; } return False; } @export macro TestHexLiteral(): void { check(Convert(0xffff) + 1 == 0x10000); check(Convert(-0xffff) == -65535); } @export macro TestLargeIntegerLiterals(implicit c: Context)(): void { let _x: int32 = 0x40000000; let _y: int32 = 0x7fffffff; } @export macro TestMultilineAssert(): void { const someVeryLongVariableNameThatWillCauseLineBreaks: Smi = 5; check( someVeryLongVariableNameThatWillCauseLineBreaks > 0 && someVeryLongVariableNameThatWillCauseLineBreaks < 10); } @export macro TestNewlineInString(): void { Print('Hello, World!\n'); } const kConstexprConst: constexpr int31 = 5; const kIntptrConst: intptr = 4; const kSmiConst: Smi = 3; @export macro TestModuleConstBindings(): void { check(kConstexprConst == Int32Constant(5)); check(kIntptrConst == 4); check(kSmiConst == 3); } @export macro TestLocalConstBindings(): void { 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; } @export macro TestStruct1(i: TestStructA): Smi { return i.i; } @export macro TestStruct2(implicit context: Context)(): TestStructA { return TestStructA{ indexes: UnsafeCast(kEmptyFixedArray), i: 27, k: 31 }; } @export macro TestStruct3(implicit context: Context)(): TestStructA { let a: TestStructA = TestStructA{indexes: UnsafeCast(kEmptyFixedArray), i: 13, k: 5}; let _b: TestStructA = a; const 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; } @export macro TestStruct4(implicit context: Context)(): TestStructC { return TestStructC{x: TestStruct2(), y: TestStruct2()}; } macro TestStructInLabel(implicit context: Context)(): never labels Foo(TestStructA) { goto Foo(TestStruct2()); } @export // Silence unused warning. macro CallTestStructInLabel(implicit context: Context)(): void { try { TestStructInLabel() otherwise Foo; } label Foo(_s: TestStructA) {} } // This macro tests different versions of the for-loop where some parts // are (not) present. @export macro TestForLoop(): void { 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) { } } @export macro TestSubtyping(x: Smi): void { const _foo: JSAny = 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; } @export macro TestTypeswitch(implicit context: Context)(): void { check(TypeswitchExample(FromConstexpr(5)) == 26); const a: FixedArray = AllocateZeroedFixedArray(3); check(TypeswitchExample(a) == 13); check(TypeswitchExample(FromConstexpr(0.5)) == 27); } @export macro TestTypeswitchAsanLsanFailure(implicit context: Context)(obj: Object): void { 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; } @export macro TestGenericOverload(implicit context: Context)(): void { const xSmi: Smi = 5; const xObject: Object = xSmi; check(ExampleGenericOverload(xSmi) == 6); check(UnsafeCast(ExampleGenericOverload(xObject)) == 5); } @export macro TestEquality(implicit context: Context)(): void { const notEqual: bool = AllocateHeapNumberWithValue(0.5) != AllocateHeapNumberWithValue(0.5); check(!notEqual); const equal: bool = AllocateHeapNumberWithValue(0.5) == AllocateHeapNumberWithValue(0.5); check(equal); } @export macro TestOrAnd(x: bool, y: bool, z: bool): bool { return x || y && z ? true : false; } @export macro TestAndOr(x: bool, y: bool, z: bool): bool { return x && y || z ? true : false; } @export macro TestLogicalOperators(): void { check(TestAndOr(true, true, true)); check(TestAndOr(true, true, false)); check(TestAndOr(true, false, true)); check(!TestAndOr(true, false, false)); check(TestAndOr(false, true, true)); check(!TestAndOr(false, true, false)); check(TestAndOr(false, false, true)); check(!TestAndOr(false, false, false)); check(TestOrAnd(true, true, true)); check(TestOrAnd(true, true, false)); check(TestOrAnd(true, false, true)); check(TestOrAnd(true, false, false)); check(TestOrAnd(false, true, true)); check(!TestOrAnd(false, true, false)); check(!TestOrAnd(false, false, true)); check(!TestOrAnd(false, false, false)); } @export macro TestCall(i: Smi): Smi labels A { if (i < 5) return i; goto A; } @export macro TestOtherwiseWithCode1(): void { let v: Smi = 0; let s: Smi = 1; try { TestCall(10) otherwise goto B(++s); } label B(v1: Smi) { v = v1; } dcheck(v == 2); } @export macro TestOtherwiseWithCode2(): void { let s: Smi = 0; for (let i: Smi = 0; i < 10; ++i) { TestCall(i) otherwise break; ++s; } dcheck(s == 5); } @export macro TestOtherwiseWithCode3(): void { let s: Smi = 0; for (let i: Smi = 0; i < 10; ++i) { s += TestCall(i) otherwise break; } dcheck(s == 10); } @export macro TestForwardLabel(): void { try { goto A; } label A { goto B(5); } label B(b: Smi) { dcheck(b == 5); } } @export macro TestQualifiedAccess(implicit context: Context)(): void { const s: Smi = 0; check(!Is(s)); } @export macro TestCatch1(implicit context: Context)(): Smi { let r: Smi = 0; try { ThrowTypeError(MessageTemplate::kInvalidArrayLength); } catch (_e) { r = 1; return r; } } @export macro TestCatch2Wrapper(implicit context: Context)(): never { ThrowTypeError(MessageTemplate::kInvalidArrayLength); } @export macro TestCatch2(implicit context: Context)(): Smi { let r: Smi = 0; try { TestCatch2Wrapper(); } catch (_e) { r = 2; return r; } } @export macro TestCatch3WrapperWithLabel(implicit context: Context)(): never labels _Abort { ThrowTypeError(MessageTemplate::kInvalidArrayLength); } @export macro TestCatch3(implicit context: Context)(): Smi { let r: Smi = 0; try { TestCatch3WrapperWithLabel() otherwise Abort; } catch (_e) { r = 2; return r; } label Abort { return -1; } } // 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. @export transitioning macro TestIterator(implicit context: Context)( o: JSReceiver, map: Map): void { try { const t1: JSAny = iterator::GetIteratorMethod(o); const t2: iterator::IteratorRecord = iterator::GetIterator(o); const _t3: JSAny = iterator::IteratorStep(t2) otherwise Fail; const _t4: JSAny = iterator::IteratorStep(t2, map) otherwise Fail; const _t5: JSAny = iterator::IteratorValue(o); const _t6: JSAny = iterator::IteratorValue(o, map); const _t7: JSArray = iterator::IterableToList(t1, t1); iterator::IteratorCloseOnException(t2); } label Fail {} } @export macro TestFrame1(implicit context: Context)(): void { const f: Frame = LoadFramePointer(); const frameType: FrameType = Cast(f.context_or_frame_type) otherwise unreachable; dcheck(frameType == STUB_FRAME); dcheck(f.caller == LoadParentFramePointer()); typeswitch (f) { case (_f: StandardFrame): { unreachable; } case (_f: StubFrame): { } } } @export macro TestNew(implicit context: Context)(): void { const f: JSArray = NewJSArray(); check(f.IsEmpty()); f.length = 0; } struct TestInner { macro SetX(newValue: int32): void { this.x = newValue; } macro GetX(): int32 { return this.x; } x: int32; y: int32; } struct TestOuter { a: int32; b: TestInner; c: int32; } @export macro TestStructConstructor(implicit context: Context)(): void { // Test default constructor let a: TestOuter = TestOuter{a: 5, b: TestInner{x: 6, y: 7}, c: 8}; check(a.a == 5); check(a.b.x == 6); check(a.b.y == 7); check(a.c == 8); a.b.x = 1; check(a.b.x == 1); a.b.SetX(2); check(a.b.x == 2); check(a.b.GetX() == 2); } class InternalClass extends HeapObject { macro Flip(): void 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}; } @export macro TestInternalClass(implicit context: Context)(): void { const o = NewInternalClass(5); o.Flip() otherwise unreachable; check(o.a == 6); check(o.b == 5); } struct StructWithConst { macro TestMethod1(): int32 { return this.b; } macro TestMethod2(): Object { return this.a; } a: Object; const b: int32; } @export macro TestConstInStructs(): void { const x = StructWithConst{a: Null, b: 1}; let y = StructWithConst{a: Null, b: 1}; y.a = Undefined; const _copy = x; check(x.TestMethod1() == 1); check(x.TestMethod2() == Null); } @export macro TestParentFrameArguments(implicit context: Context)(): void { const parentFrame = LoadParentFramePointer(); const castFrame = Cast(parentFrame) otherwise unreachable; const arguments = GetFrameArguments(castFrame, 1); ArgumentsIterator{arguments, current: 0}; } struct TestIterator { macro Next(): Object labels NoMore { if (this.count-- == 0) goto NoMore; return TheHole; } count: Smi; } @export macro TestNewFixedArrayFromSpread(implicit context: Context)(): Object { let i = TestIterator{count: 5}; return new FixedArray{map: kFixedArrayMap, length: 5, objects: ...i}; } class SmiPair extends HeapObject { macro GetA():&Smi { return &this.a; } a: Smi; b: Smi; } macro Swap(a:&T, b:&T): void { const tmp = *a; *a = *b; *b = tmp; } @export macro TestReferences(): void { 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); } @export macro TestSlices(): void { const it = TestIterator{count: 3}; const a = new FixedArray{map: kFixedArrayMap, length: 3, objects: ...it}; check(a.length == 3); const oneTwoThree = Convert(123); a.objects[0] = oneTwoThree; const firstRef:&Object = &a.objects[0]; check(TaggedEqual(*firstRef, oneTwoThree)); const slice: MutableSlice = &a.objects; const firstRefAgain:&Object = slice.TryAtIndex(0) otherwise unreachable; check(TaggedEqual(*firstRefAgain, oneTwoThree)); const threeTwoOne = Convert(321); *firstRefAgain = threeTwoOne; check(TaggedEqual(a.objects[0], threeTwoOne)); // *slice; // error, not allowed // a.objects; // error, not allowed // a.objects = slice; // error, not allowed // TODO(gsps): Currently errors, but should be allowed: // const _sameSlice: MutableSlice = &(*slice); // (*slice)[0] : Smi } @export macro TestSliceEnumeration(implicit context: Context)(): Undefined { const fixedArray: FixedArray = AllocateZeroedFixedArray(3); for (let i: intptr = 0; i < 3; i++) { check(UnsafeCast(fixedArray.objects[i]) == 0); fixedArray.objects[i] = Convert(i) + 3; } let slice = &fixedArray.objects; for (let i: intptr = 0; i < slice.length; i++) { let ref = slice.TryAtIndex(i) otherwise unreachable; const value = UnsafeCast(*ref); check(value == Convert(i) + 3); *ref = value + 4; } let it = slice.Iterator(); let count: Smi = 0; while (true) { const value = UnsafeCast(it.Next() otherwise break); check(value == count + 7); count++; } check(count == 3); check(it.Empty()); return Undefined; } @export macro TestStaticAssert(): void { static_assert(1 + 2 == 3); static_assert(Convert(5) < Convert(6)); static_assert(!(Convert(5) < Convert(5))); static_assert(!(Convert(6) < Convert(5))); static_assert(Convert(5) <= Convert(5)); static_assert(Convert(5) <= Convert(6)); static_assert(!(Convert(6) <= Convert(5))); static_assert(Convert(-6) < Convert(-5)); static_assert(!(Convert(-5) < Convert(-5))); static_assert(!(Convert(-5) < Convert(-6))); static_assert(Convert(-5) <= Convert(-5)); static_assert(Convert(-6) <= Convert(-5)); static_assert(!(Convert(-5) <= Convert(-6))); } class SmiBox extends HeapObject { value: Smi; unrelated: Smi; } builtin NewSmiBox(implicit context: Context)(value: Smi): SmiBox { return new SmiBox{value, unrelated: 0}; } @export macro TestLoadEliminationFixed(implicit context: Context)(): void { const box = NewSmiBox(123); const v1 = box.value; box.unrelated = 999; const v2 = (box.unrelated == 0) ? box.value : box.value; static_assert(TaggedEqual(v1, v2)); box.value = 11; const v3 = box.value; const eleven: Smi = 11; static_assert(TaggedEqual(v3, eleven)); } @export macro TestLoadEliminationVariable(implicit context: Context)(): void { const a = UnsafeCast(kEmptyFixedArray); const box = NewSmiBox(1); const v1 = a.objects[box.value]; const u1 = a.objects[box.value + 2]; const v2 = a.objects[box.value]; const u2 = a.objects[box.value + 2]; static_assert(TaggedEqual(v1, v2)); static_assert(TaggedEqual(u1, u2)); } @export macro TestRedundantArrayElementCheck(implicit context: Context)(): Smi { const a = kEmptyFixedArray; for (let i: Smi = 0; i < a.length; i++) { if (a.objects[i] == TheHole) { if (a.objects[i] == TheHole) { return -1; } else { static_assert(false); } } } return 1; } @export macro TestRedundantSmiCheck(implicit context: Context)(): Smi { const a = kEmptyFixedArray; const x = a.objects[1]; typeswitch (x) { case (Smi): { Cast(x) otherwise VerifiedUnreachable(); return -1; } case (Object): { } } return 1; } struct SBox { value: T; } @export macro TestGenericStruct1(): intptr { const i: intptr = 123; let box = SBox{value: i}; let boxbox: SBox> = SBox{value: box}; check(box.value == 123); boxbox.value.value *= 2; check(boxbox.value.value == 246); return boxbox.value.value; } struct TestTuple { const fst: T1; const snd: T2; } macro TupleSwap(tuple: TestTuple): TestTuple { return TestTuple{fst: tuple.snd, snd: tuple.fst}; } @export macro TestGenericStruct2(): TestTuple, TestTuple> { const intptrAndSmi = TestTuple{fst: 1, snd: 2}; const smiAndIntptr = TupleSwap(intptrAndSmi); check(intptrAndSmi.fst == smiAndIntptr.snd); check(intptrAndSmi.snd == smiAndIntptr.fst); const tupleTuple = TestTuple>{fst: intptrAndSmi, snd: smiAndIntptr}; return tupleTuple; } macro BranchAndWriteResult(x: Smi, box: SmiBox): bool { if (x > 5 || x < 0) { box.value = 1; return true; } else { box.value = 2; return false; } } @export macro TestBranchOnBoolOptimization(implicit context: Context)(input: Smi): void { const box = NewSmiBox(1); // If the two branches get combined into one, we should be able to determine // the value of {box} statically. if (BranchAndWriteResult(input, box)) { static_assert(box.value == 1); } else { static_assert(box.value == 2); } } bitfield struct TestBitFieldStruct extends uint8 { a: bool: 1 bit; b: uint16: 3 bit; c: uint32: 3 bit; d: bool: 1 bit; } @export macro TestBitFieldLoad( val: TestBitFieldStruct, expectedA: bool, expectedB: uint16, expectedC: uint32, expectedD: bool): void { check(val.a == expectedA); check(val.b == expectedB); check(val.c == expectedC); check(val.d == expectedD); } @export macro TestBitFieldStore(val: TestBitFieldStruct): void { let val: TestBitFieldStruct = val; // Get a mutable local copy. const a: bool = val.a; const b: uint16 = val.b; let c: uint32 = val.c; const d: bool = val.d; val.a = !a; TestBitFieldLoad(val, !a, b, c, d); c = Unsigned(7 - Signed(val.c)); val.c = c; TestBitFieldLoad(val, !a, b, c, d); val.d = val.b == val.c; TestBitFieldLoad(val, !a, b, c, b == c); } @export macro TestBitFieldInit(a: bool, b: uint16, c: uint32, d: bool): void { const val: TestBitFieldStruct = TestBitFieldStruct{a: a, b: b, c: c, d: d}; TestBitFieldLoad(val, a, b, c, d); } // Some other bitfield structs, to verify getting uintptr values out of word32 // structs and vice versa. bitfield struct TestBitFieldStruct2 extends uint32 { a: uintptr: 5 bit; b: uintptr: 6 bit; } bitfield struct TestBitFieldStruct3 extends uintptr { c: bool: 1 bit; d: uint32: 9 bit; e: uintptr: 17 bit; } @export macro TestBitFieldUintptrOps( val2: TestBitFieldStruct2, val3: TestBitFieldStruct3): void { let val2: TestBitFieldStruct2 = val2; // Get a mutable local copy. let val3: TestBitFieldStruct3 = val3; // Get a mutable local copy. // Caller is expected to provide these exact values, so we can verify // reading values before starting to write anything. check(val2.a == 3); check(val2.b == 61); check(val3.c); check(val3.d == 500); check(val3.e == 0x1cc); val2.b = 16; check(val2.a == 3); check(val2.b == 16); val2.b++; check(val2.a == 3); check(val2.b == 17); val3.d = 99; val3.e = 1234; check(val3.c); check(val3.d == 99); check(val3.e == 1234); } bitfield struct TestBitFieldStruct4 extends uint31 { a: bool: 1 bit; b: int32: 3 bit; c: bool: 1 bit; } bitfield struct TestBitFieldStruct5 extends uint31 { b: int32: 19 bit; a: bool: 1 bit; c: bool: 1 bit; } @export macro TestBitFieldMultipleFlags(a: bool, b: int32, c: bool): void { const f = TestBitFieldStruct4{a: a, b: b, c: c}; let simpleExpression = f.a & f.b == 3 & !f.c; let expectedReduction = (Signed(f) & 0x1f) == Convert(1 | 3 << 1); static_assert(simpleExpression == expectedReduction); simpleExpression = !f.a & f.b == 4 & f.c; expectedReduction = (Signed(f) & 0x1f) == Convert(4 << 1 | 1 << 4); static_assert(simpleExpression == expectedReduction); simpleExpression = f.b == 0 & f.c; expectedReduction = (Signed(f) & 0x1e) == Convert(1 << 4); static_assert(simpleExpression == expectedReduction); simpleExpression = f.a & f.c; expectedReduction = (Signed(f) & 0x11) == Convert(1 | 1 << 4); static_assert(simpleExpression == expectedReduction); const f2 = TestBitFieldStruct5{b: b, a: a, c: c}; simpleExpression = !f2.a & f2.b == 1234 & f2.c; expectedReduction = (Signed(f2) & 0x1fffff) == Convert(1234 | 1 << 20); static_assert(simpleExpression == expectedReduction); simpleExpression = !f2.a & !f2.c; expectedReduction = (Signed(f2) & 0x180000) == Convert(0); static_assert(simpleExpression == expectedReduction); } @export class ExportedSubClass extends ExportedSubClassBase { c_field: int32; d_field: int32; e_field: Smi; } @export class ExportedSubClassBase extends HeapObject { a: HeapObject; b: HeapObject; } @abstract class AbstractInternalClass extends HeapObject { } class AbstractInternalClassSubclass1 extends AbstractInternalClass {} class AbstractInternalClassSubclass2 extends AbstractInternalClass {} class InternalClassWithSmiElements extends FixedArrayBase { data: Smi; object: Oddball; entries[length]: Smi; } struct InternalClassStructElement { a: Smi; b: Smi; } class InternalClassWithStructElements extends HeapObject { dummy1: int32; dummy2: int32; const count: Smi; data: Smi; object: Object; entries[count]: Smi; more_entries[count]: InternalClassStructElement; } struct SmiGeneratorIterator { macro Next(): Smi labels _NoMore { return this.value++; } value: Smi; } struct InternalClassStructElementGeneratorIterator { macro Next(): InternalClassStructElement labels _NoMore { return InternalClassStructElement{a: this.value++, b: this.value++}; } value: Smi; } @export macro TestFullyGeneratedClassWithElements(): void { // Test creation, initialization and access of a fully generated class with // simple (Smi) elements const length: Smi = Convert(3); const object1 = new InternalClassWithSmiElements{ length, data: 0, object: Undefined, entries: ...SmiGeneratorIterator { value: 11 } }; dcheck(object1.length == 3); dcheck(object1.data == 0); dcheck(object1.object == Undefined); dcheck(object1.entries[0] == 11); dcheck(object1.entries[1] == 12); dcheck(object1.entries[2] == 13); // Test creation, initialization and access of a fully generated class // with elements that are a struct. const object2 = new InternalClassWithStructElements{ dummy1: 44, dummy2: 45, count: length, data: 55, object: Undefined, entries: ...SmiGeneratorIterator{value: 3}, more_entries: ...InternalClassStructElementGeneratorIterator { value: 1 } }; dcheck(object2.dummy1 == 44); dcheck(object2.dummy2 == 45); dcheck(object2.count == 3); dcheck(object2.data == 55); dcheck(object2.object == Undefined); dcheck(object2.entries[0] == 3); dcheck(object2.entries[1] == 4); dcheck(object2.entries[2] == 5); dcheck(object2.more_entries[0].a == 1); dcheck(object2.more_entries[0].b == 2); dcheck(object2.more_entries[1].a == 3); dcheck(object2.more_entries[1].b == 4); dcheck(object2.more_entries[2].a == 5); dcheck(object2.more_entries[2].b == 6); } @export macro TestFullyGeneratedClassFromCpp(): ExportedSubClass { return new ExportedSubClass{a: Null, b: Null, c_field: 7, d_field: 8, e_field: 9}; } @export class ExportedSubClass2 extends ExportedSubClassBase { x_field: int32; y_field: int32; z_field: Smi; } @export macro TestGeneratedCastOperators(implicit context: Context)(): void { const a = new ExportedSubClass{a: Null, b: Null, c_field: 3, d_field: 4, e_field: 5}; const b = new ExportedSubClassBase{a: Undefined, b: Null}; const c = new ExportedSubClass2{a: Null, b: Null, x_field: 3, y_field: 4, z_field: 5}; const aO: Object = a; const bO: Object = b; const cO: Object = c; dcheck(Is(aO)); dcheck(Is(aO)); dcheck(!Is(aO)); dcheck(Is(bO)); dcheck(!Is(bO)); dcheck(Is(cO)); dcheck(!Is(cO)); dcheck(Is(cO)); const jsf: JSFunction = *NativeContextSlot(ContextSlot::REGEXP_FUNCTION_INDEX); dcheck(!Is(jsf)); const parameterValues = NewFixedArray(0, ConstantIterator(TheHole)); const elements = NewSloppyArgumentsElements( 0, context, parameterValues, ConstantIterator(TheHole)); const fastArgs = arguments::NewJSFastAliasedArgumentsObject( elements, Convert(0), jsf); dcheck(Is(fastArgs)); } extern runtime InYoungGeneration(implicit context: Context)(HeapObject): Boolean; @export macro TestNewPretenured(implicit context: Context)(): void { const obj = new (Pretenured) ExportedSubClassBase{a: Undefined, b: Null}; dcheck(Is(obj)); dcheck(InYoungGeneration(obj) == False); } @export macro TestWord8Phi(): void { for (let i: intptr = -5; i < 5; ++i) { let x: int8; if (i == -1) { x = -1; } else { x = Convert(i); } check(x == Convert(i)); } } @export macro TestOffHeapSlice(ptr: RawPtr, length: intptr): void { const string = UnsafeCast(Convert('Hello World!')); check(*torque_internal::unsafe::NewOffHeapReference(ptr) == string.chars[0]); let offHeapSlice = torque_internal::unsafe::NewOffHeapConstSlice(ptr, length); let onHeapSlice = &string.chars; for (let i: intptr = 0; i < onHeapSlice.length; ++i) { check(*onHeapSlice.AtIndex(i) == *offHeapSlice.AtIndex(i)); } } struct TwoValues { a: Smi; b: Map; } builtin ReturnTwoValues(implicit context: Context)( value: Smi, obj: HeapObject): TwoValues { return TwoValues{a: value + 1, b: obj.map}; } @export macro TestCallMultiReturnBuiltin(implicit context: Context)(): void { const result = ReturnTwoValues(444, FromConstexpr('hi')); check(result.a == 445); check(result.b == FromConstexpr('hi').map); } @export macro TestRunLazyTwice(lazySmi: Lazy): Smi { const firstResult = RunLazy(lazySmi); const secondResult = RunLazy(lazySmi); return firstResult + secondResult; } macro GetLazySmi(): Smi { return 3; } macro AddTwoSmiValues(a: Smi, b: Smi): Smi { return a + b; } macro AddSmiAndConstexprValues(a: Smi, b: constexpr int31): Smi { return a + b; } @export macro TestCreateLazyNodeFromTorque(): void { const lazy = %MakeLazy('GetLazySmi'); const result = TestRunLazyTwice(lazy); check(result == 6); // The macro can also be referred to using namespace qualifications. const lazy2 = %MakeLazy('test::GetLazySmi'); const result2 = TestRunLazyTwice(lazy2); check(result2 == 6); // We can save params to the macro. The most common usage is likely a // single-arg macro that just returns the arg, but we can use any number of // params. const lazy3 = %MakeLazy('AddTwoSmiValues', 5, 6); const result3 = TestRunLazyTwice(lazy3); check(result3 == 22); // It's okay if some of the params are constexpr and some aren't. const lazy4 = %MakeLazy('AddSmiAndConstexprValues', 7, 8); const result4 = TestRunLazyTwice(lazy4); check(result4 == 30); } }