1 /**
2 * Copyright (c) 2021-2025 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
31 namespace ark::es2panda::compiler {
CompileSourceBlock(PandaGen * pg,const ir::BlockStatement * block)32 static 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
CompileFunctionParameterDeclaration(PandaGen * pg,const ir::ScriptFunction * func)68 static 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 ES2PANDA_ASSERT(paramVar && paramVar->IsLocalVariable());
85
86 VReg paramReg = VReg(varbinder::VarBinder::MANDATORY_PARAMS_NUMBER + VReg::PARAM_START + index++);
87 ES2PANDA_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
LoadClassContexts(const ir::AstNode * node,PandaGen * pg,VReg ctor,const util::StringView & name)128 void Function::LoadClassContexts(const ir::AstNode *node, PandaGen *pg, VReg ctor, const util::StringView &name)
129 {
130 auto *classDef = util::Helpers::GetContainingClassDefinition(node);
131 ES2PANDA_ASSERT(classDef != nullptr);
132
133 do {
134 auto res = pg->Scope()->Find(classDef->InternalName());
135 ES2PANDA_ASSERT(res.variable);
136
137 if (classDef->HasMatchingPrivateKey(name)) {
138 pg->LoadLexicalVar(node, res.lexLevel, res.variable->AsLocalVariable()->LexIdx());
139 pg->StoreAccumulator(node, ctor);
140 break;
141 }
142
143 classDef = util::Helpers::GetContainingClassDefinition(classDef->Parent());
144 } while (classDef != nullptr);
145 }
146
IterateOverElements(const ArenaVector<ir::AstNode * > & elements,PandaGen * pg,VReg & ctor,VReg & thisReg,VReg & computedInstanceFieldsArray)147 void Function::IterateOverElements(const ArenaVector<ir::AstNode *> &elements, PandaGen *pg, VReg &ctor, VReg &thisReg,
148 VReg &computedInstanceFieldsArray)
149 {
150 uint32_t computedInstanceFieldsIndex = 0;
151 for (auto const &element : elements) {
152 if (!element->IsClassProperty()) {
153 continue;
154 }
155
156 const auto *prop = element->AsClassProperty();
157
158 if ((prop->IsStatic())) {
159 continue;
160 }
161
162 if (prop->IsPrivateElement()) {
163 if (prop->Value() == nullptr) {
164 pg->LoadConst(element, Constant::JS_UNDEFINED);
165 } else {
166 RegScope scopeProp(pg);
167 prop->Value()->Compile(pg);
168 }
169
170 pg->ClassPrivateFieldAdd(prop, ctor, thisReg, prop->Key()->AsIdentifier()->Name());
171 continue;
172 }
173
174 RegScope keyScope(pg);
175
176 Operand key;
177 if (prop->IsComputed()) {
178 VReg keyReg = pg->AllocReg();
179 pg->LoadAccumulator(prop, computedInstanceFieldsArray);
180 pg->LoadObjByIndex(prop, computedInstanceFieldsIndex++);
181 pg->StoreAccumulator(prop, keyReg);
182 key = keyReg;
183 } else {
184 key = pg->ToOwnPropertyKey(prop->Key(), false);
185 }
186
187 if (prop->Value() == nullptr) {
188 pg->LoadConst(element, Constant::JS_UNDEFINED);
189 } else {
190 RegScope scopeProp(pg);
191 prop->Value()->Compile(pg);
192 }
193
194 pg->StoreOwnProperty(prop, thisReg, key);
195 }
196 }
197
CompileInstanceFields(PandaGen * pg,const ir::ScriptFunction * decl)198 void Function::CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *decl)
199 {
200 const auto klass = util::Helpers::GetClassDefiniton(decl);
201 const auto &elements = klass->Body();
202
203 RegScope rs(pg);
204 auto thisReg = pg->AllocReg();
205 auto ctor = pg->AllocReg();
206 pg->GetThis(decl);
207 pg->StoreAccumulator(decl, thisReg);
208 pg->GetFunctionObject(decl);
209 pg->StoreAccumulator(decl, ctor);
210
211 VReg computedInstanceFieldsArray {};
212
213 if (klass->HasPrivateMethod()) {
214 pg->ClassPrivateMethodOrAccessorAdd(decl, ctor, thisReg);
215 }
216
217 if (klass->HasComputedInstanceField()) {
218 computedInstanceFieldsArray = pg->AllocReg();
219 pg->LoadClassComputedInstanceFields(klass, ctor);
220 pg->StoreAccumulator(klass, computedInstanceFieldsArray);
221 }
222
223 IterateOverElements(elements, pg, ctor, thisReg, computedInstanceFieldsArray);
224 }
225
CompileFunction(PandaGen * pg)226 static void CompileFunction(PandaGen *pg)
227 {
228 const auto *decl = pg->RootNode()->AsScriptFunction();
229
230 if (decl->IsConstructor() && (util::Helpers::GetClassDefiniton(decl)->Super() == nullptr)) {
231 Function::CompileInstanceFields(pg, decl);
232 }
233
234 auto *funcParamScope = pg->TopScope()->ParamScope();
235 auto *nameVar = funcParamScope->NameVar();
236
237 if (nameVar != nullptr) {
238 RegScope rs(pg);
239 pg->GetFunctionObject(pg->RootNode());
240 pg->StoreAccToLexEnv(pg->RootNode(), funcParamScope->Find(nameVar->Name()), true);
241 }
242
243 CompileFunctionParameterDeclaration(pg, decl);
244
245 pg->FunctionEnter();
246 const ir::AstNode *body = decl->Body();
247
248 if (body->IsExpression()) {
249 body->Compile(pg);
250 pg->DirectReturn(decl);
251 } else {
252 CompileSourceBlock(pg, body->AsBlockStatement());
253 }
254
255 pg->FunctionExit();
256 }
257
Compile(PandaGen * pg)258 void Function::Compile(PandaGen *pg)
259 {
260 FunctionRegScope lrs(pg);
261 auto *topScope = pg->TopScope();
262
263 if (pg->FunctionHasFinalizer()) {
264 ES2PANDA_ASSERT(topScope->IsFunctionScope());
265
266 TryContext tryCtx(pg);
267 pg->FunctionInit(tryCtx.GetCatchTable());
268
269 CompileFunction(pg);
270 } else {
271 pg->FunctionInit(nullptr);
272
273 if (topScope->IsFunctionScope()) {
274 CompileFunction(pg);
275 } else {
276 ES2PANDA_ASSERT(topScope->IsGlobalScope() || topScope->IsModuleScope());
277 CompileSourceBlock(pg, pg->RootNode()->AsBlockStatement());
278 }
279 }
280
281 pg->SortCatchTables();
282 }
283 } // namespace ark::es2panda::compiler
284