/* * Copyright (c) 2021 - 2023 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 panda::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 { 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 { 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 { varbinder::FunctionScope *scope = node->Scope(); auto res = scope->Find(varbinder::VarBinder::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) { // NOTE: frobert. Implement top-level await 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) { // .return(value) 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); // .throw(value) 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); // .next(value) pg_->SetLabel(node, notThrowLabel); 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(); // 4. Let iteratorRecord be ? GetIterator(value, generatorKind). Iterator iterator(pg_, node, GeneratorKind()); // 6. Let received be NormalCompletion(undefined). 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()); // 7. Repeat pg_->SetLabel(node, loopStart); pg_->StoreConst(node, exitReturn, Constant::JS_FALSE); // a. If received.[[Type]] is normal, then 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); // b. Else if received.[[Type]] is throw, then pg_->SetLabel(node, throwCompletion); pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::THROW)); pg_->Condition(node, lexer::TokenType::PUNCTUATOR_STRICT_EQUAL, receivedType, returnCompletion); // i. Let throw be ? GetMethod(iterator, "throw"). iterator.GetMethod("throw"); // ii. If throw is not undefined, then pg_->BranchIfNotUndefined(node, callMethod); // iii. Else, // 1. NOTE: If iterator does not have a throw method, this throw is going to terminate the yield* loop. But first we // need to give iterator a chance to clean up. // 2. Let closeCompletion be Completion { [[Type]]: normal, [[Value]]: empty, [[Target]]: empty }. // 3. If generatorKind is async, perform ? AsyncIteratorClose(iteratorRecord, closeCompletion). // 4. Else, perform ? IteratorClose(iteratorRecord, closeCompletion). iterator.Close(false); // 5. NOTE: The next step throws a TypeError to indicate that there was a yield* protocol violation: iterator does // not have a throw method. // 6. Throw a TypeError exception. pg_->ThrowThrowNotExist(node); // c. Else, // i. Assert: received.[[Type]] is return. pg_->SetLabel(node, returnCompletion); pg_->StoreConst(node, exitReturn, Constant::JS_TRUE); // ii. Let return be ? GetMethod(iterator, "return"). iterator.GetMethod("return"); // iii. If return is undefined, then pg_->BranchIfNotUndefined(node, callMethod); // 1. If generatorKind is async, set received.[[Value]] to ? Await(received.[[Value]]). pg_->ControlFlowChangeBreak(); pg_->LoadAccumulator(node, receivedValue); if (GeneratorKind() == IteratorType::ASYNC) { Await(node); } // 2. Return Completion(received). pg_->DirectReturn(node); pg_->SetLabel(node, callMethod); // i. Let innerResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « received.[[Value]] »). // 1. Let innerResult be ? Call(throw, iterator, « received.[[Value]] »). // iv. Let innerReturnResult be ? Call(return, iterator, « received.[[Value]] »). iterator.CallMethodWithValue(); // ii. ii. If generatorKind is async, set innerResult to ? Await(innerResult). // 2. If generatorKind is async, set innerResult to ? Await(innerResult). // v. If generatorKind is async, set innerReturnResult to ? Await(innerReturnResult). if (GeneratorKind() == IteratorType::ASYNC) { Await(node); } pg_->StoreAccumulator(node, receivedValue); // ii. If Type(innerResult) is not Object, throw a TypeError exception. // 4. If Type(innerResult) is not Object, throw a TypeError exception. // vi. If Type(innerReturnResult) is not Object, throw a TypeError exception. pg_->ThrowIfNotObject(node); // iv. Let done be ? IteratorComplete(innerResult). // v. Let done be ? IteratorComplete(innerResult). // vii. Let done be ? IteratorComplete(innerReturnResult). iterator.Complete(); pg_->BranchIfTrue(node, iteratorComplete); // vi. If generatorKind is async, set received to AsyncGeneratorYield(? IteratorValue(innerResult)). // 7. If generatorKind is async, set received to AsyncGeneratorYield(? IteratorValue(innerResult)). // ix. If generatorKind is async, set received to AsyncGeneratorYield(? IteratorValue(innerReturnResult)). if (GeneratorKind() == IteratorType::ASYNC) { iterator.Value(); // 27.6.3.8 AsyncGeneratorYield // 5. Set value to ? Await(value). Await(node); // 6. Set generator.[[AsyncGeneratorState]] to suspendedYield. AsyncYield(node, receivedType, receivedValue); // a. If resumptionValue.[[Type]] is not return pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::RETURN)); pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, receivedType, loopStart); // b. Let awaited be Await(resumptionValue.[[Value]]). pg_->LoadAccumulator(node, receivedValue); pg_->AsyncFunctionAwait(node, funcObj_); SuspendResumeExecution(node, receivedType, receivedValue); // c. If awaited.[[Type]] is throw, return Completion(awaited). pg_->LoadAccumulatorInt(node, static_cast<int32_t>(ResumeMode::THROW)); // d. Assert: awaited.[[Type]] is normal. // e. Return Completion { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }. pg_->Condition(node, lexer::TokenType::PUNCTUATOR_EQUAL, receivedType, returnCompletion); } else { // vii. Else, set received to GeneratorYield(innerResult). // 8. Else, set received to GeneratorYield(innerResult). // x. Else, set received to GeneratorYield(innerReturnResult). pg_->LoadAccumulator(node, receivedValue); pg_->GeneratorYield(node, funcObj_); SuspendResumeExecution(node, receivedType, receivedValue); } pg_->Branch(node, loopStart); // v. If done is true, then // 6. If done is true, then // viii. If done is true, then pg_->SetLabel(node, iteratorComplete); pg_->LoadAccumulator(node, exitReturn); pg_->BranchIfFalse(node, normalOrThrowCompletion); // 1. Let value be ? IteratorValue(innerReturnResult). iterator.Value(); if (pg_->CheckControlFlowChange()) { pg_->StoreAccumulator(node, receivedValue); pg_->ControlFlowChangeBreak(); pg_->LoadAccumulator(node, receivedValue); } // 2. Return Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. pg_->DirectReturn(node); pg_->SetLabel(node, normalOrThrowCompletion); // 1. Return ? IteratorValue(innerResult). // a. Return ? IteratorValue(innerResult). iterator.Value(); } } // namespace panda::es2panda::compiler