* Copyright (c) 2021-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 "functionBuilder.h"
#include "varbinder/varbinder.h"
#include "util/helpers.h"
#include "ir/statement.h"
#include "ir/base/scriptFunction.h"
#include "compiler/base/iterators.h"
#include "compiler/core/pandagen.h"
namespace ark::es2panda::compiler {
FunctionBuilder::FunctionBuilder(PandaGen *pg, CatchTable *catchTable)
: pg_(pg), catchTable_(catchTable), funcObj_(catchTable != nullptr ? pg_->AllocReg() : VReg(VReg::REG_START))
{
}
IteratorType FunctionBuilder::GeneratorKind() const
{
return IteratorType::SYNC;
}
void FunctionBuilder::DirectReturn(const ir::AstNode *node) const
{
pg_->EmitReturn(node);
}
void FunctionBuilder::ImplicitReturn(const ir::AstNode *node) const
{
const auto *rootNode = pg_->RootNode();
if (!rootNode->IsScriptFunction() || !rootNode->AsScriptFunction()->IsConstructor()) {
pg_->EmitReturnUndefined(node);
return;
}
pg_->GetThis(rootNode);
pg_->ThrowIfSuperNotCorrectCall(rootNode, 0);
pg_->EmitReturn(node);
}
void FunctionBuilder::AsyncYield(const ir::AstNode *node, VReg completionType, VReg completionValue) const
{
ES2PANDA_ASSERT(BuilderKind() == BuilderType::ASYNC_GENERATOR);
pg_->GeneratorYield(node, funcObj_);
pg_->SuspendAsyncGenerator(node, funcObj_);
ResumeGenerator(node, completionType, completionValue);
}
void FunctionBuilder::SuspendResumeExecution(const ir::AstNode *node, VReg completionType, VReg completionValue) const
{
ES2PANDA_ASSERT(BuilderKind() == BuilderType::ASYNC || BuilderKind() == BuilderType::ASYNC_GENERATOR ||
BuilderKind() == BuilderType::GENERATOR);
pg_->SuspendGenerator(node, funcObj_);
ResumeGenerator(node, completionType, completionValue);
}
void FunctionBuilder::ResumeGenerator(const ir::AstNode *node, VReg completionType, VReg completionValue) const
{
ES2PANDA_ASSERT(BuilderKind() == BuilderType::ASYNC || BuilderKind() == BuilderType::ASYNC_GENERATOR ||
BuilderKind() == BuilderType::GENERATOR);
pg_->ResumeGenerator(node, funcObj_);
pg_->StoreAccumulator(node, completionValue);
pg_->GetResumeMode(node, funcObj_);
pg_->StoreAccumulator(node, completionType);
}
VReg FunctionBuilder::FunctionReg(const ir::ScriptFunction *node) const
{
varbinder::FunctionScope *scope = node->Scope();
auto res = scope->Find(varbinder::VarBinder::MANDATORY_PARAM_FUNC);
ES2PANDA_ASSERT(res.level == 0 && res.variable->IsLocalVariable());
return res.variable->AsLocalVariable()->Vreg();
}
void FunctionBuilder::Await(const ir::AstNode *node)
{
if (BuilderKind() == BuilderType::NORMAL) {
PandaGen::Unimplemented();
}
ES2PANDA_ASSERT(BuilderKind() == BuilderType::ASYNC || BuilderKind() == BuilderType::ASYNC_GENERATOR);
RegScope rs(pg_);
VReg completionType = pg_->AllocReg();
VReg completionValue = pg_->AllocReg();
pg_->AsyncFunctionAwait(node, funcObj_);
SuspendResumeExecution(node, completionType, completionValue);
HandleCompletion(node, completionType, completionValue);
}
void FunctionBuilder::HandleCompletion(const ir::AstNode *node, VReg completionType, VReg completionValue)
{
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::RETURN));
auto *notRetLabel = pg_->AllocLabel();
pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, completionType, notRetLabel);
if (!handleReturn_) {
handleReturn_ = true;
pg_->ControlFlowChangeBreak();
handleReturn_ = false;
}
pg_->LoadAccumulator(node, completionValue);
pg_->DirectReturn(node);
pg_->SetLabel(node, notRetLabel);
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::THROW));
auto *notThrowLabel = pg_->AllocLabel();
pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, completionType, notThrowLabel);
pg_->LoadAccumulator(node, completionValue);
pg_->EmitThrow(node);
pg_->SetLabel(node, notThrowLabel);
pg_->LoadAccumulator(node, completionValue);
}
void FunctionBuilder::YieldStar(const ir::AstNode *node)
{
ES2PANDA_ASSERT(BuilderKind() == BuilderType::GENERATOR || BuilderKind() == BuilderType::ASYNC_GENERATOR);
RegScope rs(pg_);
auto *loopStart = pg_->AllocLabel();
auto *returnCompletion = pg_->AllocLabel();
auto *throwCompletion = pg_->AllocLabel();
auto *callMethod = pg_->AllocLabel();
auto *normalOrThrowCompletion = pg_->AllocLabel();
auto *iteratorComplete = pg_->AllocLabel();
Iterator iterator(pg_, node, GeneratorKind());
VReg receivedValue = iterator.NextResult();
VReg receivedType = pg_->AllocReg();
VReg nextMethod = pg_->AllocReg();
VReg exitReturn = pg_->AllocReg();
pg_->StoreConst(node, receivedValue, Constant::JS_UNDEFINED);
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::NEXT));
pg_->StoreAccumulator(node, receivedType);
pg_->MoveVreg(node, nextMethod, iterator.Method());
pg_->SetLabel(node, loopStart);
pg_->StoreConst(node, exitReturn, Constant::JS_FALSE);
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::NEXT));
pg_->Condition(node, lexer::TokenType::PUNCTUATOR_STRICT_EQUAL, receivedType, throwCompletion);
pg_->MoveVreg(node, iterator.Method(), nextMethod);
pg_->Branch(node, callMethod);
pg_->SetLabel(node, throwCompletion);
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::THROW));
pg_->Condition(node, lexer::TokenType::PUNCTUATOR_STRICT_EQUAL, receivedType, returnCompletion);
iterator.GetMethod("throw");
pg_->BranchIfNotUndefined(node, callMethod);
iterator.Close(false);
pg_->ThrowThrowNotExist(node);
pg_->SetLabel(node, returnCompletion);
pg_->StoreConst(node, exitReturn, Constant::JS_TRUE);
iterator.GetMethod("return");
pg_->BranchIfNotUndefined(node, callMethod);
pg_->ControlFlowChangeBreak();
pg_->LoadAccumulator(node, receivedValue);
if (GeneratorKind() == IteratorType::ASYNC) {
Await(node);
}
pg_->DirectReturn(node);
pg_->SetLabel(node, callMethod);
iterator.CallMethodWithValue();
if (GeneratorKind() == IteratorType::ASYNC) {
Await(node);
}
pg_->StoreAccumulator(node, receivedValue);
pg_->ThrowIfNotObject(node);
iterator.Complete();
pg_->BranchIfTrue(node, iteratorComplete);
if (GeneratorKind() == IteratorType::ASYNC) {
iterator.Value();
Await(node);
AsyncYield(node, receivedType, receivedValue);
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::RETURN));
pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, receivedType, loopStart);
pg_->LoadAccumulator(node, receivedValue);
pg_->AsyncFunctionAwait(node, funcObj_);
SuspendResumeExecution(node, receivedType, receivedValue);
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::THROW));
pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, receivedType, returnCompletion);
} else {
pg_->LoadAccumulator(node, receivedValue);
pg_->GeneratorYield(node, funcObj_);
SuspendResumeExecution(node, receivedType, receivedValue);
}
pg_->Branch(node, loopStart);
pg_->SetLabel(node, iteratorComplete);
pg_->LoadAccumulator(node, exitReturn);
pg_->BranchIfFalse(node, normalOrThrowCompletion);
iterator.Value();
if (pg_->CheckControlFlowChange()) {
pg_->StoreAccumulator(node, receivedValue);
pg_->ControlFlowChangeBreak();
pg_->LoadAccumulator(node, receivedValue);
}
pg_->DirectReturn(node);
pg_->SetLabel(node, normalOrThrowCompletion);
iterator.Value();
}
}