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