| // 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): 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); |
| } |
| |
| @export |
| macro TestConstexpr1() { |
| check(FromConstexpr<bool>(IsFastElementsKind(PACKED_SMI_ELEMENTS))); |
| } |
| |
| @export |
| macro TestConstexprIf() { |
| check(ElementsKindTestHelper1(UINT8_ELEMENTS)); |
| check(ElementsKindTestHelper1(UINT16_ELEMENTS)); |
| check(!ElementsKindTestHelper1(UINT32_ELEMENTS)); |
| } |
| |
| @export |
| macro TestConstexprReturn() { |
| check(FromConstexpr<bool>(ElementsKindTestHelper2(UINT8_ELEMENTS))); |
| check(FromConstexpr<bool>(ElementsKindTestHelper2(UINT16_ELEMENTS))); |
| check(!FromConstexpr<bool>(ElementsKindTestHelper2(UINT32_ELEMENTS))); |
| check(FromConstexpr<bool>(!ElementsKindTestHelper2(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<T: type>(_c: Context, _param: T): Object { |
| return Null; |
| } |
| |
| GenericBuiltinTest<Object>(_c: Context, param: Object): Object { |
| return param; |
| } |
| |
| @export |
| 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; |
| } |
| } |
| |
| @export |
| macro TestPartiallyUnusedLabel(): Boolean { |
| const r1: bool = CallLabelTestHelper4(true); |
| const 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; |
| } |
| |
| @export |
| 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); |
| 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; |
| } |
| |
| @export |
| 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; |
| } |
| |
| @export |
| macro TestVariableRedeclaration(implicit context: Context)(): Boolean { |
| let _var1: int31 = FromConstexpr<bool>(42 == 0) ? 0 : 1; |
| let _var2: int31 = FromConstexpr<bool>(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(c: Context) { |
| const fptr1: builtin(Context, Smi) => Object = GenericBuiltinTest<Smi>; |
| const 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); |
| } |
| |
| type ObjectToObject = builtin(Context, Object) => Object; |
| @export |
| macro TestTypeAlias(x: ObjectToObject): BuiltinPtr { |
| return x; |
| } |
| |
| @export |
| macro TestUnsafeCast(implicit context: Context)(n: Number): Boolean { |
| if (TaggedIsSmi(n)) { |
| const m: Smi = UnsafeCast<Smi>(n); |
| |
| check(TestHelperPlus1(context, m) == 11); |
| return True; |
| } |
| return False; |
| } |
| |
| @export |
| macro TestHexLiteral() { |
| check(Convert<intptr>(0xffff) + 1 == 0x10000); |
| check(Convert<intptr>(-0xffff) == -65535); |
| } |
| |
| @export |
| macro TestLargeIntegerLiterals(implicit c: Context)() { |
| let _x: int32 = 0x40000000; |
| let _y: int32 = 0x7fffffff; |
| } |
| |
| @export |
| macro TestMultilineAssert() { |
| const someVeryLongVariableNameThatWillCauseLineBreaks: Smi = 5; |
| check( |
| someVeryLongVariableNameThatWillCauseLineBreaks > 0 && |
| someVeryLongVariableNameThatWillCauseLineBreaks < 10); |
| } |
| |
| @export |
| macro TestNewlineInString() { |
| Print('Hello, World!\n'); |
| } |
| |
| const kConstexprConst: constexpr int31 = 5; |
| const kIntptrConst: intptr = 4; |
| const kSmiConst: Smi = 3; |
| |
| @export |
| macro TestModuleConstBindings() { |
| check(kConstexprConst == Int32Constant(5)); |
| check(kIntptrConst == 4); |
| check(kSmiConst == 3); |
| } |
| |
| @export |
| 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; |
| } |
| |
| @export |
| macro TestStruct1(i: TestStructA): Smi { |
| return i.i; |
| } |
| |
| @export |
| macro TestStruct2(implicit context: Context)(): TestStructA { |
| return TestStructA{ |
| indexes: UnsafeCast<FixedArray>(kEmptyFixedArray), |
| i: 27, |
| k: 31 |
| }; |
| } |
| |
| @export |
| macro TestStruct3(implicit context: Context)(): TestStructA { |
| let a: TestStructA = |
| TestStructA{indexes: UnsafeCast<FixedArray>(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<FixedArray>(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)() { |
| 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() { |
| 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) { |
| const _foo: Object = x; |
| } |
| |
| macro IncrementIfSmi<A: type>(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<int32>(x); |
| } |
| case (a: FixedArray): { |
| result = result + Convert<int32>(a.length); |
| } |
| case (_x: HeapNumber): { |
| result = result + 7; |
| } |
| } |
| |
| return result; |
| } |
| |
| @export |
| macro TestTypeswitch(implicit context: Context)() { |
| check(TypeswitchExample(FromConstexpr<Smi>(5)) == 26); |
| const a: FixedArray = AllocateZeroedFixedArray(3); |
| check(TypeswitchExample(a) == 13); |
| check(TypeswitchExample(FromConstexpr<Number>(0.5)) == 27); |
| } |
| |
| @export |
| macro TestTypeswitchAsanLsanFailure(implicit context: Context)(obj: Object) { |
| typeswitch (obj) { |
| case (_o: Smi): { |
| } |
| case (_o: JSTypedArray): { |
| } |
| case (_o: JSReceiver): { |
| } |
| case (_o: HeapObject): { |
| } |
| } |
| } |
| |
| macro ExampleGenericOverload<A: type>(o: Object): A { |
| return o; |
| } |
| macro ExampleGenericOverload<A: type>(o: Smi): A { |
| return o + 1; |
| } |
| |
| @export |
| macro TestGenericOverload(implicit context: Context)() { |
| const xSmi: Smi = 5; |
| const xObject: Object = xSmi; |
| check(ExampleGenericOverload<Smi>(xSmi) == 6); |
| check(UnsafeCast<Smi>(ExampleGenericOverload<Object>(xObject)) == 5); |
| } |
| |
| @export |
| 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; |
| } |
| } |
| |
| @export |
| macro TestOrAnd1(x: bool, y: bool, z: bool): bool { |
| return BoolToBranch(x) || y && z ? true : false; |
| } |
| |
| @export |
| macro TestOrAnd2(x: bool, y: bool, z: bool): bool { |
| return x || BoolToBranch(y) && z ? true : false; |
| } |
| |
| @export |
| macro TestOrAnd3(x: bool, y: bool, z: bool): bool { |
| return x || y && BoolToBranch(z) ? true : false; |
| } |
| |
| @export |
| macro TestAndOr1(x: bool, y: bool, z: bool): bool { |
| return BoolToBranch(x) && y || z ? true : false; |
| } |
| |
| @export |
| macro TestAndOr2(x: bool, y: bool, z: bool): bool { |
| return x && BoolToBranch(y) || z ? true : false; |
| } |
| |
| @export |
| macro TestAndOr3(x: bool, y: bool, z: bool): bool { |
| return x && y || BoolToBranch(z) ? true : false; |
| } |
| |
| @export |
| 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)); |
| } |
| |
| @export |
| macro TestCall(i: Smi): Smi labels A { |
| if (i < 5) return i; |
| goto A; |
| } |
| |
| @export |
| 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); |
| } |
| |
| @export |
| macro TestOtherwiseWithCode2() { |
| let s: Smi = 0; |
| for (let i: Smi = 0; i < 10; ++i) { |
| TestCall(i) otherwise break; |
| ++s; |
| } |
| assert(s == 5); |
| } |
| |
| @export |
| macro TestOtherwiseWithCode3() { |
| let s: Smi = 0; |
| for (let i: Smi = 0; i < 10; ++i) { |
| s += TestCall(i) otherwise break; |
| } |
| assert(s == 10); |
| } |
| |
| @export |
| macro TestForwardLabel() { |
| try { |
| goto A; |
| } |
| label A { |
| goto B(5); |
| } |
| label B(b: Smi) { |
| assert(b == 5); |
| } |
| } |
| |
| @export |
| macro TestQualifiedAccess(implicit context: Context)() { |
| const s: Smi = 0; |
| check(!array::IsJSArray(s)); |
| } |
| |
| @export |
| macro TestCatch1(implicit context: Context)(): Smi { |
| let r: Smi = 0; |
| try { |
| ThrowTypeError(kInvalidArrayLength); |
| } catch (_e) { |
| r = 1; |
| return r; |
| } |
| } |
| |
| @export |
| macro TestCatch2Wrapper(implicit context: Context)(): never { |
| ThrowTypeError(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(kInvalidArrayLength); |
| } |
| |
| @export |
| 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. |
| @export |
| macro TestIterator(implicit context: Context)(o: JSReceiver, 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(o); |
| const _t6: Object = iterator::IteratorValue(o, map); |
| |
| const _t7: JSArray = iterator::IterableToList(t1, t1); |
| |
| iterator::IteratorCloseOnException(t2, t5); |
| } |
| label Fail {} |
| } |
| |
| @export |
| macro TestFrame1(implicit context: Context)() { |
| const f: Frame = LoadFramePointer(); |
| const frameType: FrameType = |
| Cast<FrameType>(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): { |
| } |
| } |
| } |
| |
| @export |
| macro TestNew(implicit context: Context)() { |
| const f: JSArray = NewJSArray(); |
| check(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; |
| } |
| |
| @export |
| macro TestStructConstructor(implicit context: Context)() { |
| // 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 Struct { |
| Flip() labels NotASmi { |
| const tmp = Cast<Smi>(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)() { |
| 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; |
| } |
| |
| @export |
| macro TestConstInStructs() { |
| 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); |
| } |
| |
| struct TestIterator { |
| 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 Struct { |
| GetA():&Smi { |
| return & this.a; |
| } |
| a: Smi; |
| b: Smi; |
| } |
| |
| macro Swap<T: type>(a:&T, b:&T) { |
| const tmp = * a; |
| * a = * b; |
| * b = tmp; |
| } |
| |
| @export |
| 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); |
| } |
| |
| @export |
| macro TestStaticAssert() { |
| StaticAssert(1 + 2 == 3); |
| } |
| |
| class SmiBox extends Struct { |
| value: Smi; |
| unrelated: Smi; |
| } |
| |
| builtin NewSmiBox(implicit context: Context)(value: Smi): SmiBox { |
| return new SmiBox{value, unrelated: 0}; |
| } |
| |
| @export |
| macro TestLoadEliminationFixed(implicit context: Context)() { |
| const box = NewSmiBox(123); |
| const v1 = box.value; |
| box.unrelated = 999; |
| const v2 = (box.unrelated == 0) ? box.value : box.value; |
| StaticAssert(WordEqual(v1, v2)); |
| |
| box.value = 11; |
| const v3 = box.value; |
| const eleven: Smi = 11; |
| StaticAssert(WordEqual(v3, eleven)); |
| } |
| |
| @export |
| macro TestLoadEliminationVariable(implicit context: Context)() { |
| const a = UnsafeCast<FixedArray>(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]; |
| StaticAssert(WordEqual(v1, v2)); |
| StaticAssert(WordEqual(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 { |
| StaticAssert(false); |
| } |
| } |
| } |
| return 1; |
| } |
| |
| @export |
| macro TestRedundantSmiCheck(implicit context: Context)(): Smi { |
| const a = kEmptyFixedArray; |
| const x = a.objects[1]; |
| typeswitch (x) { |
| case (Smi): { |
| Cast<Smi>(x) otherwise VerifiedUnreachable(); |
| return -1; |
| } |
| case (Object): { |
| } |
| } |
| return 1; |
| } |
| |
| struct SBox<T: type> { |
| value: T; |
| } |
| |
| @export |
| macro TestGenericStruct1(): intptr { |
| const i: intptr = 123; |
| let box = SBox<intptr>{value: i}; |
| let boxbox = SBox<SBox<intptr>>{value: box}; |
| check(box.value == 123); |
| boxbox.value.value *= 2; |
| check(boxbox.value.value == 246); |
| return boxbox.value.value; |
| } |
| |
| struct TestTuple<T1: type, T2: type> { |
| const fst: T1; |
| const snd: T2; |
| } |
| |
| macro Swap<T1: type, T2: type>(tuple: TestTuple<T1, T2>): |
| TestTuple<T2, T1> { |
| return TestTuple<T2, T1>{fst: tuple.snd, snd: tuple.fst}; |
| } |
| |
| @export |
| macro TestGenericStruct2(): TestTuple<Smi, intptr> { |
| const intptrAndSmi = TestTuple<intptr, Smi>{fst: 1, snd: 2}; |
| const smiAndIntptr = Swap<intptr, Smi>(intptrAndSmi); |
| check(intptrAndSmi.fst == smiAndIntptr.snd); |
| check(intptrAndSmi.snd == smiAndIntptr.fst); |
| return smiAndIntptr; |
| } |
| |
| } |