1/**
2 * Copyright (c) 2021-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 "scriptFunction.h"
17
18#include "checker/TSchecker.h"
19#include "compiler/core/ETSGen.h"
20#include "compiler/core/pandagen.h"
21#include "ir/astDump.h"
22#include "ir/srcDump.h"
23
24namespace ark::es2panda::ir {
25
26ScriptFunction::ScriptFunction(ArenaAllocator *allocator, ScriptFunctionData &&data)
27    : AstNode(AstNodeType::SCRIPT_FUNCTION, data.flags),
28      irSignature_(std::move(data.signature)),
29      body_(data.body),
30      funcFlags_(data.funcFlags),
31      declare_(data.declare),
32      lang_(data.lang),
33      returnStatements_(allocator->Adapter())
34{
35    for (auto *param : irSignature_.Params()) {
36        param->SetParent(this);
37    }
38
39    if (auto *returnType = irSignature_.ReturnType(); returnType != nullptr) {
40        returnType->SetParent(this);
41    }
42
43    if (auto *typeParams = irSignature_.TypeParams(); typeParams != nullptr) {
44        typeParams->SetParent(this);
45    }
46}
47
48std::size_t ScriptFunction::FormalParamsLength() const noexcept
49{
50    std::size_t length = 0U;
51
52    for (const auto *param : irSignature_.Params()) {
53        if (param->IsRestElement() || param->IsAssignmentPattern()) {
54            break;
55        }
56
57        ++length;
58    }
59
60    return length;
61}
62
63void ScriptFunction::SetIdent(Identifier *id) noexcept
64{
65    id_ = id;
66    id_->SetParent(this);
67}
68
69ScriptFunction *ScriptFunction::Clone(ArenaAllocator *allocator, AstNode *parent)
70{
71    ArenaVector<Expression *> params {allocator->Adapter()};
72    for (auto *param : Params()) {
73        params.push_back(param->Clone(allocator, nullptr)->AsExpression());
74    }
75    auto *res = util::NodeAllocator::ForceSetParent<ScriptFunction>(
76        allocator, allocator,
77        ScriptFunctionData {
78            body_ != nullptr ? body_->Clone(allocator, nullptr) : nullptr,
79            FunctionSignature {
80                TypeParams() != nullptr ? TypeParams()->Clone(allocator, nullptr)->AsTSTypeParameterDeclaration()
81                                        : nullptr,
82                std::move(params),
83                ReturnTypeAnnotation() != nullptr ? ReturnTypeAnnotation()->Clone(allocator, nullptr)->AsTypeNode()
84                                                  : nullptr},
85            funcFlags_, flags_, declare_, lang_});
86    res->SetParent(parent);
87    return res;
88}
89
90void ScriptFunction::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
91{
92    if (id_ != nullptr) {
93        if (auto *transformedNode = cb(id_); id_ != transformedNode) {
94            id_->SetTransformedNode(transformationName, transformedNode);
95            id_ = transformedNode->AsIdentifier();
96        }
97    }
98
99    irSignature_.TransformChildren(cb, transformationName);
100
101    if (body_ != nullptr) {
102        if (auto *transformedNode = cb(body_); body_ != transformedNode) {
103            body_->SetTransformedNode(transformationName, transformedNode);
104            body_ = transformedNode;
105        }
106    }
107}
108
109void ScriptFunction::Iterate(const NodeTraverser &cb) const
110{
111    if (id_ != nullptr) {
112        cb(id_);
113    }
114    irSignature_.Iterate(cb);
115    if (body_ != nullptr) {
116        cb(body_);
117    }
118}
119
120void ScriptFunction::SetReturnTypeAnnotation(TypeNode *node) noexcept
121{
122    irSignature_.SetReturnType(node);
123    if (node != nullptr) {
124        node->SetParent(this);
125    }
126}
127
128void ScriptFunction::Dump(ir::AstDumper *dumper) const
129{
130    dumper->Add({{"type", "ScriptFunction"},
131                 {"id", AstDumper::Nullish(id_)},
132                 {"generator", IsGenerator()},
133                 {"async", IsAsyncFunc()},
134                 {"expression", ((funcFlags_ & ir::ScriptFunctionFlags::EXPRESSION) != 0)},
135                 {"params", irSignature_.Params()},
136                 {"returnType", AstDumper::Optional(irSignature_.ReturnType())},
137                 {"typeParameters", AstDumper::Optional(irSignature_.TypeParams())},
138                 {"declare", AstDumper::Optional(declare_)},
139                 {"body", AstDumper::Optional(body_)}});
140
141    if (IsThrowing()) {
142        dumper->Add({"throwMarker", "throws"});
143    } else if (IsRethrowing()) {
144        dumper->Add({"throwMarker", "rethrows"});
145    }
146}
147
148void ScriptFunction::Dump(ir::SrcDumper *dumper) const
149{
150    if (TypeParams() != nullptr) {
151        dumper->Add("<");
152        TypeParams()->Dump(dumper);
153        dumper->Add(">");
154    }
155    dumper->Add("(");
156    for (auto param : Params()) {
157        if (param->IsETSParameterExpression() && param->AsETSParameterExpression()->Ident() != nullptr &&
158            param->AsETSParameterExpression()->Ident()->Name() == varbinder::VarBinder::MANDATORY_PARAM_THIS) {
159            continue;
160        }
161        param->Dump(dumper);
162        if (param != Params().back()) {
163            dumper->Add(", ");
164        }
165    }
166    dumper->Add(")");
167    if (ReturnTypeAnnotation() != nullptr) {
168        dumper->Add(": ");
169        ReturnTypeAnnotation()->Dump(dumper);
170    }
171
172    if (IsThrowing()) {
173        dumper->Add(" throws");
174    }
175
176    if (HasBody()) {
177        if (IsArrow()) {
178            dumper->Add(" =>");
179        }
180        if (body_->IsBlockStatement()) {
181            dumper->Add(" {");
182            if (!body_->AsBlockStatement()->Statements().empty()) {
183                dumper->IncrIndent();
184                dumper->Endl();
185                body_->Dump(dumper);
186                dumper->DecrIndent();
187                dumper->Endl();
188            }
189            dumper->Add("}");
190        } else {
191            dumper->Add(" ");
192            body_->Dump(dumper);
193        }
194    }
195    if (!IsArrow()) {
196        dumper->Endl();
197    }
198}
199
200void ScriptFunction::Compile(compiler::PandaGen *pg) const
201{
202    pg->GetAstCompiler()->Compile(this);
203}
204void ScriptFunction::Compile(compiler::ETSGen *etsg) const
205{
206    etsg->GetAstCompiler()->Compile(this);
207}
208
209checker::Type *ScriptFunction::Check(checker::TSChecker *checker)
210{
211    return checker->GetAnalyzer()->Check(this);
212}
213
214checker::Type *ScriptFunction::Check(checker::ETSChecker *checker)
215{
216    return checker->GetAnalyzer()->Check(this);
217}
218}  // namespace ark::es2panda::ir
219