* Copyright (c) 2021-2026 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 <compiler/core/pandagen.h>
#include <ir/base/classDefinition.h>
#include <ir/base/scriptFunction.h>
namespace panda::es2panda::compiler {
FunctionBuilder::FunctionBuilder(PandaGen *pg, CatchTable *catchTable)
: pg_(pg), catchTable_(catchTable), funcObj_(pg_->AllocReg())
{
}
IteratorType FunctionBuilder::GeneratorKind() const
{
return IteratorType::SYNC;
}
void FunctionBuilder::DirectReturn(const ir::AstNode *node) const
{
pg_->NotifyConcurrentResult(node);
pg_->EmitReturn(node);
}
void FunctionBuilder::ImplicitReturn(const ir::AstNode *node) const
{
const auto *rootNode = pg_->RootNode();
pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION);
if (!rootNode->IsScriptFunction() || !rootNode->AsScriptFunction()->IsConstructor()) {
if (pg_->isDebuggerEvaluateExpressionMode() && rootNode->IsProgram()) {
pg_->NotifyConcurrentResult(node);
pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
pg_->EmitReturn(node);
return;
}
pg_->LoadConst(node, Constant::JS_UNDEFINED);
pg_->NotifyConcurrentResult(node);
bool isEmptyRange = (node->Range().start.line == 0 && node->Range().end.line == 0 &&
node->Range().start.index == 0 && node->Range().end.index == 0);
if (isEmptyRange) {
pg_->EmitReturnUndefined(node);
pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
} else {
pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
pg_->EmitReturnUndefined(node);
}
return;
}
pg_->GetThis(node);
if (pg_->IsDerivedConstructor()) {
pg_->CheckIfSuperCorrectCallBeforeReturn(node);
}
pg_->NotifyConcurrentResult(node);
pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
pg_->EmitReturn(node);
}
void FunctionBuilder::ExplicitReturn(const ir::AstNode *node) const
{
DirectReturn(node);
}
void FunctionBuilder::AsyncYield(const ir::AstNode *node, VReg value, VReg completionType, VReg completionValue) const
{
ASSERT(BuilderKind() == BuilderType::ASYNC_GENERATOR);
RegScope rs(pg_);
VReg done = pg_->AllocReg();
pg_->GeneratorYield(node, funcObj_);
* is at the top of the execution context stack as the running execution context.
* 27.6.3.8.9 Return ! AsyncGeneratorResolve(generator, value, false).
*/
pg_->StoreConst(node, done, Constant::JS_FALSE);
pg_->AsyncGeneratorResolve(node, funcObj_, value, done);
resumeGenerator(node, completionType, completionValue);
}
void FunctionBuilder::SuspendResumeExecution(const ir::AstNode *node, VReg completionType, VReg completionValue) const
{
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
{
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
{
binder::FunctionScope *scope = node->Scope();
auto res = scope->Find(binder::Binder::MANDATORY_PARAM_FUNC);
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();
}
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)
{
if (BuilderKind() == BuilderType::GENERATOR) {
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_->ControlFlowChangeReturn();
handleReturn_ = false;
}
pg_->LoadAccumulator(node, completionValue);
pg_->DirectReturn(node);
pg_->SetLabel(node, notRetLabel);
}
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::THROW));
auto *not_throw_label = pg_->AllocLabel();
pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, completionType, not_throw_label);
pg_->LoadAccumulator(node, completionValue);
pg_->EmitThrow(node);
pg_->SetLabel(node, not_throw_label);
pg_->LoadAccumulator(node, completionValue);
}
void FunctionBuilder::YieldStar(const ir::AstNode *node)
{
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();
VReg iterValue = 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_->ControlFlowChangeReturn();
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, receivedValue);
iterator.Complete();
pg_->BranchIfTrue(node, iteratorComplete);
pg_->LoadAccumulator(node, receivedValue);
if (GeneratorKind() == IteratorType::ASYNC) {
iterator.Value();
Await(node);
pg_->StoreAccumulator(node, iterValue);
AsyncYield(node, iterValue, 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_NOT_EQUAL, receivedType, loopStart);
pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::RETURN));
pg_->StoreAccumulator(node, receivedType);
} else {
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_->ControlFlowChangeReturn();
pg_->LoadAccumulator(node, receivedValue);
}
pg_->DirectReturn(node);
pg_->SetLabel(node, normalOrThrowCompletion);
iterator.Value();
}
}