ede2740711
This change adds a new abstract type Lazy<T> which can be used to interoperate with CSA code that uses LazyNode. This new type has special code-generation rules because its generated type is not TNode<...> but std::function<TNode<...>()>. Torque code can do nothing with this type except pass it around, but passing it to the CSA function RunLazy is an easy way to execute the std::function and get back a normal value. Torque code can also create Lazy<T> values using the intrinsic function %MakeLazy, which takes the name of a macro as its first parameter, followed by arguments to that macro which will be passed when the LazyNode is evaluated. We use the macro's name because the language doesn't support taking references to macros, and implementing such a feature would be complicated. Bug: v8:7793 Change-Id: I09120960e3492dd51be0d4c57e14ff3826b99262 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2701752 Commit-Queue: Seth Brenith <seth.brenith@microsoft.com> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Cr-Commit-Position: refs/heads/master@{#72964}
1414 lines
33 KiB
Plaintext
1414 lines
33 KiB
Plaintext
// 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() {
|
|
check(FromConstexpr<bool>(
|
|
IsFastElementsKind(ElementsKind::PACKED_SMI_ELEMENTS)));
|
|
}
|
|
|
|
@export
|
|
macro TestConstexprIf() {
|
|
check(ElementsKindTestHelper1(ElementsKind::UINT8_ELEMENTS));
|
|
check(ElementsKindTestHelper1(ElementsKind::UINT16_ELEMENTS));
|
|
check(!ElementsKindTestHelper1(ElementsKind::UINT32_ELEMENTS));
|
|
}
|
|
|
|
@export
|
|
macro TestConstexprReturn() {
|
|
check(FromConstexpr<bool>(
|
|
ElementsKindTestHelper2(ElementsKind::UINT8_ELEMENTS)));
|
|
check(FromConstexpr<bool>(
|
|
ElementsKindTestHelper2(ElementsKind::UINT16_ELEMENTS)));
|
|
check(!FromConstexpr<bool>(
|
|
ElementsKindTestHelper2(ElementsKind::UINT32_ELEMENTS)));
|
|
check(FromConstexpr<bool>(
|
|
!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<T: type>(_param: T): JSAny {
|
|
return Null;
|
|
}
|
|
|
|
GenericBuiltinTest<JSAny>(param: JSAny): JSAny {
|
|
return param;
|
|
}
|
|
|
|
@export
|
|
macro TestBuiltinSpecialization() {
|
|
check(GenericBuiltinTest<Smi>(0) == Null);
|
|
check(GenericBuiltinTest<Smi>(1) == Null);
|
|
check(GenericBuiltinTest<JSAny>(Undefined) == Undefined);
|
|
check(GenericBuiltinTest<JSAny>(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(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<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() {
|
|
const fptr1: builtin(Smi) => JSAny = GenericBuiltinTest<Smi>;
|
|
const fptr2: builtin(JSAny) => JSAny = GenericBuiltinTest<JSAny>;
|
|
|
|
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<Smi>(n);
|
|
|
|
check(TestHelperPlus1(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: JSAny = 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);
|
|
}
|
|
|
|
@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() {
|
|
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() {
|
|
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(!Is<JSArray>(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) {
|
|
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)() {
|
|
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: StubFrame): {
|
|
}
|
|
}
|
|
}
|
|
|
|
@export
|
|
macro TestNew(implicit context: Context)() {
|
|
const f: JSArray = NewJSArray();
|
|
check(f.IsEmpty());
|
|
f.length = 0;
|
|
}
|
|
|
|
struct TestInner {
|
|
macro SetX(newValue: int32) {
|
|
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)() {
|
|
// 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() 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 {
|
|
macro TestMethod1(): int32 {
|
|
return this.b;
|
|
}
|
|
macro 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);
|
|
}
|
|
|
|
@export
|
|
macro TestParentFrameArguments(implicit context: Context)() {
|
|
const parentFrame = LoadParentFramePointer();
|
|
const castFrame = Cast<StandardFrame>(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<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 TestSlices() {
|
|
const it = TestIterator{count: 3};
|
|
const a = new FixedArray{map: kFixedArrayMap, length: 3, objects: ...it};
|
|
check(a.length == 3);
|
|
|
|
const oneTwoThree = Convert<Smi>(123);
|
|
a.objects[0] = oneTwoThree;
|
|
const firstRef:&Object = &a.objects[0];
|
|
check(TaggedEqual(*firstRef, oneTwoThree));
|
|
|
|
const slice: MutableSlice<Object> = &a.objects;
|
|
const firstRefAgain:&Object = slice.TryAtIndex(0) otherwise unreachable;
|
|
check(TaggedEqual(*firstRefAgain, oneTwoThree));
|
|
|
|
const threeTwoOne = Convert<Smi>(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<Object> = &(*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<Smi>(fixedArray.objects[i]) == 0);
|
|
fixedArray.objects[i] = Convert<Smi>(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<Smi>(*ref);
|
|
check(value == Convert<Smi>(i) + 3);
|
|
*ref = value + 4;
|
|
}
|
|
|
|
let it = slice.Iterator();
|
|
let count: Smi = 0;
|
|
while (true) {
|
|
const value = UnsafeCast<Smi>(it.Next() otherwise break);
|
|
check(value == count + 7);
|
|
count++;
|
|
}
|
|
check(count == 3);
|
|
check(it.Empty());
|
|
|
|
return Undefined;
|
|
}
|
|
|
|
@export
|
|
macro TestStaticAssert() {
|
|
static_assert(1 + 2 == 3);
|
|
|
|
static_assert(Convert<uintptr>(5) < Convert<uintptr>(6));
|
|
static_assert(!(Convert<uintptr>(5) < Convert<uintptr>(5)));
|
|
static_assert(!(Convert<uintptr>(6) < Convert<uintptr>(5)));
|
|
static_assert(Convert<uintptr>(5) <= Convert<uintptr>(5));
|
|
static_assert(Convert<uintptr>(5) <= Convert<uintptr>(6));
|
|
static_assert(!(Convert<uintptr>(6) <= Convert<uintptr>(5)));
|
|
|
|
static_assert(Convert<intptr>(-6) < Convert<intptr>(-5));
|
|
static_assert(!(Convert<intptr>(-5) < Convert<intptr>(-5)));
|
|
static_assert(!(Convert<intptr>(-5) < Convert<intptr>(-6)));
|
|
static_assert(Convert<intptr>(-5) <= Convert<intptr>(-5));
|
|
static_assert(Convert<intptr>(-6) <= Convert<intptr>(-5));
|
|
static_assert(!(Convert<intptr>(-5) <= Convert<intptr>(-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)() {
|
|
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)() {
|
|
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];
|
|
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<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{value: i};
|
|
let boxbox: SBox<SBox<intptr>> = SBox{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 TupleSwap<T1: type, T2: type>(tuple: TestTuple<T1, T2>):
|
|
TestTuple<T2, T1> {
|
|
return TestTuple{fst: tuple.snd, snd: tuple.fst};
|
|
}
|
|
|
|
@export
|
|
macro TestGenericStruct2():
|
|
TestTuple<TestTuple<intptr, Smi>, TestTuple<Smi, intptr>> {
|
|
const intptrAndSmi = TestTuple<intptr, Smi>{fst: 1, snd: 2};
|
|
const smiAndIntptr = TupleSwap(intptrAndSmi);
|
|
check(intptrAndSmi.fst == smiAndIntptr.snd);
|
|
check(intptrAndSmi.snd == smiAndIntptr.fst);
|
|
const tupleTuple =
|
|
TestTuple<TestTuple<intptr, Smi>>{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) {
|
|
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) {
|
|
check(val.a == expectedA);
|
|
check(val.b == expectedB);
|
|
check(val.c == expectedC);
|
|
check(val.d == expectedD);
|
|
}
|
|
|
|
@export
|
|
macro TestBitFieldStore(val: TestBitFieldStruct) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
const f = TestBitFieldStruct4{a: a, b: b, c: c};
|
|
let simpleExpression = f.a & f.b == 3 & !f.c;
|
|
let expectedReduction = (Signed(f) & 0x1f) == Convert<int32>(1 | 3 << 1);
|
|
static_assert(simpleExpression == expectedReduction);
|
|
simpleExpression = !f.a & f.b == 4 & f.c;
|
|
expectedReduction = (Signed(f) & 0x1f) == Convert<int32>(4 << 1 | 1 << 4);
|
|
static_assert(simpleExpression == expectedReduction);
|
|
simpleExpression = f.b == 0 & f.c;
|
|
expectedReduction = (Signed(f) & 0x1e) == Convert<int32>(1 << 4);
|
|
static_assert(simpleExpression == expectedReduction);
|
|
simpleExpression = f.a & f.c;
|
|
expectedReduction = (Signed(f) & 0x11) == Convert<int32>(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<int32>(1234 | 1 << 20);
|
|
static_assert(simpleExpression == expectedReduction);
|
|
simpleExpression = !f2.a & !f2.c;
|
|
expectedReduction = (Signed(f2) & 0x180000) == Convert<int32>(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() {
|
|
// Test creation, initialization and access of a fully generated class with
|
|
// simple (Smi) elements
|
|
const length: Smi = Convert<Smi>(3);
|
|
const object1 = new InternalClassWithSmiElements{
|
|
length,
|
|
data: 0,
|
|
object: Undefined,
|
|
entries: ...SmiGeneratorIterator {
|
|
value: 11
|
|
}
|
|
};
|
|
assert(object1.length == 3);
|
|
assert(object1.data == 0);
|
|
assert(object1.object == Undefined);
|
|
assert(object1.entries[0] == 11);
|
|
assert(object1.entries[1] == 12);
|
|
assert(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
|
|
}
|
|
};
|
|
|
|
assert(object2.dummy1 == 44);
|
|
assert(object2.dummy2 == 45);
|
|
assert(object2.count == 3);
|
|
assert(object2.data == 55);
|
|
assert(object2.object == Undefined);
|
|
assert(object2.entries[0] == 3);
|
|
assert(object2.entries[1] == 4);
|
|
assert(object2.entries[2] == 5);
|
|
assert(object2.more_entries[0].a == 1);
|
|
assert(object2.more_entries[0].b == 2);
|
|
assert(object2.more_entries[1].a == 3);
|
|
assert(object2.more_entries[1].b == 4);
|
|
assert(object2.more_entries[2].a == 5);
|
|
assert(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)() {
|
|
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;
|
|
assert(Is<ExportedSubClassBase>(aO));
|
|
assert(Is<ExportedSubClass>(aO));
|
|
assert(!Is<ExportedSubClass2>(aO));
|
|
assert(Is<ExportedSubClassBase>(bO));
|
|
assert(!Is<ExportedSubClass>(bO));
|
|
assert(Is<ExportedSubClassBase>(cO));
|
|
assert(!Is<ExportedSubClass>(cO));
|
|
assert(Is<ExportedSubClass2>(cO));
|
|
|
|
const jsf: JSFunction =
|
|
*NativeContextSlot(ContextSlot::REGEXP_FUNCTION_INDEX);
|
|
assert(!Is<JSSloppyArgumentsObject>(jsf));
|
|
|
|
const parameterValues = NewFixedArray(0, ConstantIterator(TheHole));
|
|
const elements = NewSloppyArgumentsElements(
|
|
0, context, parameterValues, ConstantIterator(TheHole));
|
|
const fastArgs = arguments::NewJSFastAliasedArgumentsObject(
|
|
elements, Convert<Smi>(0), jsf);
|
|
assert(Is<JSArgumentsObject>(fastArgs));
|
|
}
|
|
|
|
extern runtime InYoungGeneration(implicit context: Context)(HeapObject):
|
|
Boolean;
|
|
|
|
@export
|
|
macro TestNewPretenured(implicit context: Context)() {
|
|
const obj = new (Pretenured) ExportedSubClassBase{a: Undefined, b: Null};
|
|
assert(Is<ExportedSubClassBase>(obj));
|
|
assert(InYoungGeneration(obj) == False);
|
|
}
|
|
|
|
@export
|
|
macro TestWord8Phi() {
|
|
for (let i: intptr = -5; i < 5; ++i) {
|
|
let x: int8;
|
|
if (i == -1) {
|
|
x = -1;
|
|
} else {
|
|
x = Convert<int8>(i);
|
|
}
|
|
check(x == Convert<int8>(i));
|
|
}
|
|
}
|
|
|
|
@export
|
|
macro TestOffHeapSlice(ptr: RawPtr<char8>, length: intptr) {
|
|
const string = UnsafeCast<SeqOneByteString>(Convert<String>('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)() {
|
|
const result = ReturnTwoValues(444, FromConstexpr<String>('hi'));
|
|
check(result.a == 445);
|
|
check(result.b == FromConstexpr<String>('hi').map);
|
|
}
|
|
|
|
@export
|
|
macro TestRunLazyTwice(lazySmi: Lazy<Smi>): 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() {
|
|
const lazy = %MakeLazy<Smi>('GetLazySmi');
|
|
const result = TestRunLazyTwice(lazy);
|
|
check(result == 6);
|
|
|
|
// The macro can also be referred to using namespace qualifications.
|
|
const lazy2 = %MakeLazy<Smi>('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<Smi>('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<Smi>('AddSmiAndConstexprValues', 7, 8);
|
|
const result4 = TestRunLazyTwice(lazy4);
|
|
check(result4 == 30);
|
|
}
|
|
}
|