13af6ab5fSopenharmony_ci/*
23af6ab5fSopenharmony_ci * Copyright (c) 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 "interfaceObjectLiteralLowering.h"
173af6ab5fSopenharmony_ci#include "checker/ETSchecker.h"
183af6ab5fSopenharmony_ci#include "checker/ets/typeRelationContext.h"
193af6ab5fSopenharmony_ci#include "ir/expressions/assignmentExpression.h"
203af6ab5fSopenharmony_ci#include "util/helpers.h"
213af6ab5fSopenharmony_ci
223af6ab5fSopenharmony_cinamespace ark::es2panda::compiler {
233af6ab5fSopenharmony_ci
243af6ab5fSopenharmony_cistd::string_view InterfaceObjectLiteralLowering::Name() const
253af6ab5fSopenharmony_ci{
263af6ab5fSopenharmony_ci    return "InterfaceObjectLiteralLowering";
273af6ab5fSopenharmony_ci}
283af6ab5fSopenharmony_ci
293af6ab5fSopenharmony_cistatic inline bool IsInterfaceType(const checker::Type *type)
303af6ab5fSopenharmony_ci{
313af6ab5fSopenharmony_ci    return type != nullptr && type->IsETSObjectType() &&
323af6ab5fSopenharmony_ci           type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE) &&
333af6ab5fSopenharmony_ci           !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC);
343af6ab5fSopenharmony_ci}
353af6ab5fSopenharmony_ci
363af6ab5fSopenharmony_cistatic ir::AstNode *CreateAnonClassImplCtor(checker::ETSChecker *checker)
373af6ab5fSopenharmony_ci{
383af6ab5fSopenharmony_ci    checker::ETSChecker::ClassInitializerBuilder initBuilder =
393af6ab5fSopenharmony_ci        [checker]([[maybe_unused]] ArenaVector<ir::Statement *> *statements,
403af6ab5fSopenharmony_ci                  [[maybe_unused]] ArenaVector<ir::Expression *> *params) {
413af6ab5fSopenharmony_ci            checker->AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
423af6ab5fSopenharmony_ci        };
433af6ab5fSopenharmony_ci
443af6ab5fSopenharmony_ci    return checker->CreateClassInstanceInitializer(initBuilder);
453af6ab5fSopenharmony_ci}
463af6ab5fSopenharmony_ci
473af6ab5fSopenharmony_cistatic ir::ClassProperty *CreateAnonClassField(ir::MethodDefinition *ifaceMethod, checker::ETSChecker *checker)
483af6ab5fSopenharmony_ci{
493af6ab5fSopenharmony_ci    auto *const allocator = checker->Allocator();
503af6ab5fSopenharmony_ci
513af6ab5fSopenharmony_ci    // Field type annotation
523af6ab5fSopenharmony_ci    auto *fieldType = ifaceMethod->Function()->Signature()->ReturnType();
533af6ab5fSopenharmony_ci    if (IsInterfaceType(fieldType)) {
543af6ab5fSopenharmony_ci        auto *anonClass = fieldType->AsETSObjectType()->GetDeclNode()->AsTSInterfaceDeclaration()->GetAnonClass();
553af6ab5fSopenharmony_ci        ASSERT(anonClass != nullptr);
563af6ab5fSopenharmony_ci        fieldType = anonClass->Definition()->TsType();
573af6ab5fSopenharmony_ci    }
583af6ab5fSopenharmony_ci    ASSERT(fieldType != nullptr);
593af6ab5fSopenharmony_ci    auto *fieldTypeNode = checker->AllocNode<ir::OpaqueTypeNode>(fieldType);
603af6ab5fSopenharmony_ci
613af6ab5fSopenharmony_ci    // Field identifier
623af6ab5fSopenharmony_ci    util::UString fieldName(std::string("_"), allocator);
633af6ab5fSopenharmony_ci    fieldName.Append(ifaceMethod->Id()->Name());
643af6ab5fSopenharmony_ci    auto *fieldId = checker->AllocNode<ir::Identifier>(fieldName.View(), nullptr, allocator);
653af6ab5fSopenharmony_ci
663af6ab5fSopenharmony_ci    // Field modifiers flags
673af6ab5fSopenharmony_ci    ir::ModifierFlags fieldMF = ir::ModifierFlags::PRIVATE;
683af6ab5fSopenharmony_ci
693af6ab5fSopenharmony_ci    // No overloads means no setter function with the same name, so the field is readonly
703af6ab5fSopenharmony_ci    if (ifaceMethod->Overloads().empty()) {
713af6ab5fSopenharmony_ci        fieldMF |= ir::ModifierFlags::READONLY;
723af6ab5fSopenharmony_ci    }
733af6ab5fSopenharmony_ci
743af6ab5fSopenharmony_ci    // Create synthetic class property node
753af6ab5fSopenharmony_ci    auto *field = checker->AllocNode<ir::ClassProperty>(fieldId, nullptr, fieldTypeNode->Clone(allocator, nullptr),
763af6ab5fSopenharmony_ci                                                        fieldMF, allocator, false);
773af6ab5fSopenharmony_ci    field->SetRange(ifaceMethod->Range());
783af6ab5fSopenharmony_ci
793af6ab5fSopenharmony_ci    return field;
803af6ab5fSopenharmony_ci}
813af6ab5fSopenharmony_ci
823af6ab5fSopenharmony_cistatic ir::MethodDefinition *CreateAnonClassFieldGetterSetter(checker::ETSChecker *checker,
833af6ab5fSopenharmony_ci                                                              ir::MethodDefinition *ifaceMethod, bool isSetter)
843af6ab5fSopenharmony_ci{
853af6ab5fSopenharmony_ci    checker::ETSChecker::MethodBuilder methodBuilder = [checker, ifaceMethod,
863af6ab5fSopenharmony_ci                                                        isSetter](ArenaVector<ir::Statement *> *statements,
873af6ab5fSopenharmony_ci                                                                  ArenaVector<ir::Expression *> *params,
883af6ab5fSopenharmony_ci                                                                  checker::Type **returnType) {
893af6ab5fSopenharmony_ci        auto *const allocator = checker->Allocator();
903af6ab5fSopenharmony_ci
913af6ab5fSopenharmony_ci        // Adding mandatory 'this' parameter
923af6ab5fSopenharmony_ci        checker->AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
933af6ab5fSopenharmony_ci
943af6ab5fSopenharmony_ci        // ifaceMethod is getter, so it should have return type
953af6ab5fSopenharmony_ci        auto *retType = ifaceMethod->Function()->Signature()->ReturnType();
963af6ab5fSopenharmony_ci        if (IsInterfaceType(retType)) {
973af6ab5fSopenharmony_ci            auto *anonClass = retType->AsETSObjectType()->GetDeclNode()->AsTSInterfaceDeclaration()->GetAnonClass();
983af6ab5fSopenharmony_ci            ASSERT(anonClass != nullptr);
993af6ab5fSopenharmony_ci            retType = anonClass->Definition()->TsType();
1003af6ab5fSopenharmony_ci        }
1013af6ab5fSopenharmony_ci        ASSERT(retType != nullptr);
1023af6ab5fSopenharmony_ci
1033af6ab5fSopenharmony_ci        // Field identifier
1043af6ab5fSopenharmony_ci        util::UString fieldName(std::string("_"), allocator);
1053af6ab5fSopenharmony_ci        fieldName.Append(ifaceMethod->Id()->Name());
1063af6ab5fSopenharmony_ci        auto *fieldId = checker->AllocNode<ir::Identifier>(fieldName.View(), nullptr, allocator);
1073af6ab5fSopenharmony_ci
1083af6ab5fSopenharmony_ci        if (isSetter) {
1093af6ab5fSopenharmony_ci            // Setter call params
1103af6ab5fSopenharmony_ci            ir::ETSParameterExpression *param =
1113af6ab5fSopenharmony_ci                checker->AddParam(ifaceMethod->Id()->Name(), checker->AllocNode<ir::OpaqueTypeNode>(retType));
1123af6ab5fSopenharmony_ci            params->push_back(param);
1133af6ab5fSopenharmony_ci
1143af6ab5fSopenharmony_ci            // Setter body:
1153af6ab5fSopenharmony_ci            // this.<fieldName> = <callParam>;
1163af6ab5fSopenharmony_ci            auto *thisExpr = checker->AllocNode<ir::ThisExpression>();
1173af6ab5fSopenharmony_ci            auto *lhs = checker->AllocNode<ir::MemberExpression>(
1183af6ab5fSopenharmony_ci                thisExpr, fieldId->Clone(allocator, nullptr), ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
1193af6ab5fSopenharmony_ci            auto *rhs = param->Ident()->Clone(allocator, nullptr);
1203af6ab5fSopenharmony_ci
1213af6ab5fSopenharmony_ci            auto *assignment =
1223af6ab5fSopenharmony_ci                checker->AllocNode<ir::AssignmentExpression>(lhs, rhs, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
1233af6ab5fSopenharmony_ci            auto *statement = checker->AllocNode<ir::ExpressionStatement>(assignment);
1243af6ab5fSopenharmony_ci            statements->push_back(statement);
1253af6ab5fSopenharmony_ci
1263af6ab5fSopenharmony_ci            // Setter return type
1273af6ab5fSopenharmony_ci            *returnType = checker->GlobalVoidType();
1283af6ab5fSopenharmony_ci        } else {
1293af6ab5fSopenharmony_ci            // Getter call params are empty
1303af6ab5fSopenharmony_ci
1313af6ab5fSopenharmony_ci            // Getter body:
1323af6ab5fSopenharmony_ci            // Just return this.<fieldName>;
1333af6ab5fSopenharmony_ci            auto *thisExpr = checker->AllocNode<ir::ThisExpression>();
1343af6ab5fSopenharmony_ci            auto *argument = checker->AllocNode<ir::MemberExpression>(
1353af6ab5fSopenharmony_ci                thisExpr, fieldId->Clone(allocator, nullptr), ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
1363af6ab5fSopenharmony_ci
1373af6ab5fSopenharmony_ci            auto *statement = checker->AllocNode<ir::ReturnStatement>(argument);
1383af6ab5fSopenharmony_ci            statements->push_back(statement);
1393af6ab5fSopenharmony_ci
1403af6ab5fSopenharmony_ci            // Getter return type
1413af6ab5fSopenharmony_ci            *returnType = retType;
1423af6ab5fSopenharmony_ci        }
1433af6ab5fSopenharmony_ci    };
1443af6ab5fSopenharmony_ci
1453af6ab5fSopenharmony_ci    ir::ModifierFlags modifierFlags = ir::ModifierFlags::PUBLIC;
1463af6ab5fSopenharmony_ci    modifierFlags |= isSetter ? ir::ModifierFlags::SETTER : ir::ModifierFlags::GETTER;
1473af6ab5fSopenharmony_ci    ir::ScriptFunctionFlags funcFlags = ir::ScriptFunctionFlags::METHOD;
1483af6ab5fSopenharmony_ci    funcFlags |= isSetter ? ir::ScriptFunctionFlags::SETTER : ir::ScriptFunctionFlags::GETTER;
1493af6ab5fSopenharmony_ci
1503af6ab5fSopenharmony_ci    return checker->CreateClassMethod(ifaceMethod->Id()->Name().Utf8(), funcFlags, modifierFlags, methodBuilder);
1513af6ab5fSopenharmony_ci}
1523af6ab5fSopenharmony_ci
1533af6ab5fSopenharmony_cistatic void FillClassBody(checker::ETSChecker *checker, ArenaVector<ir::AstNode *> *classBody,
1543af6ab5fSopenharmony_ci                          const ArenaVector<ir::AstNode *> &ifaceBody, ir::ObjectExpression *objExpr)
1553af6ab5fSopenharmony_ci{
1563af6ab5fSopenharmony_ci    for (auto *it : ifaceBody) {
1573af6ab5fSopenharmony_ci        ASSERT(it->IsMethodDefinition());
1583af6ab5fSopenharmony_ci        auto *ifaceMethod = it->AsMethodDefinition();
1593af6ab5fSopenharmony_ci
1603af6ab5fSopenharmony_ci        if (!ifaceMethod->Function()->IsGetter() && !ifaceMethod->Function()->IsSetter()) {
1613af6ab5fSopenharmony_ci            checker->ThrowTypeError("Interface has methods", objExpr->Start());
1623af6ab5fSopenharmony_ci        }
1633af6ab5fSopenharmony_ci
1643af6ab5fSopenharmony_ci        if (!ifaceMethod->Function()->IsGetter()) {
1653af6ab5fSopenharmony_ci            continue;
1663af6ab5fSopenharmony_ci        }
1673af6ab5fSopenharmony_ci
1683af6ab5fSopenharmony_ci        auto *field = CreateAnonClassField(ifaceMethod, checker);
1693af6ab5fSopenharmony_ci        classBody->push_back(field);
1703af6ab5fSopenharmony_ci
1713af6ab5fSopenharmony_ci        auto *getter = CreateAnonClassFieldGetterSetter(checker, ifaceMethod, false);
1723af6ab5fSopenharmony_ci        classBody->push_back(getter);
1733af6ab5fSopenharmony_ci
1743af6ab5fSopenharmony_ci        if (ifaceMethod->Overloads().size() == 1 && ifaceMethod->Overloads()[0]->Function()->IsSetter()) {
1753af6ab5fSopenharmony_ci            auto *setter = CreateAnonClassFieldGetterSetter(checker, ifaceMethod, true);
1763af6ab5fSopenharmony_ci            classBody->push_back(setter);
1773af6ab5fSopenharmony_ci        }
1783af6ab5fSopenharmony_ci    }
1793af6ab5fSopenharmony_ci}
1803af6ab5fSopenharmony_ci
1813af6ab5fSopenharmony_cistatic void FillAnonClassBody(checker::ETSChecker *checker, ArenaVector<ir::AstNode *> *classBody,
1823af6ab5fSopenharmony_ci                              ir::TSInterfaceDeclaration *ifaceNode, ir::ObjectExpression *objExpr)
1833af6ab5fSopenharmony_ci{
1843af6ab5fSopenharmony_ci    for (auto *extendedIface : ifaceNode->TsType()->AsETSObjectType()->Interfaces()) {
1853af6ab5fSopenharmony_ci        auto extendedIfaceBody = extendedIface->GetDeclNode()->AsTSInterfaceDeclaration()->Body()->Body();
1863af6ab5fSopenharmony_ci        FillClassBody(checker, classBody, extendedIfaceBody, objExpr);
1873af6ab5fSopenharmony_ci    }
1883af6ab5fSopenharmony_ci
1893af6ab5fSopenharmony_ci    FillClassBody(checker, classBody, ifaceNode->Body()->Body(), objExpr);
1903af6ab5fSopenharmony_ci}
1913af6ab5fSopenharmony_ci
1923af6ab5fSopenharmony_cistatic checker::ETSObjectType *GenerateAnonClassTypeFromInterface(checker::ETSChecker *checker,
1933af6ab5fSopenharmony_ci                                                                  ir::TSInterfaceDeclaration *ifaceNode,
1943af6ab5fSopenharmony_ci                                                                  ir::ObjectExpression *objExpr)
1953af6ab5fSopenharmony_ci{
1963af6ab5fSopenharmony_ci    if (ifaceNode->GetAnonClass() != nullptr) {
1973af6ab5fSopenharmony_ci        return ifaceNode->GetAnonClass()->Definition()->TsType()->AsETSObjectType();
1983af6ab5fSopenharmony_ci    }
1993af6ab5fSopenharmony_ci
2003af6ab5fSopenharmony_ci    auto classBodyBuilder = [checker, ifaceNode, objExpr](ArenaVector<ir::AstNode *> *classBody) {
2013af6ab5fSopenharmony_ci        if (ifaceNode->TsType() == nullptr) {
2023af6ab5fSopenharmony_ci            ifaceNode->Check(checker);
2033af6ab5fSopenharmony_ci        }
2043af6ab5fSopenharmony_ci
2053af6ab5fSopenharmony_ci        FillAnonClassBody(checker, classBody, ifaceNode, objExpr);
2063af6ab5fSopenharmony_ci        classBody->push_back(CreateAnonClassImplCtor(checker));
2073af6ab5fSopenharmony_ci    };
2083af6ab5fSopenharmony_ci
2093af6ab5fSopenharmony_ci    util::UString className(util::StringView("$anonymous_class$"), checker->Allocator());
2103af6ab5fSopenharmony_ci    className.Append(ifaceNode->Id()->Name());
2113af6ab5fSopenharmony_ci    auto *classDecl = checker->BuildClass(className.View(), classBodyBuilder);
2123af6ab5fSopenharmony_ci    auto *classDef = classDecl->Definition();
2133af6ab5fSopenharmony_ci    auto *classType = classDef->TsType()->AsETSObjectType();
2143af6ab5fSopenharmony_ci
2153af6ab5fSopenharmony_ci    // Class type params
2163af6ab5fSopenharmony_ci    if (ifaceNode->TypeParams() != nullptr) {
2173af6ab5fSopenharmony_ci        // NOTE: to be done
2183af6ab5fSopenharmony_ci        checker->ThrowTypeError("Object literal cannot be of typed interface type", objExpr->Start());
2193af6ab5fSopenharmony_ci    }
2203af6ab5fSopenharmony_ci
2213af6ab5fSopenharmony_ci    // Class implements
2223af6ab5fSopenharmony_ci    auto *classImplements =
2233af6ab5fSopenharmony_ci        checker->AllocNode<ir::TSClassImplements>(checker->AllocNode<ir::OpaqueTypeNode>(ifaceNode->TsType()));
2243af6ab5fSopenharmony_ci    classImplements->SetParent(classDef);
2253af6ab5fSopenharmony_ci    classDef->Implements().emplace_back(classImplements);
2263af6ab5fSopenharmony_ci    classType->RemoveObjectFlag(checker::ETSObjectFlags::RESOLVED_INTERFACES);
2273af6ab5fSopenharmony_ci    checker->GetInterfacesOfClass(classType);
2283af6ab5fSopenharmony_ci
2293af6ab5fSopenharmony_ci    ifaceNode->SetAnonClass(classDecl);
2303af6ab5fSopenharmony_ci    return classType;
2313af6ab5fSopenharmony_ci}
2323af6ab5fSopenharmony_ci
2333af6ab5fSopenharmony_cistatic void HandleInterfaceLowering(checker::ETSChecker *checker, ir::ObjectExpression *objExpr)
2343af6ab5fSopenharmony_ci{
2353af6ab5fSopenharmony_ci    const auto *const targetType = objExpr->TsType();
2363af6ab5fSopenharmony_ci    ASSERT(targetType->AsETSObjectType()->GetDeclNode()->IsTSInterfaceDeclaration());
2373af6ab5fSopenharmony_ci    auto *ifaceNode = targetType->AsETSObjectType()->GetDeclNode()->AsTSInterfaceDeclaration();
2383af6ab5fSopenharmony_ci    auto *resultType = GenerateAnonClassTypeFromInterface(checker, ifaceNode, objExpr);
2393af6ab5fSopenharmony_ci
2403af6ab5fSopenharmony_ci    if (const auto *const parent = objExpr->Parent(); parent->IsArrayExpression()) {
2413af6ab5fSopenharmony_ci        for (auto *elem : parent->AsArrayExpression()->Elements()) {
2423af6ab5fSopenharmony_ci            if (!elem->IsObjectExpression()) {
2433af6ab5fSopenharmony_ci                continue;
2443af6ab5fSopenharmony_ci            }
2453af6ab5fSopenharmony_ci            // Adjusting ts types of other object literals in array
2463af6ab5fSopenharmony_ci            elem->AsObjectExpression()->SetTsType(resultType);
2473af6ab5fSopenharmony_ci        }
2483af6ab5fSopenharmony_ci    }
2493af6ab5fSopenharmony_ci    objExpr->SetTsType(resultType);
2503af6ab5fSopenharmony_ci}
2513af6ab5fSopenharmony_ci
2523af6ab5fSopenharmony_cibool InterfaceObjectLiteralLowering::Perform(public_lib::Context *ctx, parser::Program *program)
2533af6ab5fSopenharmony_ci{
2543af6ab5fSopenharmony_ci    for (auto &[_, extPrograms] : program->ExternalSources()) {
2553af6ab5fSopenharmony_ci        (void)_;
2563af6ab5fSopenharmony_ci        for (auto *extProg : extPrograms) {
2573af6ab5fSopenharmony_ci            Perform(ctx, extProg);
2583af6ab5fSopenharmony_ci        }
2593af6ab5fSopenharmony_ci    }
2603af6ab5fSopenharmony_ci
2613af6ab5fSopenharmony_ci    auto *checker = ctx->checker->AsETSChecker();
2623af6ab5fSopenharmony_ci
2633af6ab5fSopenharmony_ci    program->Ast()->IterateRecursivelyPostorder([checker](ir::AstNode *ast) -> void {
2643af6ab5fSopenharmony_ci        if (ast->IsObjectExpression() && IsInterfaceType(ast->AsObjectExpression()->TsType())) {
2653af6ab5fSopenharmony_ci            HandleInterfaceLowering(checker, ast->AsObjectExpression());
2663af6ab5fSopenharmony_ci        }
2673af6ab5fSopenharmony_ci    });
2683af6ab5fSopenharmony_ci
2693af6ab5fSopenharmony_ci    return true;
2703af6ab5fSopenharmony_ci}
2713af6ab5fSopenharmony_ci
2723af6ab5fSopenharmony_cibool InterfaceObjectLiteralLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program)
2733af6ab5fSopenharmony_ci{
2743af6ab5fSopenharmony_ci    for (auto &[_, extPrograms] : program->ExternalSources()) {
2753af6ab5fSopenharmony_ci        (void)_;
2763af6ab5fSopenharmony_ci        for (auto *extProg : extPrograms) {
2773af6ab5fSopenharmony_ci            if (!Postcondition(ctx, extProg)) {
2783af6ab5fSopenharmony_ci                return false;
2793af6ab5fSopenharmony_ci            }
2803af6ab5fSopenharmony_ci        }
2813af6ab5fSopenharmony_ci    }
2823af6ab5fSopenharmony_ci
2833af6ab5fSopenharmony_ci    return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) -> bool {
2843af6ab5fSopenharmony_ci        return ast->IsObjectExpression() && IsInterfaceType(ast->AsObjectExpression()->TsType());
2853af6ab5fSopenharmony_ci    });
2863af6ab5fSopenharmony_ci}
2873af6ab5fSopenharmony_ci}  // namespace ark::es2panda::compiler
288