// 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. #include "src/torque/torque-compiler.h" #include "src/torque/utils.h" #include "test/unittests/test-utils.h" #include "testing/gmock-support.h" namespace v8 { namespace internal { namespace torque { namespace { // 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' constexpr 'ObjectPtr'; type Smi extends Tagged generates 'TNode' constexpr 'Smi'; @abstract extern class HeapObject extends Tagged { map: Map; } type Map extends HeapObject generates 'TNode'; type Object = Smi | HeapObject; type JSReceiver extends HeapObject generates 'TNode'; type JSObject extends JSReceiver generates 'TNode'; type int32 generates 'TNode' constexpr 'int32_t'; type uint32 generates 'TNode' constexpr 'uint32_t'; type int31 extends int32 generates 'TNode' constexpr 'int31_t'; type uint31 extends uint32 generates 'TNode' constexpr 'uint31_t'; type int16 extends int31 generates 'TNode' constexpr 'int16_t'; type uint16 extends uint31 generates 'TNode' constexpr 'uint16_t'; type int8 extends int16 generates 'TNode' constexpr 'int8_t'; type uint8 extends uint16 generates 'TNode' constexpr 'uint8_t'; type int64 generates 'TNode' constexpr 'int64_t'; type intptr generates 'TNode' constexpr 'intptr_t'; type uintptr generates 'TNode' constexpr 'uintptr_t'; type float32 generates 'TNode' constexpr 'float'; type float64 generates 'TNode' constexpr 'double'; type bool generates 'TNode' constexpr 'bool'; type bint generates 'TNode' constexpr 'BInt'; type string constexpr 'const char*'; type RawPtr generates 'TNode' constexpr 'void*'; type Code extends HeapObject generates 'TNode'; type BuiltinPtr extends Smi generates 'TNode'; type Context extends HeapObject generates 'TNode'; type NativeContext extends Context; )"; TorqueCompilerResult TestCompileTorque(std::string source) { TorqueCompilerOptions options; options.output_directory = ""; options.collect_language_server_data = false; options.force_assert_statements = false; options.v8_root = "."; source = kTestTorquePrelude + source; return CompileTorque(source, options); } void ExpectSuccessfulCompilation(std::string source) { TorqueCompilerResult result = TestCompileTorque(std::move(source)); std::vector messages; for (const auto& message : result.messages) { messages.push_back(message.message); } EXPECT_EQ(messages, std::vector{}); } template void ExpectFailingCompilation( std::string source, ::testing::PolymorphicMatcher message_pattern) { TorqueCompilerResult result = TestCompileTorque(std::move(source)); ASSERT_FALSE(result.messages.empty()); EXPECT_THAT(result.messages[0].message, message_pattern); } } // namespace TEST(Torque, Prelude) { ExpectSuccessfulCompilation(""); } TEST(Torque, StackDeleteRange) { Stack stack = {1, 2, 3, 4, 5, 6, 7}; stack.DeleteRange(StackRange{BottomOffset{2}, BottomOffset{4}}); Stack result = {1, 2, 5, 6, 7}; ASSERT_TRUE(stack == result); } using ::testing::HasSubstr; TEST(Torque, TypeNamingConventionLintError) { ExpectFailingCompilation(R"( type foo generates 'TNode'; )", HasSubstr("\"foo\"")); } TEST(Torque, StructNamingConventionLintError) { ExpectFailingCompilation(R"( struct foo {} )", HasSubstr("\"foo\"")); } 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; } @export 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; } )"); } TEST(Torque, TypeDeclarationOrder) { ExpectSuccessfulCompilation(R"( type Baztype = Foo | FooType; @abstract @noVerifier extern class Foo extends HeapObject { fooField: FooType; } @noVerifier extern class Bar extends Foo { barField: Bartype; bazfield: Baztype; } type Bartype = FooType; type FooType = Smi | Bar; )"); } TEST(Torque, ConditionalFields) { // This class should throw alignment errors if @if decorators aren't // working. ExpectSuccessfulCompilation(R"( @noVerifier 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"( @noVerifier 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")); } TEST(Torque, ConstexprLetBindingDoesNotCrash) { ExpectFailingCompilation( R"(@export macro FooBar() { let foo = 0; check(foo >= 0); })", HasSubstr("Use 'const' instead of 'let' for variable 'foo'")); } TEST(Torque, DoubleUnderScorePrefixIllegalForIdentifiers) { ExpectFailingCompilation(R"( @export macro Foo() { let __x; } )", HasSubstr("Lexer Error")); } TEST(Torque, UnusedLetBindingLintError) { ExpectFailingCompilation(R"( @export macro Foo(y: Smi) { let x: Smi = y; } )", HasSubstr("Variable 'x' is never used.")); } TEST(Torque, UnderscorePrefixSilencesUnusedWarning) { ExpectSuccessfulCompilation(R"( @export macro Foo(y: Smi) { let _x: Smi = y; } )"); } TEST(Torque, UsingUnderscorePrefixedIdentifierError) { ExpectFailingCompilation(R"( @export macro Foo(y: Smi) { let _x: Smi = y; check(_x == y); } )", HasSubstr("Trying to reference '_x'")); } TEST(Torque, UnusedArgumentLintError) { ExpectFailingCompilation(R"( @export macro Foo(x: Smi) {} )", HasSubstr("Variable 'x' is never used.")); } TEST(Torque, UsingUnderscorePrefixedArgumentSilencesWarning) { ExpectSuccessfulCompilation(R"( @export macro Foo(_y: Smi) {} )"); } TEST(Torque, UnusedLabelLintError) { ExpectFailingCompilation(R"( @export macro Foo() labels Bar {} )", HasSubstr("Label 'Bar' is never used.")); } TEST(Torque, UsingUnderScorePrefixLabelSilencesWarning) { ExpectSuccessfulCompilation(R"( @export macro Foo() labels _Bar {} )"); } TEST(Torque, NoUnusedWarningForImplicitArguments) { ExpectSuccessfulCompilation(R"( @export macro Foo(implicit c: Context, r: JSReceiver)() {} )"); } TEST(Torque, NoUnusedWarningForVariablesOnlyUsedInAsserts) { ExpectSuccessfulCompilation(R"( @export macro Foo(x: bool) { assert(x); } )"); } TEST(Torque, ImportNonExistentFile) { ExpectFailingCompilation(R"(import "foo/bar.tq")", HasSubstr("File 'foo/bar.tq' not found.")); } TEST(Torque, LetShouldBeConstLintError) { ExpectFailingCompilation(R"( @export macro Foo(y: Smi): Smi { let x: Smi = y; return x; })", HasSubstr("Variable 'x' is never assigned to.")); } TEST(Torque, LetShouldBeConstIsSkippedForStructs) { ExpectSuccessfulCompilation(R"( struct Foo{ a: Smi; } @export macro Bar(x: Smi): Foo { let foo = Foo{a: x}; return foo; } )"); } } // namespace torque } // namespace internal } // namespace v8