13af6ab5fSopenharmony_ci/**
23af6ab5fSopenharmony_ci * Copyright (c) 2023-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 "interfacePropertyDeclarations.h"
173af6ab5fSopenharmony_ci
183af6ab5fSopenharmony_ci#include "checker/ETSchecker.h"
193af6ab5fSopenharmony_ci#include "checker/types/type.h"
203af6ab5fSopenharmony_ci#include "compiler/lowering/util.h"
213af6ab5fSopenharmony_ci#include "ir/astNode.h"
223af6ab5fSopenharmony_ci#include "ir/expression.h"
233af6ab5fSopenharmony_ci#include "ir/expressions/identifier.h"
243af6ab5fSopenharmony_ci#include "ir/opaqueTypeNode.h"
253af6ab5fSopenharmony_ci#include "ir/statements/blockStatement.h"
263af6ab5fSopenharmony_ci#include "ir/ts/tsInterfaceBody.h"
273af6ab5fSopenharmony_ci#include "ir/base/classProperty.h"
283af6ab5fSopenharmony_ci#include "ir/ets/etsUnionType.h"
293af6ab5fSopenharmony_ci#include "ir/ets/etsNullishTypes.h"
303af6ab5fSopenharmony_ci
313af6ab5fSopenharmony_cinamespace ark::es2panda::compiler {
323af6ab5fSopenharmony_ci
333af6ab5fSopenharmony_cinamespace {
343af6ab5fSopenharmony_ci
353af6ab5fSopenharmony_civoid TransformOptionalFieldTypeAnnotation(checker::ETSChecker *const checker, ir::ClassProperty *const field)
363af6ab5fSopenharmony_ci{
373af6ab5fSopenharmony_ci    if (!field->IsOptionalDeclaration()) {
383af6ab5fSopenharmony_ci        return;
393af6ab5fSopenharmony_ci    }
403af6ab5fSopenharmony_ci
413af6ab5fSopenharmony_ci    if (field->IsETSUnionType()) {
423af6ab5fSopenharmony_ci        bool alreadyHasUndefined = false;
433af6ab5fSopenharmony_ci        auto unionTypes = field->AsETSUnionType()->Types();
443af6ab5fSopenharmony_ci        for (const auto &type : unionTypes) {
453af6ab5fSopenharmony_ci            if (type->IsETSUndefinedType()) {
463af6ab5fSopenharmony_ci                alreadyHasUndefined = true;
473af6ab5fSopenharmony_ci                break;
483af6ab5fSopenharmony_ci            }
493af6ab5fSopenharmony_ci        }
503af6ab5fSopenharmony_ci        if (!alreadyHasUndefined) {
513af6ab5fSopenharmony_ci            ArenaVector<ir::TypeNode *> types(field->AsETSUnionType()->Types(), checker->Allocator()->Adapter());
523af6ab5fSopenharmony_ci            types.push_back(checker->AllocNode<ir::ETSUndefinedType>());
533af6ab5fSopenharmony_ci            auto *const unionType = checker->AllocNode<ir::ETSUnionType>(std::move(types));
543af6ab5fSopenharmony_ci            field->SetTypeAnnotation(unionType);
553af6ab5fSopenharmony_ci        }
563af6ab5fSopenharmony_ci    } else {
573af6ab5fSopenharmony_ci        ArenaVector<ir::TypeNode *> types(checker->Allocator()->Adapter());
583af6ab5fSopenharmony_ci        types.push_back(field->TypeAnnotation());
593af6ab5fSopenharmony_ci        types.push_back(checker->AllocNode<ir::ETSUndefinedType>());
603af6ab5fSopenharmony_ci        auto *const unionType = checker->AllocNode<ir::ETSUnionType>(std::move(types));
613af6ab5fSopenharmony_ci        field->SetTypeAnnotation(unionType);
623af6ab5fSopenharmony_ci    }
633af6ab5fSopenharmony_ci    field->ClearModifier(ir::ModifierFlags::OPTIONAL);
643af6ab5fSopenharmony_ci}
653af6ab5fSopenharmony_ci
663af6ab5fSopenharmony_ci}  // namespace
673af6ab5fSopenharmony_ci
683af6ab5fSopenharmony_cistatic ir::MethodDefinition *GenerateGetterOrSetter(checker::ETSChecker *const checker, ir::ClassProperty *const field,
693af6ab5fSopenharmony_ci                                                    bool isSetter)
703af6ab5fSopenharmony_ci{
713af6ab5fSopenharmony_ci    auto classScope = NearestScope(field);
723af6ab5fSopenharmony_ci    auto *paramScope = checker->Allocator()->New<varbinder::FunctionParamScope>(checker->Allocator(), classScope);
733af6ab5fSopenharmony_ci    auto *functionScope = checker->Allocator()->New<varbinder::FunctionScope>(checker->Allocator(), paramScope);
743af6ab5fSopenharmony_ci
753af6ab5fSopenharmony_ci    functionScope->BindParamScope(paramScope);
763af6ab5fSopenharmony_ci    paramScope->BindFunctionScope(functionScope);
773af6ab5fSopenharmony_ci
783af6ab5fSopenharmony_ci    auto flags = ir::ModifierFlags::PUBLIC;
793af6ab5fSopenharmony_ci    flags |= ir::ModifierFlags::ABSTRACT;
803af6ab5fSopenharmony_ci
813af6ab5fSopenharmony_ci    TransformOptionalFieldTypeAnnotation(checker, field);
823af6ab5fSopenharmony_ci    ArenaVector<ir::Expression *> params(checker->Allocator()->Adapter());
833af6ab5fSopenharmony_ci
843af6ab5fSopenharmony_ci    if (isSetter) {
853af6ab5fSopenharmony_ci        auto paramIdent = field->Key()->AsIdentifier()->Clone(checker->Allocator(), nullptr);
863af6ab5fSopenharmony_ci        paramIdent->SetTsTypeAnnotation(field->TypeAnnotation()->Clone(checker->Allocator(), nullptr));
873af6ab5fSopenharmony_ci        paramIdent->TypeAnnotation()->SetParent(paramIdent);
883af6ab5fSopenharmony_ci
893af6ab5fSopenharmony_ci        auto *const paramExpression = checker->AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr);
903af6ab5fSopenharmony_ci        paramExpression->SetRange(paramIdent->Range());
913af6ab5fSopenharmony_ci        auto *const paramVar = std::get<2>(paramScope->AddParamDecl(checker->Allocator(), paramExpression));
923af6ab5fSopenharmony_ci
933af6ab5fSopenharmony_ci        paramIdent->SetVariable(paramVar);
943af6ab5fSopenharmony_ci        paramExpression->SetVariable(paramVar);
953af6ab5fSopenharmony_ci
963af6ab5fSopenharmony_ci        params.push_back(paramExpression);
973af6ab5fSopenharmony_ci    }
983af6ab5fSopenharmony_ci
993af6ab5fSopenharmony_ci    auto signature = ir::FunctionSignature(nullptr, std::move(params), isSetter ? nullptr : field->TypeAnnotation());
1003af6ab5fSopenharmony_ci
1013af6ab5fSopenharmony_ci    auto *func = checker->AllocNode<ir::ScriptFunction>(
1023af6ab5fSopenharmony_ci        checker->Allocator(), ir::ScriptFunction::ScriptFunctionData {nullptr, std::move(signature),
1033af6ab5fSopenharmony_ci                                                                      isSetter ? ir::ScriptFunctionFlags::SETTER
1043af6ab5fSopenharmony_ci                                                                               : ir::ScriptFunctionFlags::GETTER,
1053af6ab5fSopenharmony_ci                                                                      flags, true});
1063af6ab5fSopenharmony_ci
1073af6ab5fSopenharmony_ci    func->SetRange(field->Range());
1083af6ab5fSopenharmony_ci
1093af6ab5fSopenharmony_ci    func->SetScope(functionScope);
1103af6ab5fSopenharmony_ci
1113af6ab5fSopenharmony_ci    auto const &name = field->Key()->AsIdentifier()->Name();
1123af6ab5fSopenharmony_ci    auto methodIdent = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
1133af6ab5fSopenharmony_ci    auto *decl = checker->Allocator()->New<varbinder::VarDecl>(name);
1143af6ab5fSopenharmony_ci    auto var = functionScope->AddDecl(checker->Allocator(), decl, ScriptExtension::ETS);
1153af6ab5fSopenharmony_ci
1163af6ab5fSopenharmony_ci    methodIdent->SetVariable(var);
1173af6ab5fSopenharmony_ci
1183af6ab5fSopenharmony_ci    auto *funcExpr = checker->AllocNode<ir::FunctionExpression>(func);
1193af6ab5fSopenharmony_ci    funcExpr->SetRange(func->Range());
1203af6ab5fSopenharmony_ci    func->AddFlag(ir::ScriptFunctionFlags::METHOD);
1213af6ab5fSopenharmony_ci
1223af6ab5fSopenharmony_ci    auto *method = checker->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, methodIdent, funcExpr,
1233af6ab5fSopenharmony_ci                                                            flags, checker->Allocator(), false);
1243af6ab5fSopenharmony_ci
1253af6ab5fSopenharmony_ci    method->Id()->SetMutator();
1263af6ab5fSopenharmony_ci    method->SetRange(field->Range());
1273af6ab5fSopenharmony_ci    method->Function()->SetIdent(method->Id()->Clone(checker->Allocator(), nullptr));
1283af6ab5fSopenharmony_ci    method->Function()->AddModifier(method->Modifiers());
1293af6ab5fSopenharmony_ci    paramScope->BindNode(func);
1303af6ab5fSopenharmony_ci    functionScope->BindNode(func);
1313af6ab5fSopenharmony_ci
1323af6ab5fSopenharmony_ci    return method;
1333af6ab5fSopenharmony_ci}
1343af6ab5fSopenharmony_ci
1353af6ab5fSopenharmony_cistatic ir::Expression *UpdateInterfacePropertys(checker::ETSChecker *const checker,
1363af6ab5fSopenharmony_ci                                                ir::TSInterfaceBody *const interface)
1373af6ab5fSopenharmony_ci{
1383af6ab5fSopenharmony_ci    if (interface->Body().empty()) {
1393af6ab5fSopenharmony_ci        return interface;
1403af6ab5fSopenharmony_ci    }
1413af6ab5fSopenharmony_ci
1423af6ab5fSopenharmony_ci    auto propertyList = interface->Body();
1433af6ab5fSopenharmony_ci    ArenaVector<ir::AstNode *> newPropertyList(checker->Allocator()->Adapter());
1443af6ab5fSopenharmony_ci
1453af6ab5fSopenharmony_ci    auto scope = NearestScope(interface);
1463af6ab5fSopenharmony_ci    ASSERT(scope->IsClassScope());
1473af6ab5fSopenharmony_ci
1483af6ab5fSopenharmony_ci    for (const auto &prop : propertyList) {
1493af6ab5fSopenharmony_ci        if (!prop->IsClassProperty()) {
1503af6ab5fSopenharmony_ci            newPropertyList.emplace_back(prop);
1513af6ab5fSopenharmony_ci            continue;
1523af6ab5fSopenharmony_ci        }
1533af6ab5fSopenharmony_ci        auto getter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), false);
1543af6ab5fSopenharmony_ci        newPropertyList.emplace_back(getter);
1553af6ab5fSopenharmony_ci
1563af6ab5fSopenharmony_ci        auto methodScope = scope->AsClassScope()->InstanceMethodScope();
1573af6ab5fSopenharmony_ci        auto name = getter->Key()->AsIdentifier()->Name();
1583af6ab5fSopenharmony_ci
1593af6ab5fSopenharmony_ci        auto *decl = checker->Allocator()->New<varbinder::FunctionDecl>(checker->Allocator(), name, getter);
1603af6ab5fSopenharmony_ci
1613af6ab5fSopenharmony_ci        if (methodScope->AddDecl(checker->Allocator(), decl, ScriptExtension::ETS) == nullptr) {
1623af6ab5fSopenharmony_ci            auto prevDecl = methodScope->FindDecl(name);
1633af6ab5fSopenharmony_ci            ASSERT(prevDecl->IsFunctionDecl());
1643af6ab5fSopenharmony_ci            prevDecl->Node()->AsMethodDefinition()->AddOverload(getter);
1653af6ab5fSopenharmony_ci
1663af6ab5fSopenharmony_ci            if (!prop->AsClassProperty()->IsReadonly()) {
1673af6ab5fSopenharmony_ci                auto setter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), true);
1683af6ab5fSopenharmony_ci                newPropertyList.emplace_back(setter);
1693af6ab5fSopenharmony_ci                prevDecl->Node()->AsMethodDefinition()->AddOverload(setter);
1703af6ab5fSopenharmony_ci            }
1713af6ab5fSopenharmony_ci
1723af6ab5fSopenharmony_ci            getter->Function()->Id()->SetVariable(
1733af6ab5fSopenharmony_ci                methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS));
1743af6ab5fSopenharmony_ci            continue;
1753af6ab5fSopenharmony_ci        }
1763af6ab5fSopenharmony_ci
1773af6ab5fSopenharmony_ci        if (!prop->AsClassProperty()->IsReadonly()) {
1783af6ab5fSopenharmony_ci            auto setter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), true);
1793af6ab5fSopenharmony_ci            newPropertyList.emplace_back(setter);
1803af6ab5fSopenharmony_ci            getter->AddOverload(setter);
1813af6ab5fSopenharmony_ci        }
1823af6ab5fSopenharmony_ci        scope->AsClassScope()->InstanceFieldScope()->EraseBinding(name);
1833af6ab5fSopenharmony_ci    }
1843af6ab5fSopenharmony_ci
1853af6ab5fSopenharmony_ci    auto newInterface = checker->AllocNode<ir::TSInterfaceBody>(std::move(newPropertyList));
1863af6ab5fSopenharmony_ci    newInterface->SetRange(interface->Range());
1873af6ab5fSopenharmony_ci    newInterface->SetParent(interface->Parent());
1883af6ab5fSopenharmony_ci
1893af6ab5fSopenharmony_ci    return newInterface;
1903af6ab5fSopenharmony_ci}
1913af6ab5fSopenharmony_ci
1923af6ab5fSopenharmony_cibool InterfacePropertyDeclarationsPhase::Perform(public_lib::Context *ctx, parser::Program *program)
1933af6ab5fSopenharmony_ci{
1943af6ab5fSopenharmony_ci    for (const auto &[_, ext_programs] : program->ExternalSources()) {
1953af6ab5fSopenharmony_ci        (void)_;
1963af6ab5fSopenharmony_ci        for (auto *const extProg : ext_programs) {
1973af6ab5fSopenharmony_ci            Perform(ctx, extProg);
1983af6ab5fSopenharmony_ci        }
1993af6ab5fSopenharmony_ci    }
2003af6ab5fSopenharmony_ci
2013af6ab5fSopenharmony_ci    checker::ETSChecker *const checker = ctx->checker->AsETSChecker();
2023af6ab5fSopenharmony_ci
2033af6ab5fSopenharmony_ci    program->Ast()->TransformChildrenRecursively(
2043af6ab5fSopenharmony_ci        [checker](ir::AstNode *const ast) -> ir::AstNode * {
2053af6ab5fSopenharmony_ci            return ast->IsTSInterfaceBody() ? UpdateInterfacePropertys(checker, ast->AsTSInterfaceBody()) : ast;
2063af6ab5fSopenharmony_ci        },
2073af6ab5fSopenharmony_ci        Name());
2083af6ab5fSopenharmony_ci
2093af6ab5fSopenharmony_ci    return true;
2103af6ab5fSopenharmony_ci}
2113af6ab5fSopenharmony_ci
2123af6ab5fSopenharmony_ci}  // namespace ark::es2panda::compiler
213