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/scriptFunction.h>
26 #include <ir/expressions/assignmentExpression.h>
27 #include <ir/expressions/identifier.h>
28 #include <ir/statements/blockStatement.h>
29 #include <ir/ts/tsParameterProperty.h>
30 #include <util/helpers.h>
31
32 namespace panda::es2panda::compiler {
33
FindLastStatement(const ir::AstNode * & lastNode,const ir::AstNode * currentNode)34 static void FindLastStatement(const ir::AstNode *&lastNode, const ir::AstNode *currentNode)
35 {
36 if (currentNode->IsStatement()) {
37 if (currentNode->Range().start.index > lastNode->Range().start.index) {
38 lastNode = currentNode;
39 }
40 currentNode->Iterate([&lastNode](auto *childNode) {
41 FindLastStatement(lastNode, childNode);
42 });
43 }
44 }
45
CompileSourceBlock(PandaGen * pg,const ir::BlockStatement * block)46 static void CompileSourceBlock(PandaGen *pg, const ir::BlockStatement *block)
47 {
48 bool endReturn = false;
49
50 const auto &statements = block->Statements();
51 pg->SetFirstStmt(statements.empty() ? block : statements.front());
52
53 for (const auto *stmt : statements) {
54 stmt->Compile(pg);
55
56 if (stmt->IsReturnStatement() && (stmt == statements[statements.size() - 1])) {
57 endReturn = true;
58 }
59 }
60
61 if (endReturn) {
62 return;
63 }
64
65 const ir::AstNode *associatedNode = block;
66 if (!statements.empty()) {
67 FindLastStatement(associatedNode, statements.back());
68 }
69 pg->ImplicitReturn(associatedNode);
70 }
71
CompileFunctionParameterDeclaration(PandaGen * pg,const ir::ScriptFunction * func)72 static void CompileFunctionParameterDeclaration(PandaGen *pg, const ir::ScriptFunction *func)
73 {
74 ScopeContext scopeCtx(pg, func->Scope()->ParamScope());
75
76 uint32_t index = 0;
77
78 for (const auto *it : func->Params()) {
79 auto *param = it;
80 if (param->IsTSParameterProperty()) {
81 param = param->AsTSParameterProperty()->Parameter();
82 }
83 LReference ref = LReference::CreateLRef(pg, param, true);
84
85 [[maybe_unused]] binder::Variable *paramVar = ref.Variable();
86
87 if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
88 util::StringView name = util::Helpers::ToStringView(pg->Allocator(), index);
89 paramVar = pg->Scope()->FindLocal(name, binder::ResolveBindingOptions::BINDINGS);
90 }
91
92 ASSERT(paramVar && paramVar->IsLocalVariable());
93
94 VReg paramReg = binder::Binder::MANDATORY_PARAMS_NUMBER + index++;
95 ASSERT(paramVar->LexicalBound() || paramVar->AsLocalVariable()->Vreg() == paramReg);
96
97 // parameter has default value
98 if (param->IsAssignmentPattern()) {
99 RegScope rs(pg);
100
101 ref.Kind() == ReferenceKind::DESTRUCTURING ?
102 pg->LoadAccumulator(func, paramReg) : ref.GetValue();
103
104 auto *nonDefaultLabel = pg->AllocLabel();
105
106 if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
107 auto *loadParamLabel = pg->AllocLabel();
108
109 pg->BranchIfStrictNotUndefined(func, loadParamLabel);
110 param->AsAssignmentPattern()->Right()->Compile(pg);
111 pg->Branch(func, nonDefaultLabel);
112
113 pg->SetLabel(func, loadParamLabel);
114 pg->LoadAccumulator(func, paramReg);
115
116 pg->SetLabel(func, nonDefaultLabel);
117 ref.SetValue();
118 } else {
119 pg->BranchIfStrictNotUndefined(func, nonDefaultLabel);
120
121 param->AsAssignmentPattern()->Right()->Compile(pg);
122 ref.SetValue();
123 pg->SetLabel(func, nonDefaultLabel);
124 }
125
126 continue;
127 }
128
129 if (param->IsRestElement()) {
130 pg->CopyRestArgs(param, func->Params().size() - 1);
131 } else if (ref.Kind() == ReferenceKind::DESTRUCTURING) {
132 pg->LoadAccumulator(func, paramReg);
133 } else {
134 continue;
135 }
136 ref.SetValue();
137 }
138 }
139
CompileInstanceFields(PandaGen * pg,const ir::ScriptFunction * decl)140 static void CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *decl)
141 {
142 const auto &statements = decl->Parent()->Parent()->Parent()->AsClassDefinition()->Body();
143
144 RegScope rs(pg);
145 auto thisReg = pg->AllocReg();
146 pg->GetThis(decl);
147 pg->StoreAccumulator(decl, thisReg);
148
149 for (auto const &stmt : statements) {
150 if (stmt->IsClassProperty()) {
151 const auto *prop = stmt->AsClassProperty();
152 if (prop->IsStatic()) {
153 continue;
154 }
155 if (!prop->Value()) {
156 pg->LoadConst(stmt, Constant::JS_UNDEFINED);
157 } else {
158 RegScope rsProp(pg);
159 prop->Value()->Compile(pg);
160 }
161
162 if (!prop->Key()->IsIdentifier()) {
163 PandaGen::Unimplemented();
164 }
165
166 pg->StoreObjByName(stmt, thisReg, prop->Key()->AsIdentifier()->Name());
167 }
168 }
169 }
170
CompileFunction(PandaGen * pg)171 static void CompileFunction(PandaGen *pg)
172 {
173 const auto *decl = pg->RootNode()->AsScriptFunction();
174
175 // TODO(szilagyia): move after super call
176 if (decl->IsConstructor() && pg->Binder()->Program()->Extension() != ScriptExtension::TS) {
177 CompileInstanceFields(pg, decl);
178 }
179
180 auto *funcParamScope = pg->TopScope()->ParamScope();
181 if (funcParamScope->NameVar()) {
182 RegScope rs(pg);
183 pg->GetFunctionObject(pg->RootNode());
184 pg->StoreAccToLexEnv(pg->RootNode(), funcParamScope->Find(funcParamScope->NameVar()->Name()), true);
185 }
186
187 pg->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION);
188 pg->FunctionEnter();
189 pg->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
190
191 if (pg->IsAsyncFunction()) {
192 CompileFunctionParameterDeclaration(pg, decl);
193 }
194
195 const ir::AstNode *body = decl->Body();
196
197 if (body->IsExpression()) {
198 body->Compile(pg);
199 pg->ExplicitReturn(decl);
200 } else {
201 CompileSourceBlock(pg, body->AsBlockStatement());
202 }
203
204 pg->FunctionExit();
205 }
206
CompileFunctionOrProgram(PandaGen * pg)207 static void CompileFunctionOrProgram(PandaGen *pg)
208 {
209 FunctionRegScope lrs(pg);
210 const auto *topScope = pg->TopScope();
211
212 if (pg->FunctionHasFinalizer()) {
213 ASSERT(topScope->IsFunctionScope());
214
215 if (!pg->IsAsyncFunction()) {
216 CompileFunctionParameterDeclaration(pg, pg->RootNode()->AsScriptFunction());
217 }
218
219 TryContext tryCtx(pg);
220 pg->FunctionInit(tryCtx.GetCatchTable());
221
222 CompileFunction(pg);
223 } else {
224 pg->FunctionInit(nullptr);
225
226 if (topScope->IsFunctionScope() || topScope->IsTSModuleScope() || topScope->IsTSEnumScope()) {
227 CompileFunctionParameterDeclaration(pg, pg->RootNode()->AsScriptFunction());
228 CompileFunction(pg);
229 } else {
230 ASSERT(topScope->IsGlobalScope() || topScope->IsModuleScope());
231 CompileSourceBlock(pg, pg->RootNode()->AsBlockStatement());
232 }
233 }
234 }
235
Compile(PandaGen * pg)236 void Function::Compile(PandaGen *pg)
237 {
238 pg->SetFunctionKind();
239 CompileFunctionOrProgram(pg);
240 pg->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION);
241 pg->CopyFunctionArguments(pg->RootNode());
242 pg->InitializeLexEnv(pg->RootNode());
243 pg->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
244 pg->AdjustSpillInsns();
245 pg->SortCatchTables();
246 }
247
248 } // namespace panda::es2panda::compiler
249