* Copyright (c) 2021-2022 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 "iterators.h"
#include <compiler/core/pandagen.h>
#include <compiler/base/catchTable.h>
#include <compiler/function/functionBuilder.h>
namespace panda::es2panda::compiler {
Iterator::Iterator(PandaGen *pg, const ir::AstNode *node, IteratorType type)
: pg_(pg), node_(node), closed_(pg->AllocReg()), method_(pg->AllocReg()), iterator_(pg->AllocReg()),
nextResult_(pg->AllocReg()), type_(type)
{
if (type_ == IteratorType::ASYNC) {
pg_->GetAsyncIterator(node);
} else {
pg_->GetIterator(node);
}
pg_->StoreAccumulator(node, iterator_);
pg_->LoadObjByName(node_, iterator_, "next");
pg_->StoreAccumulator(node_, method_);
pg_->StoreConst(node_, closed_, Constant::JS_FALSE);
}
void Iterator::GetMethod(util::StringView name) const
{
pg_->LoadObjByName(node_, iterator_, name);
pg_->StoreAccumulator(node_, method_);
}
void Iterator::CallMethodWithValue() const
{
pg_->CallThis(node_, method_, 2);
}
void Iterator::CallMethod() const
{
pg_->CallThis(node_, method_, 1);
}
void Iterator::Next() const
{
CallMethod();
if (type_ == IteratorType::ASYNC) {
pg_->FuncBuilder()->Await(node_);
}
pg_->StoreAccumulator(node_, nextResult_);
pg_->ThrowIfNotObject(node_, nextResult_);
}
void Iterator::Complete() const
{
pg_->LoadObjByName(node_, nextResult_, "done");
}
void Iterator::Value() const
{
pg_->LoadObjByName(node_, nextResult_, "value");
}
void Iterator::Close(bool abruptCompletion) const
{
RegScope rs(pg_);
VReg completion = pg_->AllocReg();
VReg innerResult = pg_->AllocReg();
VReg innerException = pg_->AllocReg();
Label *noReturn = pg_->AllocLabel();
pg_->StoreAccumulator(node_, completion);
pg_->LoadAccumulator(node_, closed_);
pg_->BranchIfTrue(node_, noReturn);
pg_->StoreConst(node_, closed_, Constant::JS_TRUE);
pg_->StoreConst(node_, innerResult, Constant::JS_UNDEFINED);
pg_->StoreConst(node_, innerException, Constant::JS_HOLE);
TryContext tryCtx(pg_);
const auto &labelSet = tryCtx.LabelSet();
pg_->SetLabel(node_, labelSet.TryBegin());
GetMethod("return");
{
pg_->BranchIfUndefined(node_, noReturn);
CallMethod();
if (type_ == IteratorType::ASYNC) {
pg_->FuncBuilder()->Await(node_);
}
pg_->StoreAccumulator(node_, innerResult);
}
pg_->SetLabel(node_, labelSet.TryEnd());
pg_->Branch(node_, labelSet.CatchEnd());
pg_->SetLabel(node_, labelSet.CatchBegin());
pg_->StoreAccumulator(node_, innerException);
pg_->SetLabel(node_, labelSet.CatchEnd());
if (abruptCompletion) {
pg_->LoadAccumulator(node_, completion);
pg_->EmitThrow(node_);
} else {
pg_->LoadAccumulator(node_, innerException);
pg_->EmitRethrow(node_);
}
pg_->LoadAccumulator(node_, innerResult);
pg_->ThrowIfNotObject(node_, innerResult);
pg_->SetLabel(node_, noReturn);
pg_->LoadAccumulator(node_, completion);
if (abruptCompletion) {
pg_->EmitThrow(node_);
}
}
DestructuringIterator::DestructuringIterator(PandaGen *pg, const ir::AstNode *node)
: Iterator(pg, node, IteratorType::SYNC), done_(pg->AllocReg()), result_(pg->AllocReg())
{
pg_->StoreConst(node, done_, Constant::JS_FALSE);
pg_->StoreConst(node, result_, Constant::JS_UNDEFINED);
}
void DestructuringIterator::Step(Label *doneTarget) const
{
TryContext tryCtx(pg_);
const auto &labelSet = tryCtx.LabelSet();
Label *normalClose = pg_->AllocLabel();
Label *noClose = pg_->AllocLabel();
pg_->SetLabel(node_, labelSet.TryBegin());
JumpIfDone(noClose);
Next();
Complete();
pg_->StoreAccumulator(node_, done_);
pg_->BranchIfFalse(node_, normalClose);
pg_->StoreConst(node_, done_, Constant::JS_TRUE);
pg_->LoadConst(node_, Constant::JS_UNDEFINED);
pg_->Branch(node_, noClose);
pg_->SetLabel(node_, normalClose);
Value();
pg_->StoreAccumulator(node_, result_);
pg_->Branch(node_, labelSet.CatchEnd());
pg_->SetLabel(node_, noClose);
OnIterDone(doneTarget);
pg_->SetLabel(node_, labelSet.TryEnd());
pg_->Branch(node_, labelSet.CatchEnd());
pg_->SetLabel(node_, labelSet.CatchBegin());
pg_->StoreAccumulator(node_, result_);
pg_->StoreConst(node_, done_, Constant::JS_TRUE);
pg_->LoadAccumulator(node_, result_);
pg_->EmitThrow(node_);
pg_->SetLabel(node_, labelSet.CatchEnd());
}
void DestructuringIterator::OnIterDone([[maybe_unused]] Label *doneTarget) const
{
pg_->LoadConst(node_, Constant::JS_UNDEFINED);
}
void DestructuringRestIterator::OnIterDone([[maybe_unused]] Label *doneTarget) const
{
pg_->Branch(node_, doneTarget);
}
void DestructuringIterator::JumpIfDone(Label *noClose) const
{
pg_->LoadAccumulator(node_, done_);
pg_->BranchIfTrue(node_, noClose);
}
}