1 /*
2 * Copyright (c) 2024-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 "evaluate/debugInfoDeserialization/methodBuilder.h"
17 #include "checker/ETSchecker.h"
18 #include "libpandafile/file-inl.h"
19 #include "libpandafile/method_data_accessor-inl.h"
20 #include "libpandafile/proto_data_accessor-inl.h"
21 #include "evaluate/helpers.h"
22
23 namespace ark::es2panda::evaluate {
24
25 namespace {
26
GetFieldName(size_t fieldIdx)27 std::string GetFieldName(size_t fieldIdx)
28 {
29 std::stringstream sstream;
30 sstream << "field" << fieldIdx;
31 return sstream.str();
32 }
33
GetFunctionParameters(checker::ETSChecker * checker,panda_file::MethodDataAccessor & mda)34 ArenaVector<ir::TypeNode *> GetFunctionParameters(checker::ETSChecker *checker, panda_file::MethodDataAccessor &mda)
35 {
36 const auto &pf = mda.GetPandaFile();
37 ArenaVector<ir::TypeNode *> parameters(checker->Allocator()->Adapter());
38 mda.EnumerateTypesInProto(
39 [checker, ¶meters, &pf = std::as_const(pf)](panda_file::Type type, panda_file::File::EntityId classId) {
40 auto *typeNode = helpers::PandaTypeToTypeNode(pf, type, classId, checker);
41 ES2PANDA_ASSERT(typeNode);
42 parameters.push_back(typeNode);
43 },
44 true); // true -- skip `this` parameter
45
46 return parameters;
47 }
48
CreateTypedReturnStatement(checker::ETSChecker * checker,ir::TypeNode * type)49 ir::ReturnStatement *CreateTypedReturnStatement(checker::ETSChecker *checker, ir::TypeNode *type)
50 {
51 ES2PANDA_ASSERT(type);
52
53 if (type->IsETSPrimitiveType() && type->AsETSPrimitiveType()->GetPrimitiveType() == ir::PrimitiveType::VOID) {
54 return checker->AllocNode<ir::ReturnStatement>();
55 }
56
57 // Hack for correct validation. This function call won't be executed in compiled code,
58 // as the whole class declaration only mimics the real code loaded into runtime.
59
60 auto *allocator = checker->Allocator();
61 auto *apiClass = checker->AllocNode<ir::Identifier>(helpers::DEBUGGER_API_CLASS_NAME, allocator);
62 auto *prop =
63 checker->AllocNode<ir::Identifier>(helpers::CreateGetterName(panda_file::Type::TypeId::REFERENCE), allocator);
64 auto *callee = checker->AllocNode<ir::MemberExpression>(apiClass, prop, ir::MemberExpressionKind::PROPERTY_ACCESS,
65 false, false);
66
67 ArenaVector<ir::Expression *> args(1, checker->AllocNode<ir::NumberLiteral>("0"), allocator->Adapter());
68 auto *callExpression = checker->AllocNode<ir::CallExpression>(callee, std::move(args), nullptr, false);
69
70 auto *asExpression = checker->AllocNode<ir::TSAsExpression>(callExpression, type->Clone(allocator, nullptr), false);
71 return checker->AllocNode<ir::ReturnStatement>(asExpression);
72 }
73
74 } // namespace
75
MethodBuilder(checker::ETSChecker * checker,panda_file::MethodDataAccessor & mda,ir::ModifierFlags classModifierFlags)76 MethodBuilder::MethodBuilder(checker::ETSChecker *checker, panda_file::MethodDataAccessor &mda,
77 ir::ModifierFlags classModifierFlags)
78 : checker_(checker), mda_(mda), params_(checker_->Allocator()->Adapter()), classModifierFlags_(classModifierFlags)
79 {
80 methodName_ = util::UString(mda_.GetFullName(), checker_->Allocator()).View();
81 modifierFlags_ = ir::ModifierFlags::EXPORT | helpers::GetModifierFlags(mda_, true);
82 }
83
Build()84 ir::AstNode *MethodBuilder::Build() &&
85 {
86 ArenaVector<ir::Statement *> statements(checker_->Allocator()->Adapter());
87
88 CollectParametersAndReturnType();
89
90 bool isCtor = IsConstructor();
91 bool isCctor = IsStaticConstructor();
92
93 if (isCtor) {
94 auto *superConstructorCallStatement = CreateSuperConstructorExpressionCall();
95 statements.push_back(superConstructorCallStatement);
96 }
97
98 auto *retStatement = CreateTypedReturnStatement(checker_, returnType_);
99 statements.push_back(retStatement);
100
101 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
102 auto *id = checker_->AllocNode<ir::Identifier>(methodName_, checker_->Allocator());
103 auto *body = CreateBody(std::move(statements));
104
105 if (isCtor) {
106 return CreateIrConstructor<false>(id, body);
107 }
108 if (isCctor) {
109 return CreateIrConstructor<true>(id, body);
110 }
111 return CreateIrMethod(id, body);
112 }
113
CollectParametersAndReturnType()114 void MethodBuilder::CollectParametersAndReturnType()
115 {
116 auto parameters = GetFunctionParameters(checker_, mda_);
117 auto *allocator = checker_->Allocator();
118
119 // Start from 1, because 0 is return type
120 for (size_t idx = 1U; idx < parameters.size(); ++idx) {
121 util::UString paramName(GetFieldName(idx), allocator);
122 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
123 auto *paramIdent = checker_->AllocNode<ir::Identifier>(paramName.View(), parameters[idx], allocator);
124 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
125 auto *param = checker_->AllocNode<ir::ETSParameterExpression>(paramIdent, false, allocator);
126 params_.push_back(param);
127 }
128
129 returnType_ = parameters[0];
130 }
131
CreateBody(ArenaVector<ir::Statement * > statements)132 ir::BlockStatement *MethodBuilder::CreateBody(ArenaVector<ir::Statement *> statements)
133 {
134 bool isAbstractClass = ((classModifierFlags_ & ir::ModifierFlags::ABSTRACT) != 0);
135 bool isAbstractMethod = ((modifierFlags_ & ir::ModifierFlags::ABSTRACT) != 0);
136 bool needToCreateBody = !(isAbstractClass && isAbstractMethod);
137
138 ir::BlockStatement *body = nullptr;
139 if (needToCreateBody) {
140 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
141 body = checker_->AllocNode<ir::BlockStatement>(checker_->Allocator(), std::move(statements));
142 }
143
144 return body;
145 }
146
CreateSuperConstructorExpressionCall()147 ir::ExpressionStatement *MethodBuilder::CreateSuperConstructorExpressionCall()
148 {
149 // NOTE: in future it may be necessary to pass non empty call args,
150 // but currently frontend do not check args number.
151 ArenaVector<ir::Expression *> callArguments(checker_->Allocator()->Adapter());
152
153 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
154 auto *callee = checker_->AllocNode<ir::SuperExpression>();
155 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
156 auto *superConstructorCall =
157 checker_->AllocNode<ir::CallExpression>(callee, std::move(callArguments), nullptr, false);
158 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
159 return checker_->AllocNode<ir::ExpressionStatement>(superConstructorCall);
160 }
161
162 template <bool IS_STATIC>
CreateIrConstructor(ir::Identifier * id,ir::BlockStatement * body)163 ir::AstNode *MethodBuilder::CreateIrConstructor(ir::Identifier *id, ir::BlockStatement *body)
164 {
165 auto scriptFuncFlags = ir::ScriptFunctionFlags::EXPRESSION |
166 (IS_STATIC ? ir::ScriptFunctionFlags::STATIC_BLOCK : ir::ScriptFunctionFlags::CONSTRUCTOR);
167
168 auto *funcExpr = CreateFunctionExpression(id, body, scriptFuncFlags);
169 auto *allocator = checker_->Allocator();
170
171 if constexpr (IS_STATIC) {
172 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
173 auto *staticBlock = checker_->AllocNode<ir::ClassStaticBlock>(funcExpr, allocator);
174 ES2PANDA_ASSERT(staticBlock != nullptr);
175 staticBlock->AddModifier(ir::ModifierFlags::STATIC);
176 return staticBlock;
177 }
178 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
179 return checker_->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::CONSTRUCTOR,
180 id->Clone(allocator, nullptr), funcExpr, ir::ModifierFlags::NONE,
181 allocator, false);
182 }
183
CreateIrMethod(ir::Identifier * id,ir::BlockStatement * body)184 ir::MethodDefinition *MethodBuilder::CreateIrMethod(ir::Identifier *id, ir::BlockStatement *body)
185 {
186 auto *allocator = checker_->Allocator();
187 auto *funcExpr = CreateFunctionExpression(id, body, ir::ScriptFunctionFlags::METHOD);
188 ES2PANDA_ASSERT(funcExpr != nullptr);
189 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
190 auto *method = checker_->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD,
191 funcExpr->Function()->Id()->Clone(allocator, nullptr),
192 funcExpr, modifierFlags_, allocator, false);
193 return method;
194 }
195
CreateFunctionExpression(ir::Identifier * id,ir::BlockStatement * body,ir::ScriptFunctionFlags scriptFuncFlags)196 ir::FunctionExpression *MethodBuilder::CreateFunctionExpression(ir::Identifier *id, ir::BlockStatement *body,
197 ir::ScriptFunctionFlags scriptFuncFlags)
198 {
199 auto funcSignature = ir::FunctionSignature(nullptr, std::move(params_), nullptr);
200
201 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
202 auto *func = checker_->AllocNode<ir::ScriptFunction>(
203 checker_->Allocator(),
204 ir::ScriptFunction::ScriptFunctionData {body, std::move(funcSignature), scriptFuncFlags, modifierFlags_});
205 ES2PANDA_ASSERT(func != nullptr);
206
207 func->SetIdent(id);
208 func->SetReturnTypeAnnotation(returnType_);
209
210 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
211 return checker_->AllocNode<ir::FunctionExpression>(func);
212 }
213
214 } // namespace ark::es2panda::evaluate
215