13af6ab5fSopenharmony_ci/* 23af6ab5fSopenharmony_ci * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. 33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License. 53af6ab5fSopenharmony_ci * You may obtain a copy of the License at 63af6ab5fSopenharmony_ci * 73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 83af6ab5fSopenharmony_ci * 93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and 133af6ab5fSopenharmony_ci * limitations under the License. 143af6ab5fSopenharmony_ci */ 153af6ab5fSopenharmony_ci 163af6ab5fSopenharmony_ci#include "checker/ets/dynamic/dynamicCall.h" 173af6ab5fSopenharmony_ci#include "checker/types/ets/etsDynamicType.h" 183af6ab5fSopenharmony_ci#include "test/unit/es2panda_unit_gtest.h" 193af6ab5fSopenharmony_ci#include "ir/expressions/callExpression.h" 203af6ab5fSopenharmony_ci#include "ir/expressions/memberExpression.h" 213af6ab5fSopenharmony_ci#include "ir/expressions/identifier.h" 223af6ab5fSopenharmony_ci#include "ir/ets/etsNewClassInstanceExpression.h" 233af6ab5fSopenharmony_ci#include "ir/ets/etsTypeReferencePart.h" 243af6ab5fSopenharmony_ci#include "ir/ets/etsTypeReference.h" 253af6ab5fSopenharmony_ci#include "ir/ts/tsQualifiedName.h" 263af6ab5fSopenharmony_ci#include "util/helpers.h" 273af6ab5fSopenharmony_ci#include "compiler/lowering/scopesInit/scopesInitPhase.h" 283af6ab5fSopenharmony_ci#include "util/language.h" 293af6ab5fSopenharmony_ci#include "parser/ETSparser.h" 303af6ab5fSopenharmony_ci 313af6ab5fSopenharmony_cinamespace ark::es2panda::testing { 323af6ab5fSopenharmony_ci 333af6ab5fSopenharmony_ciclass DynamicCall : public Es2pandaUnitGtest { 343af6ab5fSopenharmony_cipublic: 353af6ab5fSopenharmony_ci std::pair<parser::Program *, ir::Expression *> ParseExpr(const std::string &strExpr) 363af6ab5fSopenharmony_ci { 373af6ab5fSopenharmony_ci auto program = 383af6ab5fSopenharmony_ci Allocator()->New<parser::Program>(Allocator(), Allocator()->New<varbinder::ETSBinder>(Allocator())); 393af6ab5fSopenharmony_ci program->VarBinder()->SetProgram(program); 403af6ab5fSopenharmony_ci program->VarBinder()->InitTopScope(); 413af6ab5fSopenharmony_ci auto etsParser = parser::ETSParser(program, CompilerOptions {}); 423af6ab5fSopenharmony_ci auto expr = etsParser.CreateExpression(strExpr); 433af6ab5fSopenharmony_ci return {program, expr}; 443af6ab5fSopenharmony_ci } 453af6ab5fSopenharmony_ci 463af6ab5fSopenharmony_ci ir::Expression *MarkChainDynamic(ir::Expression *obj) 473af6ab5fSopenharmony_ci { 483af6ab5fSopenharmony_ci if (obj == nullptr) { 493af6ab5fSopenharmony_ci return nullptr; 503af6ab5fSopenharmony_ci } 513af6ab5fSopenharmony_ci auto dynamicType = Allocator()->New<checker::ETSDynamicType>( 523af6ab5fSopenharmony_ci Allocator(), std::make_tuple("test", "test", Language::FromString("sts").value()), 533af6ab5fSopenharmony_ci std::make_tuple(obj, checker::ETSObjectFlags::NO_OPTS, nullptr), false); 543af6ab5fSopenharmony_ci if (obj->IsETSTypeReference()) { 553af6ab5fSopenharmony_ci obj = obj->AsETSTypeReference()->Part()->Name(); 563af6ab5fSopenharmony_ci } 573af6ab5fSopenharmony_ci while (obj != nullptr && (obj->IsMemberExpression() || obj->IsTSQualifiedName())) { 583af6ab5fSopenharmony_ci obj->SetTsType(dynamicType); 593af6ab5fSopenharmony_ci if (obj->IsMemberExpression()) { 603af6ab5fSopenharmony_ci obj = obj->AsMemberExpression()->Object(); 613af6ab5fSopenharmony_ci } else if (obj->IsTSQualifiedName()) { 623af6ab5fSopenharmony_ci obj = obj->AsTSQualifiedName()->Left(); 633af6ab5fSopenharmony_ci } 643af6ab5fSopenharmony_ci } 653af6ab5fSopenharmony_ci obj->SetTsType(dynamicType); 663af6ab5fSopenharmony_ci return obj; 673af6ab5fSopenharmony_ci } 683af6ab5fSopenharmony_ci 693af6ab5fSopenharmony_ci std::tuple<parser::Program *, ir::Expression *, ir::Expression *> ParseDynExpr(const std::string &strExpr) 703af6ab5fSopenharmony_ci { 713af6ab5fSopenharmony_ci auto [prog, expr] = ParseExpr(strExpr); 723af6ab5fSopenharmony_ci ir::Expression *obj = nullptr; 733af6ab5fSopenharmony_ci if (expr->IsCallExpression()) { 743af6ab5fSopenharmony_ci obj = expr->AsCallExpression()->Callee(); 753af6ab5fSopenharmony_ci } else { 763af6ab5fSopenharmony_ci obj = expr->AsETSNewClassInstanceExpression()->GetTypeRef()->AsETSTypeReference(); 773af6ab5fSopenharmony_ci } 783af6ab5fSopenharmony_ci auto first = MarkChainDynamic(obj); 793af6ab5fSopenharmony_ci return {prog, obj, first}; 803af6ab5fSopenharmony_ci } 813af6ab5fSopenharmony_ci 823af6ab5fSopenharmony_ci void AddDynImport(const char *specifierName, varbinder::ETSBinder *varbinder, ir::Identifier *node) 833af6ab5fSopenharmony_ci { 843af6ab5fSopenharmony_ci auto aIdent = Allocator()->New<ir::Identifier>(specifierName, Allocator()); 853af6ab5fSopenharmony_ci ArenaVector<ir::AstNode *> specifiers {Allocator()->Adapter()}; 863af6ab5fSopenharmony_ci auto specifier = Allocator()->New<ir::ImportSpecifier>(aIdent, aIdent); 873af6ab5fSopenharmony_ci specifiers.emplace_back(specifier); 883af6ab5fSopenharmony_ci auto importSrc = Allocator()->New<ir::ImportSource>(Allocator()->New<ir::StringLiteral>("/tmp"), 893af6ab5fSopenharmony_ci Allocator()->New<ir::StringLiteral>(), 903af6ab5fSopenharmony_ci Language::FromString("js").value(), false); 913af6ab5fSopenharmony_ci auto importDecl = 923af6ab5fSopenharmony_ci util::NodeAllocator::Alloc<ir::ETSImportDeclaration>(Allocator(), importSrc, std::move(specifiers)); 933af6ab5fSopenharmony_ci compiler::InitScopesPhaseETS::RunExternalNode(importDecl, varbinder); 943af6ab5fSopenharmony_ci varbinder->BuildImportDeclaration(importDecl); 953af6ab5fSopenharmony_ci auto var = varbinder->TopScope()->Find(specifierName); 963af6ab5fSopenharmony_ci node->SetVariable(var.variable); 973af6ab5fSopenharmony_ci } 983af6ab5fSopenharmony_ci 993af6ab5fSopenharmony_ci void AssertNameEq(const ArenaVector<util::StringView> &name, std::initializer_list<const char *> expected) 1003af6ab5fSopenharmony_ci { 1013af6ab5fSopenharmony_ci ASSERT_EQ(name.size(), expected.size()); 1023af6ab5fSopenharmony_ci auto it1 = expected.begin(); 1033af6ab5fSopenharmony_ci auto it2 = name.begin(); 1043af6ab5fSopenharmony_ci while (it2 != name.end()) { 1053af6ab5fSopenharmony_ci ASSERT_EQ(util::StringView(*it1), *it2); 1063af6ab5fSopenharmony_ci it1++, it2++; 1073af6ab5fSopenharmony_ci } 1083af6ab5fSopenharmony_ci } 1093af6ab5fSopenharmony_ci}; 1103af6ab5fSopenharmony_ci 1113af6ab5fSopenharmony_ciTEST_F(DynamicCall, JoinDynMemberChain) 1123af6ab5fSopenharmony_ci{ 1133af6ab5fSopenharmony_ci auto strExpr = "A.b.c.d()"; 1143af6ab5fSopenharmony_ci auto [prog, obj, first] = ParseDynExpr(strExpr); 1153af6ab5fSopenharmony_ci auto [squeezedObj, name] = checker::DynamicCall::SqueezeExpr(Allocator(), obj->AsMemberExpression()); 1163af6ab5fSopenharmony_ci AssertNameEq(name, {"b", "c", "d"}); 1173af6ab5fSopenharmony_ci ASSERT(squeezedObj->IsIdentifier()); 1183af6ab5fSopenharmony_ci auto varbinder = prog->VarBinder()->AsETSBinder(); 1193af6ab5fSopenharmony_ci { 1203af6ab5fSopenharmony_ci // With empty varbinder A is local variable 1213af6ab5fSopenharmony_ci auto [finalObj, callName] = checker::DynamicCall::ResolveCall(varbinder, obj); 1223af6ab5fSopenharmony_ci AssertNameEq(callName, {"b", "c", "d"}); 1233af6ab5fSopenharmony_ci } 1243af6ab5fSopenharmony_ci // Now A is import => we can optimize 1253af6ab5fSopenharmony_ci AddDynImport("A", varbinder, first->AsIdentifier()); 1263af6ab5fSopenharmony_ci auto [finalObj, callName] = checker::DynamicCall::ResolveCall(varbinder, obj); 1273af6ab5fSopenharmony_ci AssertNameEq(callName, {"A", "b", "c", "d"}); 1283af6ab5fSopenharmony_ci} 1293af6ab5fSopenharmony_ci 1303af6ab5fSopenharmony_ciTEST_F(DynamicCall, JoinCompitedMemberChain) 1313af6ab5fSopenharmony_ci{ 1323af6ab5fSopenharmony_ci auto strExpr = "A.b.c[0].d.e.f()"; 1333af6ab5fSopenharmony_ci auto [prog, obj, first] = ParseDynExpr(strExpr); 1343af6ab5fSopenharmony_ci auto [squeezedObj, name] = checker::DynamicCall::SqueezeExpr(Allocator(), obj->AsMemberExpression()); 1353af6ab5fSopenharmony_ci // Can't optimize [] 1363af6ab5fSopenharmony_ci AssertNameEq(name, {"d", "e", "f"}); 1373af6ab5fSopenharmony_ci ASSERT_EQ(squeezedObj, 1383af6ab5fSopenharmony_ci obj->AsMemberExpression()->Object()->AsMemberExpression()->Object()->AsMemberExpression()->Object()); 1393af6ab5fSopenharmony_ci auto varbinder = prog->VarBinder()->AsETSBinder(); 1403af6ab5fSopenharmony_ci { 1413af6ab5fSopenharmony_ci // Can't optimize [] 1423af6ab5fSopenharmony_ci auto [finalObj, callName] = checker::DynamicCall::ResolveCall(varbinder, obj); 1433af6ab5fSopenharmony_ci AssertNameEq(callName, {"d", "e", "f"}); 1443af6ab5fSopenharmony_ci } 1453af6ab5fSopenharmony_ci // Can't optimize [] 1463af6ab5fSopenharmony_ci AddDynImport("A", varbinder, first->AsIdentifier()); 1473af6ab5fSopenharmony_ci auto [finalObj, callName] = checker::DynamicCall::ResolveCall(varbinder, obj); 1483af6ab5fSopenharmony_ci AssertNameEq(callName, {"d", "e", "f"}); 1493af6ab5fSopenharmony_ci} 1503af6ab5fSopenharmony_ci 1513af6ab5fSopenharmony_ciTEST_F(DynamicCall, JoinDynCallMember) 1523af6ab5fSopenharmony_ci{ 1533af6ab5fSopenharmony_ci auto strExpr = "A.b().c.d()"; 1543af6ab5fSopenharmony_ci auto [program, obj, first] = ParseDynExpr(strExpr); 1553af6ab5fSopenharmony_ci auto [squeezedObj, name] = checker::DynamicCall::SqueezeExpr(Allocator(), obj->AsMemberExpression()); 1563af6ab5fSopenharmony_ci AssertNameEq(name, {"c", "d"}); 1573af6ab5fSopenharmony_ci ASSERT_EQ(squeezedObj, obj->AsMemberExpression()->Object()->AsMemberExpression()->Object()); 1583af6ab5fSopenharmony_ci 1593af6ab5fSopenharmony_ci auto varbinder = program->VarBinder()->AsETSBinder(); 1603af6ab5fSopenharmony_ci auto [finalObj, callName] = checker::DynamicCall::ResolveCall(varbinder, obj); 1613af6ab5fSopenharmony_ci AssertNameEq(callName, {"c", "d"}); 1623af6ab5fSopenharmony_ci} 1633af6ab5fSopenharmony_ci 1643af6ab5fSopenharmony_ciTEST_F(DynamicCall, JoinDynStaticCallMember) 1653af6ab5fSopenharmony_ci{ 1663af6ab5fSopenharmony_ci auto strExpr = "A.b.c.d.e()"; 1673af6ab5fSopenharmony_ci auto [program, obj, first] = ParseDynExpr(strExpr); 1683af6ab5fSopenharmony_ci 1693af6ab5fSopenharmony_ci auto bObj = obj->AsMemberExpression()->Object()->AsMemberExpression()->Object(); 1703af6ab5fSopenharmony_ci ASSERT_EQ(bObj->AsMemberExpression()->Property()->AsIdentifier()->Name(), "c"); 1713af6ab5fSopenharmony_ci auto staticType = Allocator()->New<checker::ETSObjectType>(Allocator(), checker::ETSObjectFlags::NO_OPTS); 1723af6ab5fSopenharmony_ci bObj->AsMemberExpression()->Object()->SetTsType(staticType); 1733af6ab5fSopenharmony_ci 1743af6ab5fSopenharmony_ci auto [squeezedObj, name] = checker::DynamicCall::SqueezeExpr(Allocator(), obj->AsMemberExpression()); 1753af6ab5fSopenharmony_ci AssertNameEq(name, {"d", "e"}); 1763af6ab5fSopenharmony_ci ASSERT_EQ(squeezedObj, bObj); 1773af6ab5fSopenharmony_ci 1783af6ab5fSopenharmony_ci auto varbinder = program->VarBinder()->AsETSBinder(); 1793af6ab5fSopenharmony_ci AddDynImport("A", varbinder, first->AsIdentifier()); 1803af6ab5fSopenharmony_ci auto [finalObj, callName] = checker::DynamicCall::ResolveCall(varbinder, obj); 1813af6ab5fSopenharmony_ci AssertNameEq(callName, {"d", "e"}); 1823af6ab5fSopenharmony_ci} 1833af6ab5fSopenharmony_ci 1843af6ab5fSopenharmony_ciTEST_F(DynamicCall, TsQualifiedName) 1853af6ab5fSopenharmony_ci{ 1863af6ab5fSopenharmony_ci auto strExpr = "new A.b.c.d()"; 1873af6ab5fSopenharmony_ci auto [program, obj, first] = ParseDynExpr(strExpr); 1883af6ab5fSopenharmony_ci auto varbinder = program->VarBinder()->AsETSBinder(); 1893af6ab5fSopenharmony_ci AddDynImport("A", varbinder, first->AsIdentifier()); 1903af6ab5fSopenharmony_ci auto [finalObj, callName] = checker::DynamicCall::ResolveCall(varbinder, obj); 1913af6ab5fSopenharmony_ci AssertNameEq(callName, {"A", "b", "c", "d"}); 1923af6ab5fSopenharmony_ci} 1933af6ab5fSopenharmony_ci 1943af6ab5fSopenharmony_ci} // namespace ark::es2panda::testing 195