1/* 2 * Copyright (c) 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 "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 23namespace ark::es2panda::evaluate { 24 25namespace { 26 27std::string GetFieldName(size_t fieldIdx) 28{ 29 std::stringstream sstream; 30 sstream << "field" << fieldIdx; 31 return sstream.str(); 32} 33 34ArenaVector<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 ASSERT(typeNode); 42 parameters.push_back(typeNode); 43 }, 44 true); // true -- skip `this` parameter 45 46 return parameters; 47} 48 49ir::ReturnStatement *CreateTypedReturnStatement(checker::ETSChecker *checker, ir::TypeNode *type) 50{ 51 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 76MethodBuilder::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 84ir::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 114void MethodBuilder::CollectParametersAndReturnType() 115{ 116 auto parameters = GetFunctionParameters(checker_, mda_); 117 auto *checker = 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), checker); 122 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 123 auto *paramIdent = checker_->AllocNode<ir::Identifier>(paramName.View(), parameters[idx], checker); 124 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 125 auto *param = checker_->AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr); 126 params_.push_back(param); 127 } 128 129 returnType_ = parameters[0]; 130} 131 132ir::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 147ir::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 162template <bool IS_STATIC> 163ir::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 staticBlock->AddModifier(ir::ModifierFlags::STATIC); 175 return staticBlock; 176 } 177 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 178 return checker_->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::CONSTRUCTOR, 179 id->Clone(allocator, nullptr), funcExpr, ir::ModifierFlags::NONE, 180 allocator, false); 181} 182 183ir::MethodDefinition *MethodBuilder::CreateIrMethod(ir::Identifier *id, ir::BlockStatement *body) 184{ 185 auto *allocator = checker_->Allocator(); 186 auto *funcExpr = CreateFunctionExpression(id, body, ir::ScriptFunctionFlags::METHOD); 187 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 188 auto *method = checker_->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, 189 funcExpr->Function()->Id()->Clone(allocator, nullptr), 190 funcExpr, modifierFlags_, allocator, false); 191 return method; 192} 193 194ir::FunctionExpression *MethodBuilder::CreateFunctionExpression(ir::Identifier *id, ir::BlockStatement *body, 195 ir::ScriptFunctionFlags scriptFuncFlags) 196{ 197 auto funcSignature = ir::FunctionSignature(nullptr, std::move(params_), nullptr); 198 199 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 200 auto *func = checker_->AllocNode<ir::ScriptFunction>( 201 checker_->Allocator(), 202 ir::ScriptFunction::ScriptFunctionData {body, std::move(funcSignature), scriptFuncFlags, modifierFlags_}); 203 204 func->SetIdent(id); 205 func->SetReturnTypeAnnotation(returnType_); 206 207 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 208 return checker_->AllocNode<ir::FunctionExpression>(func); 209} 210 211} // namespace ark::es2panda::evaluate 212