1/*
2 * Copyright (c) 2023 - 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "compiler/lowering/ets/topLevelStmts/globalDeclTransformer.h"
17
18namespace ark::es2panda::compiler {
19
20void GlobalDeclTransformer::FilterDeclarations(ArenaVector<ir::Statement *> &stmts)
21{
22    const auto isDeclCb = [&types = typeDecl_](const ir::AstNode *node) {
23        return types.count(node->Type()) == 0U ||
24               (node->IsExportNamedDeclaration() && !node->AsExportNamedDeclaration()->Specifiers().empty());
25    };
26    stmts.erase(std::remove_if(stmts.begin(), stmts.end(), isDeclCb), stmts.end());
27}
28
29GlobalDeclTransformer::ResultT GlobalDeclTransformer::TransformStatements(const ArenaVector<ir::Statement *> &stmts,
30                                                                          bool addInitializer)
31{
32    addInitializer_ = addInitializer;
33    result_.classProperties.clear();
34    result_.initStatements.clear();
35    for (auto stmt : stmts) {
36        stmt->Accept(this);
37    }
38    return std::move(result_);
39}
40
41void GlobalDeclTransformer::VisitFunctionDeclaration(ir::FunctionDeclaration *funcDecl)
42{
43    auto *funcExpr = util::NodeAllocator::ForceSetParent<ir::FunctionExpression>(allocator_, funcDecl->Function());
44    funcDecl->Function()->SetStart(funcDecl->Function()->Id()->End());
45    funcExpr->SetRange(funcDecl->Function()->Range());
46    ir::MethodDefinitionKind methodKind;
47    if (funcDecl->Function()->IsExtensionMethod()) {
48        methodKind = ir::MethodDefinitionKind::EXTENSION_METHOD;
49    } else {
50        methodKind = ir::MethodDefinitionKind::METHOD;
51    }
52    auto *method = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>(
53        allocator_, methodKind, funcDecl->Function()->Id()->Clone(allocator_, nullptr), funcExpr,
54        funcDecl->Function()->Modifiers(), allocator_, false);
55    method->SetRange(funcDecl->Range());
56
57    if (funcDecl->Function()->IsExported() && funcDecl->Function()->HasExportAlias()) {
58        method->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS);
59    }
60
61    result_.classProperties.emplace_back(method);
62}
63
64void GlobalDeclTransformer::VisitVariableDeclaration(ir::VariableDeclaration *varDecl)
65{
66    for (auto declarator : varDecl->Declarators()) {
67        auto id = declarator->Id()->AsIdentifier();
68        auto typeAnn = id->TypeAnnotation();
69        id->SetTsTypeAnnotation(nullptr);
70        auto *field = util::NodeAllocator::ForceSetParent<ir::ClassProperty>(allocator_, id->Clone(allocator_, nullptr),
71                                                                             declarator->Init(), typeAnn,
72                                                                             varDecl->Modifiers(), allocator_, false);
73        field->SetRange(declarator->Range());
74
75        if (varDecl->IsExported() && varDecl->HasExportAlias()) {
76            field->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS);
77        }
78
79        result_.classProperties.emplace_back(field);
80        if (auto stmt = InitTopLevelProperty(field); stmt != nullptr) {
81            result_.initStatements.emplace_back(stmt);
82        }
83    }
84}
85
86ir::Identifier *GlobalDeclTransformer::RefIdent(const util::StringView &name)
87{
88    auto *const callee = util::NodeAllocator::Alloc<ir::Identifier>(allocator_, name, allocator_);
89    callee->SetReference();
90    return callee;
91}
92
93ir::ExpressionStatement *GlobalDeclTransformer::InitTopLevelProperty(ir::ClassProperty *classProperty)
94{
95    ir::ExpressionStatement *initStmt = nullptr;
96    const auto initializer = classProperty->Value();
97    if (addInitializer_ && !classProperty->IsConst() && initializer != nullptr) {
98        auto *ident = RefIdent(classProperty->Id()->Name());
99        ident->SetRange(classProperty->Id()->Range());
100
101        initializer->SetParent(nullptr);
102        auto *assignmentExpression = util::NodeAllocator::Alloc<ir::AssignmentExpression>(
103            allocator_, ident, initializer, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
104        assignmentExpression->SetRange({ident->Start(), initializer->End()});
105        assignmentExpression->SetTsType(initializer->TsType());
106
107        auto expressionStatement =
108            util::NodeAllocator::Alloc<ir::ExpressionStatement>(allocator_, assignmentExpression);
109        expressionStatement->SetRange(classProperty->Range());
110
111        classProperty->SetRange({ident->Start(), initializer->End()});
112
113        if (classProperty->TypeAnnotation() != nullptr) {
114            classProperty->SetValue(nullptr);
115        } else {
116            // Code will be ignored, but checker is going to deduce the type.
117            classProperty->SetValue(initializer->Clone(allocator_, classProperty)->AsExpression());
118        }
119        initStmt = expressionStatement;
120    } else {
121        classProperty->SetStart(classProperty->Id()->Start());
122    }
123    return initStmt;
124}
125
126void GlobalDeclTransformer::HandleNode(ir::AstNode *node)
127{
128    ASSERT(node->IsStatement());
129    if (typeDecl_.count(node->Type()) == 0U) {
130        ASSERT(!propertiesDecl_.count(node->Type()));
131        result_.initStatements.emplace_back(node->AsStatement());
132    }
133}
134
135}  // namespace ark::es2panda::compiler
136