* Copyright (c) 2024-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "evaluate/debugInfoDeserialization/methodBuilder.h"
#include "checker/ETSchecker.h"
#include "libarkfile/file-inl.h"
#include "libarkfile/method_data_accessor-inl.h"
#include "libarkfile/proto_data_accessor-inl.h"
#include "evaluate/helpers.h"
namespace ark::es2panda::evaluate {
namespace {
std::string GetFieldName(size_t fieldIdx)
{
std::stringstream sstream;
sstream << "field" << fieldIdx;
return sstream.str();
}
ArenaVector<ir::TypeNode *> GetFunctionParameters(checker::ETSChecker *checker, panda_file::MethodDataAccessor &mda)
{
const auto &pf = mda.GetPandaFile();
ArenaVector<ir::TypeNode *> parameters(checker->Allocator()->Adapter());
mda.EnumerateTypesInProto(
[checker, ¶meters, &pf = std::as_const(pf)](panda_file::Type type, panda_file::File::EntityId classId) {
auto *typeNode = helpers::PandaTypeToTypeNode(pf, type, classId, checker);
ES2PANDA_ASSERT(typeNode);
parameters.push_back(typeNode);
},
true);
return parameters;
}
ir::ReturnStatement *CreateTypedReturnStatement(checker::ETSChecker *checker, ir::TypeNode *type)
{
ES2PANDA_ASSERT(type);
if (type->IsETSPrimitiveType() && type->AsETSPrimitiveType()->GetPrimitiveType() == ir::PrimitiveType::VOID) {
return checker->AllocNode<ir::ReturnStatement>();
}
auto *allocator = checker->Allocator();
auto *apiClass = checker->AllocNode<ir::Identifier>(helpers::DEBUGGER_API_CLASS_NAME, allocator);
auto *prop =
checker->AllocNode<ir::Identifier>(helpers::CreateGetterName(panda_file::Type::TypeId::REFERENCE), allocator);
auto *callee = checker->AllocNode<ir::MemberExpression>(apiClass, prop, ir::MemberExpressionKind::PROPERTY_ACCESS,
false, false);
ArenaVector<ir::Expression *> args(1, checker->AllocNode<ir::NumberLiteral>(lexer::Number((int64_t)0)),
allocator->Adapter());
auto *callExpression = checker->AllocNode<ir::CallExpression>(callee, std::move(args), nullptr, false);
auto *asExpression = checker->AllocNode<ir::TSAsExpression>(callExpression, type->Clone(allocator, nullptr), false);
return checker->AllocNode<ir::ReturnStatement>(asExpression);
}
}
MethodBuilder::MethodBuilder(checker::ETSChecker *checker, panda_file::MethodDataAccessor &mda,
ir::ModifierFlags classModifierFlags)
: checker_(checker), mda_(mda), params_(checker_->Allocator()->Adapter()), classModifierFlags_(classModifierFlags)
{
methodName_ = util::UString(mda_.GetFullName(), checker_->Allocator()).View();
modifierFlags_ = ir::ModifierFlags::EXPORT | helpers::GetModifierFlags(mda_, true);
}
ir::AstNode *MethodBuilder::Build() &&
{
ArenaVector<ir::Statement *> statements(checker_->Allocator()->Adapter());
CollectParametersAndReturnType();
bool isCtor = IsConstructor();
bool isCctor = IsStaticConstructor();
if (isCtor) {
auto *superConstructorCallStatement = CreateSuperConstructorExpressionCall();
statements.push_back(superConstructorCallStatement);
}
auto *retStatement = CreateTypedReturnStatement(checker_, returnType_);
statements.push_back(retStatement);
auto *id = checker_->AllocNode<ir::Identifier>(methodName_, checker_->Allocator());
auto *body = CreateBody(std::move(statements));
if (isCtor) {
return CreateIrConstructor<false>(id, body);
}
if (isCctor) {
return CreateIrConstructor<true>(id, body);
}
return CreateIrMethod(id, body);
}
void MethodBuilder::CollectParametersAndReturnType()
{
auto parameters = GetFunctionParameters(checker_, mda_);
auto *allocator = checker_->Allocator();
for (size_t idx = 1U; idx < parameters.size(); ++idx) {
util::UString paramName(GetFieldName(idx), allocator);
auto *paramIdent = checker_->AllocNode<ir::Identifier>(paramName.View(), parameters[idx], allocator);
auto *param = checker_->AllocNode<ir::ETSParameterExpression>(paramIdent, false, allocator);
params_.push_back(param);
}
returnType_ = parameters[0];
}
ir::BlockStatement *MethodBuilder::CreateBody(ArenaVector<ir::Statement *> statements)
{
bool isAbstractClass = ((classModifierFlags_ & ir::ModifierFlags::ABSTRACT) != 0);
bool isAbstractMethod = ((modifierFlags_ & ir::ModifierFlags::ABSTRACT) != 0);
bool needToCreateBody = !(isAbstractClass && isAbstractMethod);
ir::BlockStatement *body = nullptr;
if (needToCreateBody) {
body = checker_->AllocNode<ir::BlockStatement>(checker_->Allocator(), std::move(statements));
}
return body;
}
ir::ExpressionStatement *MethodBuilder::CreateSuperConstructorExpressionCall()
{
ArenaVector<ir::Expression *> callArguments(checker_->Allocator()->Adapter());
auto *callee = checker_->AllocNode<ir::SuperExpression>();
auto *superConstructorCall =
checker_->AllocNode<ir::CallExpression>(callee, std::move(callArguments), nullptr, false);
return checker_->AllocNode<ir::ExpressionStatement>(superConstructorCall);
}
template <bool IS_STATIC>
ir::AstNode *MethodBuilder::CreateIrConstructor(ir::Identifier *id, ir::BlockStatement *body)
{
auto scriptFuncFlags = ir::ScriptFunctionFlags::EXPRESSION |
(IS_STATIC ? ir::ScriptFunctionFlags::STATIC_BLOCK : ir::ScriptFunctionFlags::CONSTRUCTOR);
auto *funcExpr = CreateFunctionExpression(id, body, scriptFuncFlags);
auto *allocator = checker_->Allocator();
if constexpr (IS_STATIC) {
auto *staticBlock = checker_->AllocNode<ir::ClassStaticBlock>(funcExpr, allocator);
ES2PANDA_ASSERT(staticBlock != nullptr);
staticBlock->AddModifier(ir::ModifierFlags::STATIC);
return staticBlock;
}
return checker_->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::CONSTRUCTOR,
id->Clone(allocator, nullptr), funcExpr, ir::ModifierFlags::NONE,
allocator, false);
}
ir::MethodDefinition *MethodBuilder::CreateIrMethod(ir::Identifier *id, ir::BlockStatement *body)
{
auto *allocator = checker_->Allocator();
auto *funcExpr = CreateFunctionExpression(id, body, ir::ScriptFunctionFlags::METHOD);
ES2PANDA_ASSERT(funcExpr != nullptr);
auto *method = checker_->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD,
funcExpr->Function()->Id()->Clone(allocator, nullptr),
funcExpr, modifierFlags_, allocator, false);
return method;
}
ir::FunctionExpression *MethodBuilder::CreateFunctionExpression(ir::Identifier *id, ir::BlockStatement *body,
ir::ScriptFunctionFlags scriptFuncFlags)
{
auto funcSignature = ir::FunctionSignature(nullptr, std::move(params_), nullptr);
auto *func = checker_->AllocNode<ir::ScriptFunction>(
checker_->Allocator(),
ir::ScriptFunction::ScriptFunctionData {body, std::move(funcSignature), scriptFuncFlags, modifierFlags_});
ES2PANDA_ASSERT(func != nullptr);
ES2PANDA_ASSERT(func != nullptr);
func->SetIdent(id);
func->SetReturnTypeAnnotation(returnType_);
return checker_->AllocNode<ir::FunctionExpression>(func);
}
}