From 0e2b709369a4c70fa1286edae1800c27378e4aea Mon Sep 17 00:00:00 2001 From: Molokanov Yaroslav Date: Tue, 7 Nov 2023 19:53:41 +0300 Subject: [PATCH] [ASTVerifier] Add arithmetic expression checker Signed-off-by: Molokanov Yaroslav --- ets2panda/CMakeLists.txt | 3 + ets2panda/compiler/core/ASTVerifier.cpp | 73 ++++++++++++++ ets2panda/compiler/core/ASTVerifier.h | 2 + ets2panda/ir/expressions/binaryExpression.h | 17 ++++ ets2panda/parser/parserImpl.cpp | 5 + ets2panda/parser/parserImpl.h | 4 +- ets2panda/test/CMakeLists.txt | 3 +- .../test/unit/public/ast_verifier_test.cpp | 94 ++++++++++++++++++- 8 files changed, 198 insertions(+), 3 deletions(-) diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 45978a26fe..cfadf11aed 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 fce72cee39..d4e3fe8b64 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 d5cbb7f2d5..117af97f12 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 69bdfa92f8..81c361b4e6 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 4a19f88427..1d631d932c 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 64a4b9b3f3..9f2ef98558 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 f30dfa56a4..906befaf5e 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 dd36a19379..5f1f6ed321 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 -- Gitee