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 "function.h"
17
18#include "varbinder/varbinder.h"
19#include "util/helpers.h"
20#include "varbinder/scope.h"
21#include "varbinder/variable.h"
22#include "compiler/base/lreference.h"
23#include "compiler/core/pandagen.h"
24#include "ir/base/classDefinition.h"
25#include "ir/base/classProperty.h"
26#include "ir/base/scriptFunction.h"
27#include "ir/expressions/assignmentExpression.h"
28#include "ir/expressions/identifier.h"
29#include "ir/statements/blockStatement.h"
30
31namespace ark::es2panda::compiler {
32static void CompileSourceBlock(PandaGen *pg, const ir::BlockStatement *block)
33{
34    const auto &statements = block->Statements();
35    if (statements.empty()) {
36        pg->SetFirstStmt(block);
37        pg->ImplicitReturn(block);
38        return;
39    }
40
41    pg->SetFirstStmt(statements.front());
42
43    for (const auto *stmt : statements) {
44        stmt->Compile(pg);
45    }
46
47    switch (statements.back()->Type()) {
48        case ir::AstNodeType::RETURN_STATEMENT: {
49            return;
50        }
51        case ir::AstNodeType::VARIABLE_DECLARATION:
52        case ir::AstNodeType::FUNCTION_DECLARATION:
53        case ir::AstNodeType::STRUCT_DECLARATION:
54        case ir::AstNodeType::CLASS_DECLARATION: {
55            pg->ImplicitReturn(statements.back());
56            break;
57        }
58        default: {
59            if (pg->IsEval()) {
60                pg->DirectReturn(statements.back());
61            } else {
62                pg->ImplicitReturn(statements.back());
63            }
64        }
65    }
66}
67
68static void CompileFunctionParameterDeclaration(PandaGen *pg, const ir::ScriptFunction *func)
69{
70    ScopeContext scopeCtx(pg, func->Scope()->ParamScope());
71
72    uint32_t index = 0;
73
74    for (const auto *param : func->Params()) {
75        auto ref = JSLReference::Create(pg, param, true);
76
77        [[maybe_unused]] varbinder::Variable *paramVar = ref.Variable();
78
79        if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
80            util::StringView name = util::Helpers::ToStringView(pg->Allocator(), index);
81            paramVar = pg->Scope()->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
82        }
83
84        ASSERT(paramVar && paramVar->IsLocalVariable());
85
86        VReg paramReg = VReg(varbinder::VarBinder::MANDATORY_PARAMS_NUMBER + VReg::PARAM_START + index++);
87        ASSERT(paramVar->LexicalBound() || paramVar->AsLocalVariable()->Vreg() == paramReg);
88
89        if (param->IsAssignmentPattern()) {
90            RegScope rs(pg);
91            pg->LoadAccumulator(func, paramReg);
92            auto *nonDefaultLabel = pg->AllocLabel();
93
94            if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
95                auto *loadParamLabel = pg->AllocLabel();
96
97                pg->BranchIfNotUndefined(func, loadParamLabel);
98                param->AsAssignmentPattern()->Right()->Compile(pg);
99                pg->Branch(func, nonDefaultLabel);
100
101                pg->SetLabel(func, loadParamLabel);
102                pg->LoadAccumulator(func, paramReg);
103
104                pg->SetLabel(func, nonDefaultLabel);
105                ref.SetValue();
106            } else {
107                pg->BranchIfNotUndefined(func, nonDefaultLabel);
108
109                param->AsAssignmentPattern()->Right()->Compile(pg);
110                ref.SetValue();
111                pg->SetLabel(func, nonDefaultLabel);
112            }
113
114            continue;
115        }
116
117        if (param->IsRestElement()) {
118            pg->CopyRestArgs(param, func->Params().size() - 1);
119        } else if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
120            pg->LoadAccumulator(func, paramReg);
121        } else {
122            continue;
123        }
124        ref.SetValue();
125    }
126}
127
128void Function::LoadClassContexts(const ir::AstNode *node, PandaGen *pg, VReg ctor, const util::StringView &name)
129{
130    auto *classDef = util::Helpers::GetContainingClassDefinition(node);
131
132    do {
133        auto res = pg->Scope()->Find(classDef->PrivateId());
134        ASSERT(res.variable);
135
136        if (classDef->HasMatchingPrivateKey(name)) {
137            pg->LoadLexicalVar(node, res.lexLevel, res.variable->AsLocalVariable()->LexIdx());
138            pg->StoreAccumulator(node, ctor);
139            break;
140        }
141
142        classDef = util::Helpers::GetContainingClassDefinition(classDef->Parent());
143    } while (classDef != nullptr);
144}
145
146void Function::IterateOverElements(const ArenaVector<ir::AstNode *> &elements, PandaGen *pg, VReg &ctor, VReg &thisReg,
147                                   VReg &computedInstanceFieldsArray)
148{
149    uint32_t computedInstanceFieldsIndex = 0;
150    for (auto const &element : elements) {
151        if (!element->IsClassProperty()) {
152            continue;
153        }
154
155        const auto *prop = element->AsClassProperty();
156
157        if ((prop->IsStatic())) {
158            continue;
159        }
160
161        if (prop->IsPrivateElement()) {
162            if (prop->Value() == nullptr) {
163                pg->LoadConst(element, Constant::JS_UNDEFINED);
164            } else {
165                RegScope scopeProp(pg);
166                prop->Value()->Compile(pg);
167            }
168
169            pg->ClassPrivateFieldAdd(prop, ctor, thisReg, prop->Key()->AsIdentifier()->Name());
170            continue;
171        }
172
173        RegScope keyScope(pg);
174
175        Operand key;
176        if (prop->IsComputed()) {
177            VReg keyReg = pg->AllocReg();
178            pg->LoadAccumulator(prop, computedInstanceFieldsArray);
179            pg->LoadObjByIndex(prop, computedInstanceFieldsIndex++);
180            pg->StoreAccumulator(prop, keyReg);
181            key = keyReg;
182        } else {
183            key = pg->ToOwnPropertyKey(prop->Key(), false);
184        }
185
186        if (prop->Value() == nullptr) {
187            pg->LoadConst(element, Constant::JS_UNDEFINED);
188        } else {
189            RegScope scopeProp(pg);
190            prop->Value()->Compile(pg);
191        }
192
193        pg->StoreOwnProperty(prop, thisReg, key);
194    }
195}
196
197void Function::CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *decl)
198{
199    const auto klass = util::Helpers::GetClassDefiniton(decl);
200    const auto &elements = klass->Body();
201
202    RegScope rs(pg);
203    auto thisReg = pg->AllocReg();
204    auto ctor = pg->AllocReg();
205    pg->GetThis(decl);
206    pg->StoreAccumulator(decl, thisReg);
207    pg->GetFunctionObject(decl);
208    pg->StoreAccumulator(decl, ctor);
209
210    VReg computedInstanceFieldsArray {};
211
212    if (klass->HasPrivateMethod()) {
213        pg->ClassPrivateMethodOrAccessorAdd(decl, ctor, thisReg);
214    }
215
216    if (klass->HasComputedInstanceField()) {
217        computedInstanceFieldsArray = pg->AllocReg();
218        pg->LoadClassComputedInstanceFields(klass, ctor);
219        pg->StoreAccumulator(klass, computedInstanceFieldsArray);
220    }
221
222    IterateOverElements(elements, pg, ctor, thisReg, computedInstanceFieldsArray);
223}
224
225static void CompileFunction(PandaGen *pg)
226{
227    const auto *decl = pg->RootNode()->AsScriptFunction();
228
229    if (decl->IsConstructor() && (util::Helpers::GetClassDefiniton(decl)->Super() == nullptr)) {
230        Function::CompileInstanceFields(pg, decl);
231    }
232
233    auto *funcParamScope = pg->TopScope()->ParamScope();
234    auto *nameVar = funcParamScope->NameVar();
235
236    if (nameVar != nullptr) {
237        RegScope rs(pg);
238        pg->GetFunctionObject(pg->RootNode());
239        pg->StoreAccToLexEnv(pg->RootNode(), funcParamScope->Find(nameVar->Name()), true);
240    }
241
242    CompileFunctionParameterDeclaration(pg, decl);
243
244    pg->FunctionEnter();
245    const ir::AstNode *body = decl->Body();
246
247    if (body->IsExpression()) {
248        body->Compile(pg);
249        pg->DirectReturn(decl);
250    } else {
251        CompileSourceBlock(pg, body->AsBlockStatement());
252    }
253
254    pg->FunctionExit();
255}
256
257void Function::Compile(PandaGen *pg)
258{
259    FunctionRegScope lrs(pg);
260    auto *topScope = pg->TopScope();
261
262    if (pg->FunctionHasFinalizer()) {
263        ASSERT(topScope->IsFunctionScope());
264
265        TryContext tryCtx(pg);
266        pg->FunctionInit(tryCtx.GetCatchTable());
267
268        CompileFunction(pg);
269    } else {
270        pg->FunctionInit(nullptr);
271
272        if (topScope->IsFunctionScope()) {
273            CompileFunction(pg);
274        } else {
275            ASSERT(topScope->IsGlobalScope() || topScope->IsModuleScope());
276            CompileSourceBlock(pg, pg->RootNode()->AsBlockStatement());
277        }
278    }
279
280    pg->SortCatchTables();
281}
282}  // namespace ark::es2panda::compiler
283