1/**
2 * Copyright (c) 2021 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 "asyncGeneratorFunctionBuilder.h"
17
18#include <compiler/base/catchTable.h>
19#include <compiler/core/pandagen.h>
20#include <ir/base/scriptFunction.h>
21
22namespace panda::es2panda::compiler {
23void AsyncGeneratorFunctionBuilder::Prepare(const ir::ScriptFunction *node)
24{
25    RegScope rs(pg_);
26    VReg callee = FunctionReg(node);
27    VReg completionType = pg_->AllocReg();
28    VReg completionValue = pg_->AllocReg();
29
30    pg_->CreateAsyncGeneratorObj(node, callee);
31    pg_->StoreAccumulator(node, funcObj_);
32
33    pg_->SetLabel(node, catchTable_->LabelSet().TryBegin());
34
35    pg_->LoadConst(node, Constant::JS_UNDEFINED);
36    SuspendResumeExecution(node, completionType, completionValue);
37}
38
39void AsyncGeneratorFunctionBuilder::CleanUp(const ir::ScriptFunction *node) const
40{
41    const auto &labelSet = catchTable_->LabelSet();
42
43    RegScope rs(pg_);
44    VReg retVal = pg_->AllocReg();
45
46    pg_->SetLabel(node, labelSet.TryEnd());
47    pg_->SetLabel(node, labelSet.CatchBegin());
48    pg_->StoreAccumulator(node, retVal);
49    pg_->GeneratorComplete(node, funcObj_);
50    pg_->LoadAccumulator(node, retVal);
51    pg_->AsyncGeneratorReject(node, funcObj_);
52    pg_->EmitReturn(node);
53    pg_->SetLabel(node, labelSet.CatchEnd());
54}
55
56void AsyncGeneratorFunctionBuilder::DirectReturn(const ir::AstNode *node) const
57{
58    RegScope rs(pg_);
59    VReg retVal = pg_->AllocReg();
60    VReg canSuspend = pg_->AllocReg();
61
62    pg_->StoreAccumulator(node, retVal);
63    pg_->StoreConst(node, canSuspend, Constant::JS_TRUE);
64
65    pg_->GeneratorComplete(node, funcObj_);
66    pg_->AsyncGeneratorResolve(node, funcObj_, retVal, canSuspend);
67    // The INVALID_SOURCE_LOCATION is set in the implicitReturn method. When implicitReturn invokes
68    // the DirectReturn method, the EmitReturn needs to be associated with a valid node
69    pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
70    pg_->EmitReturn(node);
71}
72
73void AsyncGeneratorFunctionBuilder::ImplicitReturn(const ir::AstNode *node) const
74{
75    pg_->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION);
76    pg_->LoadConst(node, Constant::JS_UNDEFINED);
77    DirectReturn(node);
78}
79
80void AsyncGeneratorFunctionBuilder::ExplicitReturn(const ir::AstNode *node) const
81{
82    RegScope rs(pg_);
83    VReg resumeType = pg_->AllocReg();
84    VReg resumeValue = pg_->AllocReg();
85    VReg canSuspend = pg_->AllocReg();
86
87    pg_->AsyncFunctionAwait(node, funcObj_);
88    SuspendResumeExecution(node, resumeType, resumeValue);
89    pg_->GeneratorComplete(node, funcObj_);
90    pg_->StoreConst(node, canSuspend, Constant::JS_TRUE);
91    pg_->AsyncGeneratorResolve(node, funcObj_, resumeValue, canSuspend);
92    pg_->EmitReturn(node);
93}
94
95void AsyncGeneratorFunctionBuilder::Yield(const ir::AstNode *node)
96{
97    RegScope rs(pg_);
98    VReg value = pg_->AllocReg();
99    VReg resumeType = pg_->AllocReg();
100    VReg resumeValue = pg_->AllocReg();
101    auto *notReturn = pg_->AllocLabel();
102    auto *normalCompletion = pg_->AllocLabel();
103    auto *notThrowCompletion = pg_->AllocLabel();
104
105    // 27.6.3.8.5 Set value to ? Await(value).
106    Await(node);
107    pg_->StoreAccumulator(node, value);
108
109    AsyncYield(node, value, resumeType, resumeValue);
110
111    // 27.6.3.8.8.a If resumptionValue.[[Type]] is not return
112    pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::RETURN));
113    pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, resumeType, notReturn);
114
115    // 27.6.3.8.8.b Let awaited be Await(resumptionValue.[[Value]]).
116    pg_->LoadAccumulator(node, resumeValue);
117    pg_->AsyncFunctionAwait(node, funcObj_);
118    SuspendResumeExecution(node, resumeType, resumeValue);
119
120    // 27.6.3.8.8.c. If awaited.[[Type]] is throw, return Completion(awaited).
121    pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::THROW));
122    pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, resumeType, normalCompletion);
123    pg_->LoadAccumulator(node, resumeValue);
124    pg_->EmitThrow(node);
125
126    pg_->SetLabel(node, normalCompletion);
127    // 27.6.3.8.8.d. Assert: awaited.[[Type]] is normal.
128    // 27.6.3.8.8.e. Return Completion { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }
129    pg_->ControlFlowChangeBreak();
130    pg_->LoadAccumulator(node, resumeValue);
131    DirectReturn(node);
132
133    pg_->SetLabel(node, notReturn);
134    // 27.6.3.8.8.a return Completion(resumptionValue)
135    pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::THROW));
136    pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, resumeType, notThrowCompletion);
137    pg_->LoadAccumulator(node, resumeValue);
138    pg_->EmitThrow(node);
139    pg_->SetLabel(node, notThrowCompletion);
140    pg_->LoadAccumulator(node, resumeValue);
141}
142
143IteratorType AsyncGeneratorFunctionBuilder::GeneratorKind() const
144{
145    return IteratorType::ASYNC;
146}
147}  // namespace panda::es2panda::compiler
148