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