2018-10-15 22:31:24 +00:00
|
|
|
// 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.
|
|
|
|
|
2019-04-24 13:25:27 +00:00
|
|
|
#include "src/torque/torque-compiler.h"
|
2018-10-15 22:31:24 +00:00
|
|
|
#include "src/torque/utils.h"
|
|
|
|
#include "test/unittests/test-utils.h"
|
2019-04-24 13:25:27 +00:00
|
|
|
#include "testing/gmock-support.h"
|
2018-10-15 22:31:24 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace torque {
|
|
|
|
|
2019-05-02 07:54:08 +00:00
|
|
|
namespace {
|
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
// This is a simplified version of the basic Torque type definitions.
|
|
|
|
// Some class types are replaced by abstact types to keep it self-contained and
|
|
|
|
// small.
|
|
|
|
constexpr const char* kTestTorquePrelude = R"(
|
|
|
|
type void;
|
|
|
|
type never;
|
|
|
|
|
|
|
|
type Tagged generates 'TNode<Object>' constexpr 'ObjectPtr';
|
|
|
|
type Smi extends Tagged generates 'TNode<Smi>' constexpr 'Smi';
|
|
|
|
|
|
|
|
@abstract
|
|
|
|
extern class HeapObject extends Tagged {
|
|
|
|
map: Map;
|
|
|
|
}
|
|
|
|
type Map extends HeapObject generates 'TNode<Map>';
|
|
|
|
type Object = Smi | HeapObject;
|
|
|
|
type JSReceiver extends HeapObject generates 'TNode<JSReceiver>';
|
|
|
|
type JSObject extends JSReceiver generates 'TNode<JSObject>';
|
|
|
|
type int32 generates 'TNode<Int32T>' constexpr 'int32_t';
|
|
|
|
type uint32 generates 'TNode<Uint32T>' constexpr 'uint32_t';
|
|
|
|
type int31 extends int32
|
|
|
|
generates 'TNode<Int32T>' constexpr 'int31_t';
|
|
|
|
type uint31 extends uint32
|
|
|
|
generates 'TNode<Uint32T>' constexpr 'uint31_t';
|
|
|
|
type int16 extends int31
|
|
|
|
generates 'TNode<Int16T>' constexpr 'int16_t';
|
|
|
|
type uint16 extends uint31
|
|
|
|
generates 'TNode<Uint16T>' constexpr 'uint16_t';
|
|
|
|
type int8 extends int16 generates 'TNode<Int8T>' constexpr 'int8_t';
|
|
|
|
type uint8 extends uint16
|
|
|
|
generates 'TNode<Uint8T>' constexpr 'uint8_t';
|
|
|
|
type int64 generates 'TNode<Int64T>' constexpr 'int64_t';
|
|
|
|
type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t';
|
|
|
|
type uintptr generates 'TNode<UintPtrT>' constexpr 'uintptr_t';
|
|
|
|
type float32 generates 'TNode<Float32T>' constexpr 'float';
|
|
|
|
type float64 generates 'TNode<Float64T>' constexpr 'double';
|
|
|
|
type bool generates 'TNode<BoolT>' constexpr 'bool';
|
|
|
|
type bint generates 'TNode<BInt>' constexpr 'BInt';
|
|
|
|
type string constexpr 'const char*';
|
|
|
|
type RawPtr generates 'TNode<RawPtrT>' constexpr 'void*';
|
|
|
|
type Code extends HeapObject generates 'TNode<Code>';
|
|
|
|
type BuiltinPtr extends Smi generates 'TNode<BuiltinPtr>';
|
|
|
|
type Context extends HeapObject generates 'TNode<Context>';
|
|
|
|
type NativeContext extends Context;
|
|
|
|
)";
|
|
|
|
|
|
|
|
TorqueCompilerResult TestCompileTorque(std::string source) {
|
2019-05-02 07:54:08 +00:00
|
|
|
TorqueCompilerOptions options;
|
|
|
|
options.output_directory = "";
|
|
|
|
options.collect_language_server_data = false;
|
2019-05-08 08:26:07 +00:00
|
|
|
options.force_assert_statements = false;
|
2019-06-11 08:57:18 +00:00
|
|
|
options.v8_root = ".";
|
2019-05-02 07:54:08 +00:00
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
source = kTestTorquePrelude + source;
|
2019-05-02 07:54:08 +00:00
|
|
|
return CompileTorque(source, options);
|
|
|
|
}
|
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
void ExpectSuccessfulCompilation(std::string source) {
|
|
|
|
TorqueCompilerResult result = TestCompileTorque(std::move(source));
|
|
|
|
std::vector<std::string> messages;
|
|
|
|
for (const auto& message : result.messages) {
|
|
|
|
messages.push_back(message.message);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(messages, std::vector<std::string>{});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
void ExpectFailingCompilation(
|
|
|
|
std::string source, ::testing::PolymorphicMatcher<T> message_pattern) {
|
|
|
|
TorqueCompilerResult result = TestCompileTorque(std::move(source));
|
|
|
|
ASSERT_FALSE(result.messages.empty());
|
|
|
|
EXPECT_THAT(result.messages[0].message, message_pattern);
|
|
|
|
}
|
|
|
|
|
2019-05-02 07:54:08 +00:00
|
|
|
} // namespace
|
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
TEST(Torque, Prelude) { ExpectSuccessfulCompilation(""); }
|
|
|
|
|
2018-10-15 22:31:24 +00:00
|
|
|
TEST(Torque, StackDeleteRange) {
|
|
|
|
Stack<int> stack = {1, 2, 3, 4, 5, 6, 7};
|
|
|
|
stack.DeleteRange(StackRange{BottomOffset{2}, BottomOffset{4}});
|
|
|
|
Stack<int> result = {1, 2, 5, 6, 7};
|
|
|
|
ASSERT_TRUE(stack == result);
|
|
|
|
}
|
|
|
|
|
2019-04-24 13:25:27 +00:00
|
|
|
using ::testing::HasSubstr;
|
|
|
|
TEST(Torque, TypeNamingConventionLintError) {
|
2019-05-23 20:23:36 +00:00
|
|
|
ExpectFailingCompilation(R"(
|
2019-04-24 13:25:27 +00:00
|
|
|
type foo generates 'TNode<Foo>';
|
2019-05-23 20:23:36 +00:00
|
|
|
)",
|
|
|
|
HasSubstr("\"foo\""));
|
|
|
|
}
|
2019-04-24 13:25:27 +00:00
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
TEST(Torque, StructNamingConventionLintError) {
|
|
|
|
ExpectFailingCompilation(R"(
|
|
|
|
struct foo {}
|
|
|
|
)",
|
|
|
|
HasSubstr("\"foo\""));
|
|
|
|
}
|
2019-05-02 07:54:08 +00:00
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
TEST(Torque, ClassDefinition) {
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
|
|
|
extern class TestClassWithAllTypes extends HeapObject {
|
|
|
|
a: int8;
|
|
|
|
b: uint8;
|
|
|
|
b2: uint8;
|
|
|
|
b3: uint8;
|
|
|
|
c: int16;
|
|
|
|
d: uint16;
|
|
|
|
e: int32;
|
|
|
|
f: uint32;
|
|
|
|
g: RawPtr;
|
|
|
|
h: intptr;
|
|
|
|
i: uintptr;
|
|
|
|
}
|
|
|
|
|
2019-06-11 12:26:32 +00:00
|
|
|
@export
|
2019-05-23 20:23:36 +00:00
|
|
|
macro TestClassWithAllTypesLoadsAndStores(
|
|
|
|
t: TestClassWithAllTypes, r: RawPtr, v1: int8, v2: uint8, v3: int16,
|
|
|
|
v4: uint16, v5: int32, v6: uint32, v7: intptr, v8: uintptr) {
|
|
|
|
t.a = v1;
|
|
|
|
t.b = v2;
|
|
|
|
t.c = v3;
|
|
|
|
t.d = v4;
|
|
|
|
t.e = v5;
|
|
|
|
t.f = v6;
|
|
|
|
t.g = r;
|
|
|
|
t.h = v7;
|
|
|
|
t.i = v8;
|
|
|
|
t.a = t.a;
|
|
|
|
t.b = t.b;
|
|
|
|
t.c = t.c;
|
|
|
|
t.d = t.d;
|
|
|
|
t.e = t.e;
|
|
|
|
t.f = t.f;
|
|
|
|
t.g = t.g;
|
|
|
|
t.h = t.h;
|
|
|
|
t.i = t.i;
|
|
|
|
}
|
|
|
|
)");
|
2019-05-02 07:54:08 +00:00
|
|
|
}
|
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
TEST(Torque, TypeDeclarationOrder) {
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
|
|
|
type Baztype = Foo | FooType;
|
2019-05-23 09:06:15 +00:00
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
@abstract
|
|
|
|
extern class Foo extends HeapObject {
|
|
|
|
fooField: FooType;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern class Bar extends Foo {
|
|
|
|
barField: Bartype;
|
|
|
|
bazfield: Baztype;
|
|
|
|
}
|
2019-05-23 20:23:36 +00:00
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
type Bartype = FooType;
|
|
|
|
|
|
|
|
type FooType = Smi | Bar;
|
|
|
|
)");
|
|
|
|
}
|
2019-04-24 13:25:27 +00:00
|
|
|
|
2019-05-23 20:23:36 +00:00
|
|
|
TEST(Torque, ConditionalFields) {
|
|
|
|
// This class should throw alignment errors if @if decorators aren't
|
|
|
|
// working.
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
|
|
|
extern class PreprocessingTest extends HeapObject {
|
|
|
|
@if(FALSE_FOR_TESTING) a: int8;
|
|
|
|
@if(TRUE_FOR_TESTING) a: int16;
|
|
|
|
b: int16;
|
|
|
|
d: int32;
|
|
|
|
@ifnot(TRUE_FOR_TESTING) e: int8;
|
|
|
|
@ifnot(FALSE_FOR_TESTING) f: int16;
|
|
|
|
g: int16;
|
|
|
|
h: int32;
|
|
|
|
}
|
|
|
|
)");
|
|
|
|
ExpectFailingCompilation(R"(
|
|
|
|
extern class PreprocessingTest extends HeapObject {
|
|
|
|
@if(TRUE_FOR_TESTING) a: int8;
|
|
|
|
@if(FALSE_FOR_TESTING) a: int16;
|
|
|
|
b: int16;
|
|
|
|
d: int32;
|
|
|
|
@ifnot(FALSE_FOR_TESTING) e: int8;
|
|
|
|
@ifnot(TRUE_FOR_TESTING) f: int16;
|
|
|
|
g: int16;
|
|
|
|
h: int32;
|
|
|
|
}
|
|
|
|
)",
|
|
|
|
HasSubstr("aligned"));
|
2019-04-24 13:25:27 +00:00
|
|
|
}
|
|
|
|
|
2019-05-24 11:29:51 +00:00
|
|
|
TEST(Torque, ConstexprLetBindingDoesNotCrash) {
|
|
|
|
ExpectFailingCompilation(
|
2019-06-11 12:26:32 +00:00
|
|
|
R"(@export macro FooBar() { let foo = 0; check(foo >= 0); })",
|
2019-05-24 11:29:51 +00:00
|
|
|
HasSubstr("Use 'const' instead of 'let' for variable 'foo'"));
|
|
|
|
}
|
|
|
|
|
2019-06-06 12:41:27 +00:00
|
|
|
TEST(Torque, DoubleUnderScorePrefixIllegalForIdentifiers) {
|
|
|
|
ExpectFailingCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo() {
|
2019-06-06 12:41:27 +00:00
|
|
|
let __x;
|
|
|
|
}
|
|
|
|
)",
|
|
|
|
HasSubstr("Lexer Error"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, UnusedLetBindingLintError) {
|
|
|
|
ExpectFailingCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo(y: Smi) {
|
2019-06-06 12:41:27 +00:00
|
|
|
let x: Smi = y;
|
|
|
|
}
|
|
|
|
)",
|
|
|
|
HasSubstr("Variable 'x' is never used."));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, UnderscorePrefixSilencesUnusedWarning) {
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo(y: Smi) {
|
2019-06-06 12:41:27 +00:00
|
|
|
let _x: Smi = y;
|
|
|
|
}
|
|
|
|
)");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, UsingUnderscorePrefixedIdentifierError) {
|
|
|
|
ExpectFailingCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo(y: Smi) {
|
2019-06-06 12:41:27 +00:00
|
|
|
let _x: Smi = y;
|
|
|
|
check(_x == y);
|
|
|
|
}
|
|
|
|
)",
|
|
|
|
HasSubstr("Trying to reference '_x'"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, UnusedArgumentLintError) {
|
|
|
|
ExpectFailingCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo(x: Smi) {}
|
2019-06-06 12:41:27 +00:00
|
|
|
)",
|
|
|
|
HasSubstr("Variable 'x' is never used."));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, UsingUnderscorePrefixedArgumentSilencesWarning) {
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo(_y: Smi) {}
|
2019-06-06 12:41:27 +00:00
|
|
|
)");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, UnusedLabelLintError) {
|
|
|
|
ExpectFailingCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo() labels Bar {}
|
2019-06-06 12:41:27 +00:00
|
|
|
)",
|
|
|
|
HasSubstr("Label 'Bar' is never used."));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, UsingUnderScorePrefixLabelSilencesWarning) {
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo() labels _Bar {}
|
2019-06-06 12:41:27 +00:00
|
|
|
)");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, NoUnusedWarningForImplicitArguments) {
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo(implicit c: Context, r: JSReceiver)() {}
|
2019-06-06 12:41:27 +00:00
|
|
|
)");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, NoUnusedWarningForVariablesOnlyUsedInAsserts) {
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo(x: bool) {
|
2019-06-06 12:41:27 +00:00
|
|
|
assert(x);
|
|
|
|
}
|
|
|
|
)");
|
|
|
|
}
|
|
|
|
|
2019-06-11 08:57:18 +00:00
|
|
|
TEST(Torque, ImportNonExistentFile) {
|
|
|
|
ExpectFailingCompilation(R"(import "foo/bar.tq")",
|
|
|
|
HasSubstr("File 'foo/bar.tq' not found."));
|
|
|
|
}
|
|
|
|
|
2019-06-11 10:56:10 +00:00
|
|
|
TEST(Torque, LetShouldBeConstLintError) {
|
|
|
|
ExpectFailingCompilation(R"(
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Foo(y: Smi): Smi {
|
2019-06-11 10:56:10 +00:00
|
|
|
let x: Smi = y;
|
|
|
|
return x;
|
|
|
|
})",
|
|
|
|
HasSubstr("Variable 'x' is never assigned to."));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Torque, LetShouldBeConstIsSkippedForStructs) {
|
|
|
|
ExpectSuccessfulCompilation(R"(
|
|
|
|
struct Foo{ a: Smi; }
|
2019-06-11 12:26:32 +00:00
|
|
|
@export macro Bar(x: Smi): Foo {
|
2019-06-11 10:56:10 +00:00
|
|
|
let foo = Foo{a: x};
|
|
|
|
return foo;
|
|
|
|
}
|
|
|
|
)");
|
|
|
|
}
|
|
|
|
|
2018-10-15 22:31:24 +00:00
|
|
|
} // namespace torque
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|