1/** 2 * Copyright (c) 2021 - 2024 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 "forOfStatement.h" 17 18#include "checker/TSchecker.h" 19#include "compiler/core/pandagen.h" 20#include "compiler/core/ETSGen.h" 21#include "ir/astDump.h" 22#include "ir/srcDump.h" 23 24namespace ark::es2panda::ir { 25 26checker::Type *ForOfStatement::CreateUnionIteratorTypes(checker::ETSChecker *checker, checker::Type *exprType) 27{ 28 ArenaVector<checker::Type *> types(checker->Allocator()->Adapter()); 29 30 for (auto it : exprType->AsETSUnionType()->ConstituentTypes()) { 31 if (it->IsETSStringType()) { 32 types.push_back(checker->GetGlobalTypesHolder()->GlobalCharType()); 33 } else if (it->IsETSObjectType()) { 34 types.push_back(this->CheckIteratorMethodForObject(checker, it->AsETSObjectType())); 35 } else if (it->IsETSArrayType()) { 36 types.push_back(it->AsETSArrayType()->ElementType()->Instantiate(checker->Allocator(), checker->Relation(), 37 checker->GetGlobalTypesHolder())); 38 types.back()->RemoveTypeFlag(checker::TypeFlag::CONSTANT); 39 } else { 40 return nullptr; 41 } 42 } 43 44 return checker->CreateETSUnionType(std::move(types)); 45} 46 47void ForOfStatement::TransformChildren(const NodeTransformer &cb, std::string_view transformationName) 48{ 49 if (auto *transformedNode = cb(left_); left_ != transformedNode) { 50 left_->SetTransformedNode(transformationName, transformedNode); 51 left_ = transformedNode; 52 } 53 54 if (auto *transformedNode = cb(right_); right_ != transformedNode) { 55 right_->SetTransformedNode(transformationName, transformedNode); 56 right_ = transformedNode->AsExpression(); 57 } 58 59 if (auto *transformedNode = cb(body_); body_ != transformedNode) { 60 body_->SetTransformedNode(transformationName, transformedNode); 61 body_ = transformedNode->AsStatement(); 62 } 63} 64 65void ForOfStatement::Iterate(const NodeTraverser &cb) const 66{ 67 cb(left_); 68 cb(right_); 69 cb(body_); 70} 71 72void ForOfStatement::Dump(ir::AstDumper *dumper) const 73{ 74 dumper->Add({{"type", "ForOfStatement"}, {"await", isAwait_}, {"left", left_}, {"right", right_}, {"body", body_}}); 75} 76 77void ForOfStatement::Dump(ir::SrcDumper *dumper) const 78{ 79 ASSERT(left_ != nullptr); 80 ASSERT(right_ != nullptr); 81 dumper->Add("for "); 82 if (isAwait_) { 83 dumper->Add("await "); 84 } 85 dumper->Add("("); 86 left_->Dump(dumper); 87 dumper->Add(" of "); 88 right_->Dump(dumper); 89 dumper->Add(") {"); 90 if (body_ != nullptr) { 91 dumper->IncrIndent(); 92 dumper->Endl(); 93 body_->Dump(dumper); 94 dumper->DecrIndent(); 95 dumper->Endl(); 96 } 97 dumper->Add("}"); 98} 99 100void ForOfStatement::Compile(compiler::PandaGen *pg) const 101{ 102 pg->GetAstCompiler()->Compile(this); 103} 104 105void ForOfStatement::Compile(compiler::ETSGen *etsg) const 106{ 107 etsg->GetAstCompiler()->Compile(this); 108} 109 110checker::Type *ForOfStatement::Check(checker::TSChecker *checker) 111{ 112 return checker->GetAnalyzer()->Check(this); 113} 114 115checker::Type *ForOfStatement::Check(checker::ETSChecker *checker) 116{ 117 return checker->GetAnalyzer()->Check(this); 118} 119 120ForOfStatement *ForOfStatement::Clone(ArenaAllocator *const allocator, AstNode *const parent) 121{ 122 auto *const left = left_ != nullptr ? left_->Clone(allocator, nullptr) : nullptr; 123 auto *const right = right_ != nullptr ? right_->Clone(allocator, nullptr)->AsExpression() : nullptr; 124 auto *const body = body_ != nullptr ? body_->Clone(allocator, nullptr)->AsStatement() : nullptr; 125 126 if (auto *const clone = allocator->New<ForOfStatement>(left, right, body, isAwait_); clone != nullptr) { 127 if (left != nullptr) { 128 left->SetParent(clone); 129 } 130 131 if (right != nullptr) { 132 right->SetParent(clone); 133 } 134 135 if (body != nullptr) { 136 body->SetParent(clone); 137 } 138 139 if (parent != nullptr) { 140 clone->SetParent(parent); 141 } 142 143 clone->SetRange(Range()); 144 return clone; 145 } 146 147 throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR); 148} 149 150checker::Type *ForOfStatement::CheckIteratorMethodForObject(checker::ETSChecker *checker, 151 checker::ETSObjectType *sourceType) 152{ 153 auto const &position = right_->Start(); 154 155 checker::PropertySearchFlags searchFlag = 156 checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::IS_FUNCTIONAL; 157 searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES; 158 // NOTE: maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC) 159 160 if (sourceType->HasTypeFlag(checker::TypeFlag::GENERIC)) { 161 searchFlag |= checker::PropertySearchFlags::SEARCH_ALL; 162 } 163 164 auto *const method = sourceType->GetProperty(compiler::Signatures::ITERATOR_METHOD, searchFlag); 165 if (method == nullptr || !method->HasFlag(varbinder::VariableFlags::METHOD)) { 166 checker->LogTypeError("Object type doesn't have proper iterator method.", position); 167 return nullptr; 168 } 169 170 ArenaVector<Expression *> arguments {checker->Allocator()->Adapter()}; 171 auto &signatures = checker->GetTypeOfVariable(method)->AsETSFunctionType()->CallSignatures(); 172 173 checker::Signature *signature = checker->ValidateSignatures(signatures, nullptr, arguments, position, "iterator", 174 checker::TypeRelationFlag::NO_THROW); 175 if (signature == nullptr) { 176 checker->LogTypeError("Cannot find iterator method with the required signature.", position); 177 return nullptr; 178 } 179 checker->ValidateSignatureAccessibility(sourceType, nullptr, signature, position, 180 "Iterator method is not visible here."); 181 182 ASSERT(signature->Function() != nullptr); 183 184 if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) { 185 checker->CheckThrowingStatements(this); 186 } 187 188 if (!CheckReturnTypeOfIteratorMethod(checker, sourceType, signature, position)) { 189 return nullptr; 190 } 191 192 auto *const nextMethod = 193 signature->ReturnType()->AsETSObjectType()->GetProperty(ITERATOR_INTERFACE_METHOD, searchFlag); 194 if (nextMethod == nullptr || !nextMethod->HasFlag(varbinder::VariableFlags::METHOD)) { 195 checker->LogTypeError("Iterator object doesn't have proper next method.", position); 196 return nullptr; 197 } 198 199 auto &nextSignatures = checker->GetTypeOfVariable(nextMethod)->AsETSFunctionType()->CallSignatures(); 200 201 auto const *const nextSignature = checker->ValidateSignatures(nextSignatures, nullptr, arguments, position, 202 "iterator", checker::TypeRelationFlag::NO_THROW); 203 if (nextSignature != nullptr && nextSignature->ReturnType()->IsETSObjectType()) { 204 if (auto const *const resultType = nextSignature->ReturnType()->AsETSObjectType(); 205 resultType->Name().Is(ITERATOR_RESULT_NAME)) { 206 return resultType->TypeArguments()[0]; 207 } 208 } 209 210 return nullptr; 211} 212 213bool ForOfStatement::CheckReturnTypeOfIteratorMethod(checker::ETSChecker *checker, checker::ETSObjectType *sourceType, 214 checker::Signature *signature, 215 const lexer::SourcePosition &position) 216{ 217 if ((signature->ReturnType() == nullptr || signature->ReturnType() == checker->GlobalVoidType()) && 218 signature->Function()->HasBody() && signature->Function()->Body()->IsBlockStatement()) { 219 for (auto *const it : signature->Function()->Body()->AsBlockStatement()->Statements()) { 220 if (it->IsReturnStatement()) { 221 checker::SavedCheckerContext savedContext(checker, checker::CheckerStatus::IN_CLASS, sourceType); 222 it->AsReturnStatement()->Check(checker); 223 break; 224 } 225 } 226 } 227 228 if (signature->ReturnType() != nullptr && signature->ReturnType()->IsETSObjectType() && 229 ForOfStatement::CheckIteratorInterfaceForObject(checker, signature->ReturnType()->AsETSObjectType())) { 230 return true; 231 } 232 233 checker->LogTypeError("Iterator method must return an object which implements Iterator<T>", position); 234 return false; 235} 236 237bool ForOfStatement::CheckIteratorInterfaceForObject(checker::ETSChecker *checker, checker::ETSObjectType *obj) 238{ 239 for (auto *const it : obj->Interfaces()) { 240 if (it->Name().Is(ITERATOR_INTERFACE_NAME)) { 241 return true; 242 } 243 } 244 245 return obj->SuperType() != nullptr && obj->SuperType()->IsETSObjectType() && 246 CheckIteratorInterfaceForObject(checker, obj->SuperType()->AsETSObjectType()); 247} 248 249checker::Type *ForOfStatement::CheckIteratorMethod(checker::ETSChecker *const checker) 250{ 251 if (auto *exprType = right_->TsType(); exprType != nullptr) { 252 if (exprType->IsETSObjectType()) { 253 return CheckIteratorMethodForObject(checker, exprType->AsETSObjectType()); 254 } 255 256 if (exprType->IsETSUnionType()) { 257 return this->CreateUnionIteratorTypes(checker, exprType); 258 } 259 } 260 261 return nullptr; 262} 263} // namespace ark::es2panda::ir 264