1/**
2 * Copyright (c) 2021-2022 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 <binder/binder.h>
19#include <binder/scope.h>
20#include <binder/variable.h>
21#include <compiler/base/lreference.h>
22#include <compiler/core/pandagen.h>
23#include <ir/base/classDefinition.h>
24#include <ir/base/classProperty.h>
25#include <ir/base/classStaticBlock.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#include <ir/ts/tsParameterProperty.h>
31#include <util/helpers.h>
32
33namespace panda::es2panda::compiler {
34
35static void FindLastStatement(const ir::AstNode *&lastNode, const ir::AstNode *currentNode)
36{
37    if (currentNode->IsStatement()) {
38        if (currentNode->Range().start.index > lastNode->Range().start.index) {
39            lastNode = currentNode;
40        }
41        currentNode->Iterate([&lastNode](auto *childNode) {
42            FindLastStatement(lastNode, childNode);
43        });
44    }
45}
46
47static void CompileSourceBlock(PandaGen *pg, const ir::BlockStatement *block)
48{
49    bool endReturn = false;
50
51    const auto &statements = block->Statements();
52    pg->SetFirstStmt(statements.empty() ? block : statements.front());
53
54    for (const auto *stmt : statements) {
55        stmt->Compile(pg);
56
57        if (stmt->IsReturnStatement() && (stmt == statements[statements.size() - 1])) {
58            endReturn = true;
59        }
60    }
61
62    if (endReturn) {
63        return;
64    }
65
66    const ir::AstNode *associatedNode = block;
67    if (!statements.empty()) {
68        FindLastStatement(associatedNode, statements.back());
69    }
70    pg->ImplicitReturn(associatedNode);
71}
72
73static void CompileFunctionParameterDeclaration(PandaGen *pg, const ir::ScriptFunction *func)
74{
75    ScopeContext scopeCtx(pg, func->Scope()->ParamScope());
76
77    uint32_t index = 0;
78
79    for (const auto *it : func->Params()) {
80        auto *param = it;
81        if (param->IsTSParameterProperty()) {
82            param = param->AsTSParameterProperty()->Parameter();
83        }
84        LReference ref = LReference::CreateLRef(pg, param, true);
85
86        [[maybe_unused]] binder::Variable *paramVar = ref.Variable();
87
88        if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
89            util::StringView name = util::Helpers::ToStringView(pg->Allocator(), index);
90            paramVar = pg->Scope()->FindLocal(name, binder::ResolveBindingOptions::BINDINGS);
91        }
92
93        ASSERT(paramVar && paramVar->IsLocalVariable());
94
95        VReg paramReg = binder::Binder::MANDATORY_PARAMS_NUMBER + index++;
96        ASSERT(paramVar->LexicalBound() || paramVar->AsLocalVariable()->Vreg() == paramReg);
97
98        // parameter has default value
99        if (param->IsAssignmentPattern()) {
100            RegScope rs(pg);
101
102            ref.Kind() == ReferenceKind::DESTRUCTURING ?
103                pg->LoadAccumulator(func, paramReg) : ref.GetValue();
104
105            auto *nonDefaultLabel = pg->AllocLabel();
106
107            if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
108                auto *loadParamLabel = pg->AllocLabel();
109
110                pg->BranchIfStrictNotUndefined(func, loadParamLabel);
111                param->AsAssignmentPattern()->Right()->Compile(pg);
112                pg->Branch(func, nonDefaultLabel);
113
114                pg->SetLabel(func, loadParamLabel);
115                pg->LoadAccumulator(func, paramReg);
116
117                pg->SetLabel(func, nonDefaultLabel);
118                ref.SetValue();
119            } else {
120                pg->BranchIfStrictNotUndefined(func, nonDefaultLabel);
121
122                param->AsAssignmentPattern()->Right()->Compile(pg);
123                ref.SetValue();
124                pg->SetLabel(func, nonDefaultLabel);
125            }
126
127            continue;
128        }
129        // There's extra optimization for super call in default constuctor since api13,
130        // no need to generate copyrestargs in this scene.
131        if (param->IsRestElement() && (!func->HasFlag(ir::ScriptFunctionFlags::GENERATED_CONSTRUCTOR) ||
132            pg->Binder()->Program()->TargetApiVersion() < util::Helpers::SUPER_CALL_OPT_MIN_SUPPORTED_API_VERSION)) {
133            pg->CopyRestArgs(param, func->Params().size() - 1);
134        } else if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
135            pg->LoadAccumulator(func, paramReg);
136        } else {
137            continue;
138        }
139        ref.SetValue();
140    }
141}
142
143static void CompileField(PandaGen *pg, const ir::ClassProperty *prop, VReg thisReg, int32_t level)
144{
145    RegScope rs(pg);
146    Operand op;
147    binder::PrivateNameFindResult result;
148    if (prop->IsPrivate()) {
149        result = pg->Scope()->FindPrivateName(prop->Key()->AsPrivateIdentifier()->Name());
150    } else if (prop->IsComputed() && prop->NeedCompileKey()) {
151        auto slot = prop->Parent()->AsClassDefinition()->GetSlot(prop->Key());
152        pg->LoadLexicalVar(prop->Key(), level, slot);
153        op = pg->AllocReg();
154        pg->StoreAccumulator(prop->Key(), std::get<VReg>(op));
155    } else {
156        op = pg->ToPropertyKey(prop->Key(), prop->IsComputed());
157    }
158
159    if (!prop->Value()) {
160        pg->LoadConst(prop, Constant::JS_UNDEFINED);
161    } else {
162        RegScope rsProp(pg);
163        prop->Value()->Compile(pg);
164    }
165
166    if (prop->IsPrivate()) {
167        pg->DefineClassPrivateField(prop, result.lexLevel, result.result.slot, thisReg);
168    } else {
169        pg->DefineOwnProperty(prop, thisReg, op);
170    }
171}
172
173static void CompileClassInitializer(PandaGen *pg, const ir::ScriptFunction *decl, bool isStatic)
174{
175    const auto *classDef = decl->Parent()->Parent()->Parent()->AsClassDefinition();
176    const auto &statements = classDef->Body();
177
178    RegScope rs(pg);
179    auto thisReg = pg->AllocReg();
180    pg->GetThis(decl);
181    pg->StoreAccumulator(decl, thisReg);
182    auto [level, slot] = pg->Scope()->Find(nullptr, true);
183
184    if (!isStatic && classDef->HasInstancePrivateMethod()) {
185        binder::PrivateNameFindResult result = pg->Scope()->FindPrivateName("#method");
186        pg->LoadConst(classDef, Constant::JS_UNDEFINED);
187        pg->DefineClassPrivateField(classDef, result.lexLevel, result.result.slot, thisReg);
188    }
189    for (auto const &stmt : statements) {
190        if (stmt->IsMethodDefinition()) {
191            continue;
192        }
193
194        if (stmt->IsClassProperty()) {
195            const auto *prop = stmt->AsClassProperty();
196
197            // Do not process non-static public fields when not using define semantic.
198            if (!prop->IsPrivate() && !prop->IsStatic() && !pg->Binder()->Program()->UseDefineSemantic()) {
199                continue;
200            }
201
202            if (prop->IsStatic() == isStatic) {
203                CompileField(pg, prop, thisReg, level);
204            }
205            continue;
206        }
207
208        if (!isStatic) {
209            continue;
210        }
211
212        ASSERT(stmt->IsClassStaticBlock());
213        const auto *staticBlock = stmt->AsClassStaticBlock();
214        staticBlock->Compile(pg);
215    }
216}
217
218static void CompileFunction(PandaGen *pg)
219{
220    const auto *decl = pg->RootNode()->AsScriptFunction();
221
222    if (decl->IsConstructor()) {
223        const auto *classDef = util::Helpers::GetClassDefiniton(decl);
224        if (classDef->Super() == nullptr && classDef->NeedInstanceInitializer()) {
225            RegScope rs(pg);
226            auto thisReg = pg->AllocReg();
227
228            pg->GetThis(decl);
229            pg->StoreAccumulator(decl, thisReg);
230
231            auto [level, slot] = pg->Scope()->Find(classDef->InstanceInitializer()->Key());
232            pg->LoadLexicalVar(decl, level, slot);
233            pg->CallInit(decl, thisReg);
234        }
235    }
236
237    if (decl->IsStaticInitializer() || decl->IsInstanceInitializer()) {
238        CompileClassInitializer(pg, decl, decl->IsStaticInitializer());
239        pg->ImplicitReturn(decl);
240        return;
241    }
242
243    auto *funcParamScope = pg->TopScope()->ParamScope();
244    if (funcParamScope->NameVar()) {
245        RegScope rs(pg);
246        pg->GetFunctionObject(pg->RootNode());
247        pg->StoreAccToLexEnv(pg->RootNode(), funcParamScope->Find(funcParamScope->NameVar()->Name()), true);
248    }
249
250    pg->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION);
251    pg->FunctionEnter();
252    pg->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
253
254    if (pg->IsAsyncFunction()) {
255        CompileFunctionParameterDeclaration(pg, decl);
256    }
257
258    const ir::AstNode *body = decl->Body();
259
260    if (body->IsExpression()) {
261        body->Compile(pg);
262        pg->ExplicitReturn(decl);
263    } else {
264        CompileSourceBlock(pg, body->AsBlockStatement());
265    }
266
267    pg->FunctionExit();
268}
269
270static void CompileFunctionOrProgram(PandaGen *pg)
271{
272    FunctionRegScope lrs(pg);
273    const auto *topScope = pg->TopScope();
274
275    if (pg->FunctionHasFinalizer()) {
276        ASSERT(topScope->IsFunctionScope() || topScope->IsModuleScope());
277
278        if (!pg->IsAsyncFunction()) {
279            CompileFunctionParameterDeclaration(pg, pg->RootNode()->AsScriptFunction());
280        }
281
282        TryContext tryCtx(pg);
283        pg->FunctionInit(tryCtx.GetCatchTable());
284        if (topScope->IsModuleScope()) {
285            pg->FunctionEnter();
286            CompileSourceBlock(pg, pg->RootNode()->AsBlockStatement());
287            pg->FunctionExit();
288            return;
289        }
290        CompileFunction(pg);
291    } else {
292        pg->FunctionInit(nullptr);
293
294        if (topScope->IsFunctionScope() || topScope->IsTSModuleScope() || topScope->IsTSEnumScope()) {
295            CompileFunctionParameterDeclaration(pg, pg->RootNode()->AsScriptFunction());
296            CompileFunction(pg);
297        } else {
298            ASSERT(topScope->IsGlobalScope() || topScope->IsModuleScope());
299            CompileSourceBlock(pg, pg->RootNode()->AsBlockStatement());
300        }
301    }
302}
303
304void Function::Compile(PandaGen *pg)
305{
306    pg->SetFunctionKind();
307    pg->SetInSendable();
308    CompileFunctionOrProgram(pg);
309    pg->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION);
310    pg->CopyFunctionArguments(pg->RootNode());
311    pg->InitializeLexEnv(pg->RootNode());
312    pg->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
313    pg->AdjustSpillInsns();
314    pg->SortCatchTables();
315    pg->ReArrangeIc();
316}
317
318}  // namespace panda::es2panda::compiler
319