diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 45978a26fe5155df778a079e034531ca39d33477..cfadf11aedf1075fa7d1432eb0f8199b6a63dc78 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -18,6 +18,9 @@ include(cmake/coverage.cmake) project (es2panda) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(ES2PANDA_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) +set(ES2PANDA_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}) + set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(GENERATED_DIR ${OUTPUT_DIR}/generated) set(GENERATED_STAMP ${OUTPUT_DIR}/gen_dir.stamp) diff --git a/ets2panda/compiler/core/ASTVerifier.cpp b/ets2panda/compiler/core/ASTVerifier.cpp index fce72cee39540ca260c2bfe99921bce78fc74bef..d4e3fe8b64fba74c30ab34af0f49dc158ed3ec5e 100644 --- a/ets2panda/compiler/core/ASTVerifier.cpp +++ b/ets2panda/compiler/core/ASTVerifier.cpp @@ -15,6 +15,7 @@ #include "ASTVerifier.h" +#include "checker/types/typeFlag.h" #include "es2panda.h" #include "varbinder/variableFlags.h" #include "varbinder/scope.h" @@ -47,6 +48,8 @@ #include "ir/ts/tsTypeParameter.h" #include "ir/ts/tsTypeParameterDeclaration.h" #include "ir/ts/tsTypeParameterInstantiation.h" +#include "ir/expressions/binaryExpression.h" +#include "lexer/token/tokenType.h" namespace panda::es2panda::compiler { @@ -406,4 +409,74 @@ bool ASTVerifier::HaveScopes(const ir::AstNode *ast) return has_scope; } +static bool IsNumericType(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (!ast->IsTyped()) { + return false; + } + + auto typed_ast = static_cast(ast); + + if (typed_ast->TsType() == nullptr) { + return false; + } + + return typed_ast->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC) || + typed_ast->TsType()->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || + typed_ast->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); +} + +static bool IsStringType(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (!ast->IsTyped()) { + return false; + } + + auto typed_ast = static_cast(ast); + + if (typed_ast->TsType() == nullptr) { + return false; + } + + return typed_ast->TsType()->HasTypeFlag(checker::TypeFlag::STRING_LIKE); +} + +bool ASTVerifier::CheckArithmeticExpr(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (ast->IsBinaryExpression() && ast->AsBinaryExpression()->IsArithmetic()) { + if (ast->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS && + IsStringType(ast->AsBinaryExpression()->Left()) && IsStringType(ast->AsBinaryExpression()->Right())) { + return true; + } + bool is_correct = true; + ast->Iterate([&is_correct](ir::AstNode *child) { is_correct &= (IsNumericType(child)); }); + return is_correct; + } + + return true; +} + +bool ASTVerifier::CheckArithmeticExpressions(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool is_correct = CheckArithmeticExpr(ast); + ast->IterateRecursively([this, &is_correct](ir::AstNode *child) { is_correct &= CheckArithmeticExpr(child); }); + return is_correct; +} + } // namespace panda::es2panda::compiler diff --git a/ets2panda/compiler/core/ASTVerifier.h b/ets2panda/compiler/core/ASTVerifier.h index d5cbb7f2d5923998392a6a02d4cc482d1424d013..117af97f124b1244531c41be2e3075ad2611421f 100644 --- a/ets2panda/compiler/core/ASTVerifier.h +++ b/ets2panda/compiler/core/ASTVerifier.h @@ -38,6 +38,8 @@ public: bool HasVariable(const ir::AstNode *ast); bool HasScope(const ir::AstNode *ast); bool HaveScopes(const ir::AstNode *ast); + bool CheckArithmeticExpressions(const ir::AstNode *ast); + bool CheckArithmeticExpr(const ir::AstNode *ast); ErrorMessages GetErrorMessages() { diff --git a/ets2panda/ir/expressions/binaryExpression.h b/ets2panda/ir/expressions/binaryExpression.h index 69bdfa92f80f1e02c5bbc025e2a116dd4ad525af..81c361b4e6356082cbe5651894ef7d0854a9d935 100644 --- a/ets2panda/ir/expressions/binaryExpression.h +++ b/ets2panda/ir/expressions/binaryExpression.h @@ -86,6 +86,23 @@ public: operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR; } + [[nodiscard]] bool IsArithmetic() const noexcept + { + return operator_ == lexer::TokenType::PUNCTUATOR_PLUS || operator_ == lexer::TokenType::PUNCTUATOR_MINUS || + operator_ == lexer::TokenType::PUNCTUATOR_MULTIPLY || operator_ == lexer::TokenType::PUNCTUATOR_DIVIDE || + operator_ == lexer::TokenType::PUNCTUATOR_MOD || operator_ == lexer::TokenType::PUNCTUATOR_BITWISE_OR || + operator_ == lexer::TokenType::PUNCTUATOR_BITWISE_XOR || + operator_ == lexer::TokenType::PUNCTUATOR_BITWISE_AND || + operator_ == lexer::TokenType::PUNCTUATOR_PLUS_EQUAL || + operator_ == lexer::TokenType::PUNCTUATOR_MINUS_EQUAL || + operator_ == lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL || + operator_ == lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL || + operator_ == lexer::TokenType::PUNCTUATOR_MOD_EQUAL || + operator_ == lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL || + operator_ == lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL || + operator_ == lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL; + } + void SetLeft(Expression *expr) noexcept { left_ = expr; diff --git a/ets2panda/parser/parserImpl.cpp b/ets2panda/parser/parserImpl.cpp index 4a19f8842780e4436933f767f7238451e440ca40..1d631d932c802391fa47b975918abdfe52582930 100644 --- a/ets2panda/parser/parserImpl.cpp +++ b/ets2panda/parser/parserImpl.cpp @@ -1206,6 +1206,11 @@ void ParserImpl::ThrowSyntaxError(std::string_view error_message, const lexer::S throw Error {ErrorType::SYNTAX, program_->SourceFile().Utf8(), error_message, loc.line, loc.col}; } +void ParserImpl::ThrowAllocationError(std::string_view message) const +{ + throw Error(ErrorType::GENERIC, program_->SourceFile().Utf8(), message); +} + ScriptExtension ParserImpl::Extension() const { return program_->Extension(); diff --git a/ets2panda/parser/parserImpl.h b/ets2panda/parser/parserImpl.h index 64a4b9b3f35df75a4cd4acf6e6a263ac56a5e0d4..9f2ef98558e31fde96128dfc6dd018c0ed699aec 100644 --- a/ets2panda/parser/parserImpl.h +++ b/ets2panda/parser/parserImpl.h @@ -213,6 +213,8 @@ protected: ir::MethodDefinition *last_overload, bool impl_exists, bool is_abstract = false); + void ThrowAllocationError(std::string_view message) const; + void ValidateAccessor(ExpressionParseFlags flags, lexer::TokenFlags current_token_flags); void CheckPropertyKeyAsyncModifier(ParserStatus *method_status); ir::Property *ParseShorthandProperty(const lexer::LexerPosition *start_pos); @@ -281,7 +283,7 @@ protected: { auto *ret = program_->Allocator()->New(std::forward(args)...); if (ret == nullptr) { - throw Error(ErrorType::GENERIC, program_->SourceFile().Utf8(), "Unsuccessful allocation during parsing"); + ThrowAllocationError("Unsuccessful allocation during parsing"); } return ret; diff --git a/ets2panda/test/CMakeLists.txt b/ets2panda/test/CMakeLists.txt index f30dfa56a469d7f307108abfd063d66a51b2dbeb..906befaf5e3d68eb9f4e8c2909d2871dff24e851 100644 --- a/ets2panda/test/CMakeLists.txt +++ b/ets2panda/test/CMakeLists.txt @@ -82,7 +82,7 @@ if(PANDA_WITH_ETS) LIBRARIES es2panda-public es2panda-lib arkassembler arkbytecodeopt INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${ES2PANDA_ROOT} SANITIZERS ${PANDA_SANITIZERS_LIST} ) @@ -114,6 +114,7 @@ if(PANDA_WITH_ETS) es2panda-lib INCLUDE_DIRS ${ES2PANDA_PATH} + ${ES2PANDA_BINARY_ROOT} SANITIZERS ${PANDA_SANITIZERS_LIST} ) diff --git a/ets2panda/test/unit/public/ast_verifier_test.cpp b/ets2panda/test/unit/public/ast_verifier_test.cpp index dd36a1937988cfe8ea1ae3622f6bfae857a57f81..5f1f6ed321b93e03bbcec714004d5205cf411d96 100644 --- a/ets2panda/test/unit/public/ast_verifier_test.cpp +++ b/ets2panda/test/unit/public/ast_verifier_test.cpp @@ -15,21 +15,42 @@ #include #include +#include "ir/expressions/literals/booleanLiteral.h" +#include "ir/expressions/literals/numberLiteral.h" #include "macros.h" #include "compiler/core/ASTVerifier.h" +#include "parser/ETSparser.h" #include "ir/astDump.h" #include "ir/expressions/literals/stringLiteral.h" +#include "checker/ETSchecker.h" +#include "varbinder/ETSBinder.h" class ASTVerifierTest : public testing::Test { public: - ASTVerifierTest() = default; + ASTVerifierTest() + { + allocator_ = std::make_unique(panda::SpaceType::SPACE_TYPE_COMPILER); + } ~ASTVerifierTest() override = default; + static void SetUpTestCase() + { + constexpr auto COMPILER_SIZE = panda::operator""_MB(256ULL); + panda::mem::MemConfig::Initialize(0, 0, COMPILER_SIZE, 0, 0, 0); + panda::PoolManager::Initialize(); + } + + panda::ArenaAllocator *GetAllocator() + { + return allocator_.get(); + } + NO_COPY_SEMANTIC(ASTVerifierTest); NO_MOVE_SEMANTIC(ASTVerifierTest); private: + std::unique_ptr allocator_; }; TEST_F(ASTVerifierTest, NullParent) @@ -69,3 +90,74 @@ TEST_F(ASTVerifierTest, WithoutScope) ASSERT_EQ(has_scope, true); ASSERT_EQ(messages.size(), 0); } + +TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect1) +{ + panda::es2panda::checker::ETSChecker etschecker {}; + panda::es2panda::compiler::ASTVerifier verifier {}; + + auto left = panda::es2panda::ir::NumberLiteral(panda::es2panda::lexer::Number {1}); + auto right = panda::es2panda::ir::NumberLiteral(panda::es2panda::lexer::Number {6}); + auto arithmetic_expression = + panda::es2panda::ir::BinaryExpression(&left, &right, panda::es2panda::lexer::TokenType::PUNCTUATOR_PLUS); + + left.SetTsType(etschecker.GlobalIntType()); + right.SetTsType(etschecker.GlobalIntType()); + + bool is_correct = verifier.CheckArithmeticExpressions(&arithmetic_expression); + ASSERT_EQ(is_correct, true); +} + +TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect2) +{ + panda::es2panda::checker::ETSChecker etschecker {}; + panda::es2panda::compiler::ASTVerifier verifier {}; + + auto left1 = panda::es2panda::ir::NumberLiteral(panda::es2panda::lexer::Number {1}); + auto left2 = panda::es2panda::ir::NumberLiteral(panda::es2panda::lexer::Number {12}); + auto right2 = panda::es2panda::ir::NumberLiteral(panda::es2panda::lexer::Number {6}); + auto right1 = + panda::es2panda::ir::BinaryExpression(&left2, &right2, panda::es2panda::lexer::TokenType::PUNCTUATOR_MULTIPLY); + auto arithmetic_expression = + panda::es2panda::ir::BinaryExpression(&left1, &right1, panda::es2panda::lexer::TokenType::PUNCTUATOR_PLUS); + + left1.SetTsType(etschecker.GlobalIntType()); + right1.SetTsType(etschecker.GlobalIntType()); + left2.SetTsType(etschecker.GlobalIntType()); + right2.SetTsType(etschecker.GlobalIntType()); + + bool is_correct = verifier.CheckArithmeticExpressions(&arithmetic_expression); + ASSERT_EQ(is_correct, true); +} + +TEST_F(ASTVerifierTest, ArithmeticExpressionNegative1) +{ + panda::es2panda::checker::ETSChecker etschecker {}; + panda::es2panda::compiler::ASTVerifier verifier {}; + auto left = panda::es2panda::ir::StringLiteral("1"); + auto right = panda::es2panda::ir::NumberLiteral(panda::es2panda::lexer::Number {1}); + auto arithmetic_expression = + panda::es2panda::ir::BinaryExpression(&left, &right, panda::es2panda::lexer::TokenType::PUNCTUATOR_DIVIDE); + + left.SetTsType(etschecker.GlobalETSStringLiteralType()); + right.SetTsType(etschecker.GlobalIntType()); + + bool is_correct = verifier.CheckArithmeticExpressions(&arithmetic_expression); + ASSERT_EQ(is_correct, false); +} + +TEST_F(ASTVerifierTest, ArithmeticExpressionNegative2) +{ + panda::es2panda::checker::ETSChecker etschecker {}; + panda::es2panda::compiler::ASTVerifier verifier {}; + auto left = panda::es2panda::ir::BooleanLiteral(true); + auto right = panda::es2panda::ir::NumberLiteral(panda::es2panda::lexer::Number {1}); + auto arithmetic_expression = + panda::es2panda::ir::BinaryExpression(&left, &right, panda::es2panda::lexer::TokenType::PUNCTUATOR_DIVIDE); + + left.SetTsType(etschecker.GlobalETSStringLiteralType()); + right.SetTsType(etschecker.GlobalIntType()); + + bool is_correct = verifier.CheckArithmeticExpressions(&arithmetic_expression); + ASSERT_EQ(is_correct, false); +} \ No newline at end of file