From 8402c743369d9923c38a39fbaa60228328597ff5 Mon Sep 17 00:00:00 2001 From: Amosov Alexey Date: Sun, 10 Dec 2023 12:47:19 +0300 Subject: [PATCH] fix IsContainedIn in AstVerifier, added internal check Signed-off-by: Amosov Alexey --- ets2panda/compiler/core/ASTVerifier.cpp | 104 ++- ets2panda/test/CMakeLists.txt | 2 +- .../test/unit/public/ast_verifier_test.cpp | 773 +++++++++++++++++- 3 files changed, 814 insertions(+), 65 deletions(-) diff --git a/ets2panda/compiler/core/ASTVerifier.cpp b/ets2panda/compiler/core/ASTVerifier.cpp index 60cca9a8de..e56eb60e96 100644 --- a/ets2panda/compiler/core/ASTVerifier.cpp +++ b/ets2panda/compiler/core/ASTVerifier.cpp @@ -18,6 +18,8 @@ #include "checker/types/typeFlag.h" #include "ir/astNode.h" #include "ir/base/classDefinition.h" +#include "ir/base/classElement.h" +#include "ir/statement.h" #include "ir/base/classStaticBlock.h" #include "ir/base/methodDefinition.h" #include "ir/base/scriptFunction.h" @@ -27,6 +29,7 @@ #include "ir/ets/etsTypeReference.h" #include "ir/ets/etsTypeReferencePart.h" #include "ir/ets/etsImportDeclaration.h" +#include "ir/ets/etsScript.h" #include "ir/module/importSpecifier.h" #include "ir/module/importNamespaceSpecifier.h" #include "ir/module/importDefaultSpecifier.h" @@ -120,23 +123,39 @@ bool IsContainedIn(const T *child, const T *parent) std::unordered_set saved_nodes; while (child != nullptr && child != parent) { + saved_nodes.emplace(child); + child = child->Parent(); if (saved_nodes.find(child) != saved_nodes.end()) { return false; } - child = child->Parent(); - saved_nodes.emplace(child); } return child == parent; } - +bool IsVisibleInternalNode(const ir::AstNode *ast, const ir::AstNode *obj_type_decl_node) +{ + auto *current_top_statement = (static_cast(ast->GetTopStatement())); + auto *current_program = current_top_statement->Program(); + if (current_program == nullptr) { + return false; + } + util::StringView package_name_current = current_program->GetPackageName(); + auto *object_top_statement = (static_cast(obj_type_decl_node->GetTopStatement())); + auto *object_program = object_top_statement->Program(); + if (object_program == nullptr) { + return false; + } + util::StringView package_name_object = object_program->GetPackageName(); + return current_top_statement == object_top_statement || + (package_name_current == package_name_object && !package_name_current.Empty()); +} bool ValidateVariableAccess(const varbinder::LocalVariable *prop_var, const ir::MemberExpression *ast) { - const auto *decl = prop_var->Declaration(); - if (decl == nullptr) { + const auto *prop_var_decl = prop_var->Declaration(); + if (prop_var_decl == nullptr) { return false; } - const auto *node = decl->Node(); - if (node == nullptr) { + const auto *prop_var_decl_node = prop_var_decl->Node(); + if (prop_var_decl_node == nullptr) { return false; } auto *obj_type = ast->ObjType(); @@ -147,36 +166,43 @@ bool ValidateVariableAccess(const varbinder::LocalVariable *prop_var, const ir:: if (obj_type_decl_node == nullptr) { return false; } - const auto *parent_node = node->Parent(); - if (parent_node != nullptr && parent_node->IsClassDefinition() && obj_type_decl_node->IsClassDefinition()) { - if (IsContainedIn(ast, obj_type_decl_node->AsClassDefinition())) { + const auto *prop_var_decl_node_parent = prop_var_decl_node->Parent(); + if (prop_var_decl_node_parent != nullptr && prop_var_decl_node_parent->IsClassDefinition() && + obj_type_decl_node->IsClassDefinition()) { + // Check if the variable is used where it is declared + if (IsContainedIn(ast, prop_var_decl_node_parent->AsClassDefinition())) { return true; } - if (node->IsPrivate() && parent_node == obj_type_decl_node) { - return true; + if (prop_var_decl_node->IsPrivate()) { + return false; } - if (node->IsProtected()) { + if (prop_var_decl_node->IsProtected()) { + // Check if the variable is inherited and is used in class in which it is inherited auto ret = obj_type->IsPropertyInherited(prop_var); - return ret; + return ret && IsContainedIn(ast, obj_type_decl_node->AsClassDefinition()); + } + if (prop_var_decl_node->IsInternal()) { + return IsVisibleInternalNode(ast, obj_type_decl_node); } + return true; } return false; } bool ValidateMethodAccess(const ir::MemberExpression *member_expression, const ir::CallExpression *ast) { - auto *obj_type = member_expression->ObjType(); - if (obj_type == nullptr) { + auto *member_obj_type = member_expression->ObjType(); + if (member_obj_type == nullptr) { return false; } - - if (obj_type->HasObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER) && obj_type->SuperType() != nullptr && - obj_type->SuperType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | - checker::ETSObjectFlags::GLOBAL_CLASS)) { + if (member_obj_type->HasObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER) && + member_obj_type->SuperType() != nullptr && + member_obj_type->SuperType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | + checker::ETSObjectFlags::GLOBAL)) { return true; } - const auto *decl_node = obj_type->GetDeclNode(); - if (decl_node == nullptr) { + const auto *member_obj_type_decl_node = member_obj_type->GetDeclNode(); + if (member_obj_type_decl_node == nullptr) { return false; } auto *signature = ast->Signature(); @@ -187,18 +213,25 @@ bool ValidateMethodAccess(const ir::MemberExpression *member_expression, const i if (owner_sign == nullptr) { return false; } - auto *decl_node_sign = owner_sign->GetDeclNode(); - if (decl_node_sign != nullptr && decl_node_sign->IsClassDefinition() && decl_node->IsClassDefinition()) { - if (IsContainedIn(ast, decl_node->AsClassDefinition())) { + auto *owner_sign_decl_node = owner_sign->GetDeclNode(); + if (owner_sign_decl_node != nullptr && owner_sign_decl_node->IsClassDefinition() && + member_obj_type_decl_node->IsClassDefinition()) { + // Check if the method is used where it is declared + if (IsContainedIn(ast, owner_sign_decl_node->AsClassDefinition())) { return true; } - if (signature->HasSignatureFlag(checker::SignatureFlags::PRIVATE) && decl_node_sign == decl_node) { - return true; + if (signature->HasSignatureFlag(checker::SignatureFlags::PRIVATE)) { + return false; } if (signature->HasSignatureFlag(checker::SignatureFlags::PROTECTED)) { - auto ret = obj_type->IsSignatureInherited(signature); - return ret; + // Check if the method is inherited and is used in class in which it is inherited + auto ret = member_obj_type->IsSignatureInherited(signature); + return ret && IsContainedIn(ast, member_obj_type_decl_node->AsClassDefinition()); + } + if (signature->HasSignatureFlag(checker::SignatureFlags::INTERNAL)) { + return IsVisibleInternalNode(ast, member_obj_type_decl_node); } + return true; } return false; } @@ -689,8 +722,8 @@ bool ASTVerifier::VerifyModifierAccess(const ir::AstNode *ast) } if (ast->IsMemberExpression()) { const auto *prop_var = ast->AsMemberExpression()->PropVar(); - if (prop_var != nullptr && prop_var->HasFlag(varbinder::VariableFlags::PROPERTY) && - !ValidateVariableAccess(prop_var, ast->AsMemberExpression())) { + if (prop_var == nullptr || (prop_var->HasFlag(varbinder::VariableFlags::PROPERTY) && + !ValidateVariableAccess(prop_var, ast->AsMemberExpression()))) { AddError("PROPERTY_NOT_VISIBLE_HERE: " + ToStringHelper(ast), ast->Start()); return false; } @@ -698,11 +731,14 @@ bool ASTVerifier::VerifyModifierAccess(const ir::AstNode *ast) if (ast->IsCallExpression()) { const auto *call_expr = ast->AsCallExpression(); const auto *callee = call_expr->Callee(); - if (callee != nullptr && callee->IsMemberExpression()) { + if (callee == nullptr) { + return false; + } + if (callee->IsMemberExpression()) { const auto *callee_member = callee->AsMemberExpression(); const auto *prop_var_callee = callee_member->PropVar(); - if (prop_var_callee != nullptr && prop_var_callee->HasFlag(varbinder::VariableFlags::METHOD) && - !ValidateMethodAccess(callee_member, ast->AsCallExpression())) { + if (prop_var_callee == nullptr || (prop_var_callee->HasFlag(varbinder::VariableFlags::METHOD) && + !ValidateMethodAccess(callee_member, ast->AsCallExpression()))) { AddError("PROPERTY_NOT_VISIBLE_HERE: " + ToStringHelper(callee), callee->Start()); return false; } diff --git a/ets2panda/test/CMakeLists.txt b/ets2panda/test/CMakeLists.txt index 906befaf5e..a973d61bb6 100644 --- a/ets2panda/test/CMakeLists.txt +++ b/ets2panda/test/CMakeLists.txt @@ -111,7 +111,7 @@ if(PANDA_WITH_ETS) SOURCES unit/public/ast_verifier_test.cpp LIBRARIES - es2panda-lib + es2panda-public es2panda-lib INCLUDE_DIRS ${ES2PANDA_PATH} ${ES2PANDA_BINARY_ROOT} diff --git a/ets2panda/test/unit/public/ast_verifier_test.cpp b/ets2panda/test/unit/public/ast_verifier_test.cpp index 77acdc5129..83d414bb0d 100644 --- a/ets2panda/test/unit/public/ast_verifier_test.cpp +++ b/ets2panda/test/unit/public/ast_verifier_test.cpp @@ -22,6 +22,7 @@ #include "macros.h" #include "parser/ETSparser.h" #include "varbinder/ETSBinder.h" +#include "public/es2panda_lib.h" #include #include @@ -42,41 +43,44 @@ }()) // NOLINTEND(cppcoreguidelines-macro-usage) -namespace panda::es2panda { - class ASTVerifierTest : public testing::Test { public: ASTVerifierTest() { - allocator_ = std::make_unique(SpaceType::SPACE_TYPE_COMPILER); + impl_ = es2panda_GetImpl(ES2PANDA_LIB_VERSION); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + char const *argv[] = {"test"}; + cfg_ = impl_->CreateConfig(1, argv); + allocator_ = new panda::ArenaAllocator(panda::SpaceType::SPACE_TYPE_COMPILER); } - ~ASTVerifierTest() override = default; - - static void SetUpTestCase() + ~ASTVerifierTest() override { - constexpr auto COMPILER_SIZE = operator""_MB(256ULL); - mem::MemConfig::Initialize(0, 0, COMPILER_SIZE, 0, 0, 0); - PoolManager::Initialize(); + delete allocator_; + impl_->DestroyConfig(cfg_); } - ArenaAllocator *Allocator() + panda::ArenaAllocator *Allocator() { - return allocator_.get(); + return allocator_; } NO_COPY_SEMANTIC(ASTVerifierTest); NO_MOVE_SEMANTIC(ASTVerifierTest); -private: - std::unique_ptr allocator_; +protected: + // NOLINTBEGIN(misc-non-private-member-variables-in-classes) + es2panda_Impl const *impl_; + es2panda_Config *cfg_; + panda::ArenaAllocator *allocator_; + // NOLINTEND(misc-non-private-member-variables-in-classes) }; TEST_F(ASTVerifierTest, NullParent) { - compiler::ASTVerifier verifier {Allocator()}; - ir::StringLiteral empty_node; + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + panda::es2panda::ir::StringLiteral empty_node; - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("HasParent"); bool has_parent = verifier.Verify(&empty_node, checks); const auto &errors = verifier.GetErrors(); @@ -90,10 +94,10 @@ TEST_F(ASTVerifierTest, NullParent) TEST_F(ASTVerifierTest, NullType) { - compiler::ASTVerifier verifier {Allocator()}; - ir::StringLiteral empty_node; + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + panda::es2panda::ir::StringLiteral empty_node; - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("HasType"); bool has_type = verifier.Verify(&empty_node, checks); const auto &errors = verifier.GetErrors(); @@ -107,10 +111,10 @@ TEST_F(ASTVerifierTest, NullType) TEST_F(ASTVerifierTest, WithoutScope) { - compiler::ASTVerifier verifier {Allocator()}; - ir::StringLiteral empty_node; + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + panda::es2panda::ir::StringLiteral empty_node; - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("HasScope"); bool has_scope = verifier.Verify(&empty_node, checks); const auto &errors = verifier.GetErrors(); @@ -135,7 +139,7 @@ TEST_F(ASTVerifierTest, ScopeTest) local.SetScope(&scope); - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("HasScope"); bool is_ok = verifier.Verify(&ident, checks); @@ -159,7 +163,7 @@ TEST_F(ASTVerifierTest, ScopeNodeTest) local.SetScope(&scope); - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("VerifyScopeNode"); bool is_ok = verifier.Verify(&ident, checks); @@ -181,7 +185,7 @@ TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect1) left.SetTsType(etschecker.GlobalIntType()); right.SetTsType(etschecker.GlobalIntType()); - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("CheckArithmeticExpression"); bool is_correct = verifier.Verify(arithmetic_expression.AsBinaryExpression(), checks); ASSERT_EQ(is_correct, true); @@ -210,7 +214,7 @@ TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect2) left2.SetTsType(etschecker.GlobalIntType()); right2.SetTsType(etschecker.GlobalIntType()); - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("CheckArithmeticExpression"); bool is_correct = verifier.Verify(arithmetic_expression.AsBinaryExpression(), checks); ASSERT_EQ(is_correct, true); @@ -223,7 +227,7 @@ TEST_F(ASTVerifierTest, ArithmeticExpressionNegative1) auto program = panda::es2panda::parser::Program::NewProgram(Allocator()); auto parser = panda::es2panda::parser::ETSParser(&program, panda::es2panda::CompilerOptions {}); - const util::StringView left_param("1"); + const panda::es2panda::util::StringView left_param("1"); constexpr uint32_t RIGHT_PARAM = 1; auto left = panda::es2panda::ir::StringLiteral(left_param); auto right = panda::es2panda::ir::NumberLiteral(panda::es2panda::lexer::Number {RIGHT_PARAM}); @@ -233,7 +237,7 @@ TEST_F(ASTVerifierTest, ArithmeticExpressionNegative1) left.SetTsType(etschecker.GlobalETSStringLiteralType()); right.SetTsType(etschecker.GlobalIntType()); - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("CheckArithmeticExpression"); bool is_correct = verifier.Verify(arithmetic_expression.AsBinaryExpression(), checks); @@ -254,11 +258,720 @@ TEST_F(ASTVerifierTest, ArithmeticExpressionNegative2) left.SetTsType(etschecker.GlobalETSStringLiteralType()); right.SetTsType(etschecker.GlobalIntType()); - auto checks = compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; checks.insert("CheckArithmeticExpression"); bool is_correct = verifier.Verify(arithmetic_expression.AsBinaryExpression(), checks); ASSERT_EQ(is_correct, false); } -} // namespace panda::es2panda +constexpr char const *PRIVATE_PROTECTED_PUBLIC_TEST = + R"XXX( + class Base { + public a: int = 1; + protected b: int = 2; + private c: int = 3; + public publicMethod() { + this.a = 4; + this.protectedMethod(); + this.privateMethod(); + } + protected protectedMethod() { + this.b = 5; + this.publicMethod(); + this.privateMethod(); + } + private privateMethod() { + this.c = 6; + this.publicMethod(); + this.protectedMethod(); + } + } + class Derived extends Base { + foo () { + this.a = 7; + this.b = 8; + this.publicMethod(); + this.protectedMethod(); + } + } + function main(): void { + let base: Base = new Base(); + let a = base.a; + base.publicMethod(); + let derived1: Derived = new Derived(); + let b = derived1.a; + derived1.publicMethod(); + derived1.foo(); + let derived2: Base = new Derived(); + let c = derived2.a; + derived2.publicMethod(); + } + )XXX"; + +TEST_F(ASTVerifierTest, PrivateProtectedPublicAccessTestCorrect) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, PRIVATE_PROTECTED_PUBLIC_TEST, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + + ASSERT_EQ(is_correct, true); + ASSERT_EQ(errors.size(), 0); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, PrivateAccessTestNegative1) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + } + class Derived extends Base { + public b: int = this.a; + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[1] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[0] + ->AsClassProperty() + ->AddModifier(panda::es2panda::ir::ModifierFlags::PRIVATE); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR MUST BE UNREACHABLE.ID a"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, PrivateAccessTestNegative2) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + } + function main(): void { + let base: Base = new Base(); + let a = base.a; + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[1] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[0] + ->AsClassProperty() + ->AddModifier(panda::es2panda::ir::ModifierFlags::PRIVATE); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID base.ID a"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, PrivateAccessTestNegative3) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + } + class Derived extends Base {} + function main(): void { + let derived: Derived = new Derived(); + let a = derived.a; + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[1] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[0] + ->AsClassProperty() + ->AddModifier(panda::es2panda::ir::ModifierFlags::PRIVATE); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID derived.ID a"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, PrivateAccessTestNegative4) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + } + class Derived extends Base {} + function main(): void { + let derived: Base = new Derived(); + let a = derived.a; + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[1] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[0] + ->AsClassProperty() + ->AddModifier(panda::es2panda::ir::ModifierFlags::PRIVATE); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID derived.ID a"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, PrivateAccessTestNegative5) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + public privateMethod() { + this.a = 2; + } + } + function main(): void { + let base: Base = new Base(); + base.privateMethod(); + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[0] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[1] + ->AsClassElement() + ->Value() + ->AsFunctionExpression() + ->Function() + ->AsScriptFunction() + ->Body() + ->AsBlockStatement() + ->Statements()[1] + ->AsExpressionStatement() + ->GetExpression() + ->AsCallExpression() + ->Signature() + ->AddSignatureFlag(panda::es2panda::checker::SignatureFlags::PRIVATE); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID base.ID privateMethod"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, PrivateAccessTestNegative6) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + public privateMethod() { + this.a = 2; + } + } + class Derived extends Base {} + function main(): void { + let derived: Derived = new Derived(); + derived.privateMethod(); + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[0] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[1] + ->AsClassElement() + ->Value() + ->AsFunctionExpression() + ->Function() + ->AsScriptFunction() + ->Body() + ->AsBlockStatement() + ->Statements()[1] + ->AsExpressionStatement() + ->GetExpression() + ->AsCallExpression() + ->Signature() + ->AddSignatureFlag(panda::es2panda::checker::SignatureFlags::PRIVATE); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID derived.ID privateMethod"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, PrivateAccessTestNegative7) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + public privateMethod() { + this.a = 2; + } + } + class Derived extends Base {} + function main(): void { + let derived: Base = new Derived(); + derived.privateMethod(); + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[0] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[1] + ->AsClassElement() + ->Value() + ->AsFunctionExpression() + ->Function() + ->AsScriptFunction() + ->Body() + ->AsBlockStatement() + ->Statements()[1] + ->AsExpressionStatement() + ->GetExpression() + ->AsCallExpression() + ->Signature() + ->AddSignatureFlag(panda::es2panda::checker::SignatureFlags::PRIVATE); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID derived.ID privateMethod"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, ProtectedAccessTestCorrect) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class A { + public a: int = 1; + } + class B extends A { + public b: int = this.a; + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[1] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[0] + ->AsClassProperty() + ->AddModifier(panda::es2panda::ir::ModifierFlags::PROTECTED); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + + ASSERT_EQ(is_correct, true); + ASSERT_EQ(errors.size(), 0); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, ProtectedAccessTestNegative1) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + } + function main(): void { + let base: Base = new Base(); + let a = base.a; + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[1] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[0] + ->AsClassProperty() + ->AddModifier(panda::es2panda::ir::ModifierFlags::PROTECTED); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID base.ID a"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, ProtectedAccessTestNegative2) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + } + class Derived extends Base {} + function main(): void { + let derived: Derived = new Derived(); + let a = derived.a; + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[1] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[0] + ->AsClassProperty() + ->AddModifier(panda::es2panda::ir::ModifierFlags::PROTECTED); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID derived.ID a"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, ProtectedAccessTestNegative3) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + } + class Derived extends Base {} + function main(): void { + let derived: Base = new Derived(); + let a = derived.a; + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[1] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[0] + ->AsClassProperty() + ->AddModifier(panda::es2panda::ir::ModifierFlags::PROTECTED); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID derived.ID a"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, ProtectedAccessTestNegative4) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + public protectedMethod() { + this.a = 2; + } + } + function main(): void { + let base: Base = new Base(); + base.protectedMethod(); + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[0] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[1] + ->AsClassElement() + ->Value() + ->AsFunctionExpression() + ->Function() + ->AsScriptFunction() + ->Body() + ->AsBlockStatement() + ->Statements()[1] + ->AsExpressionStatement() + ->GetExpression() + ->AsCallExpression() + ->Signature() + ->AddSignatureFlag(panda::es2panda::checker::SignatureFlags::PROTECTED); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID base.ID protectedMethod"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, ProtectedAccessTestNegative5) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + public protectedMethod() { + this.a = 2; + } + } + class Derived extends Base {} + function main(): void { + let derived: Derived = new Derived(); + derived.protectedMethod(); + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[0] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[1] + ->AsClassElement() + ->Value() + ->AsFunctionExpression() + ->Function() + ->AsScriptFunction() + ->Body() + ->AsBlockStatement() + ->Statements()[1] + ->AsExpressionStatement() + ->GetExpression() + ->AsCallExpression() + ->Signature() + ->AddSignatureFlag(panda::es2panda::checker::SignatureFlags::PROTECTED); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID derived.ID protectedMethod"); + impl_->DestroyContext(ctx); +} + +TEST_F(ASTVerifierTest, ProtectedAccessTestNegative6) +{ + panda::es2panda::compiler::ASTVerifier verifier {Allocator()}; + + char const *text = R"XXX( + class Base { + public a: int = 1; + public protectedMethod() { + this.a = 2; + } + } + class Derived extends Base {} + function main(): void { + let derived: Base = new Derived(); + derived.protectedMethod(); + } + )XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED); + + auto *ast = reinterpret_cast(impl_->ProgramAst(impl_->ContextProgram(ctx))); + + ast->AsBlockStatement() + ->Statements()[0] + ->AsClassDeclaration() + ->Definition() + ->AsClassDefinition() + ->Body()[1] + ->AsClassElement() + ->Value() + ->AsFunctionExpression() + ->Function() + ->AsScriptFunction() + ->Body() + ->AsBlockStatement() + ->Statements()[1] + ->AsExpressionStatement() + ->GetExpression() + ->AsCallExpression() + ->Signature() + ->AddSignatureFlag(panda::es2panda::checker::SignatureFlags::PROTECTED); + + auto checks = panda::es2panda::compiler::ASTVerifier::CheckSet {Allocator()->Adapter()}; + checks.insert("VerifyModifierAccessRecursive"); + bool is_correct = verifier.Verify(ast, checks); + const auto &errors = verifier.GetErrors(); + const auto [name, error] = errors[0]; + + ASSERT_EQ(is_correct, false); + ASSERT_EQ(errors.size(), 1); + ASSERT_EQ(error.message, "PROPERTY_NOT_VISIBLE_HERE: MEMBER_EXPR ID derived.ID protectedMethod"); + impl_->DestroyContext(ctx); +} -- Gitee