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