• 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 //
17 //  desc:   For-of-loop syntax is translated to the while-loop syntax by calling of special method
18 //          providing predefined 'iterator' interface:
19 //  for (let x of c) {    // c is an object of 'iterable' class
20 //    <body>
21 //  }
22 //  ...
23 //  let_ci_=_c.$_iterator()
24 //  let_it_=_ci.next()
25 //  while_(!it.done)_{
26 //    x_=_it.value!
27 //    <body>
28 //    it_=_ci.next()
29 //  }
30 //
31 
32 #include "objectIterator.h"
33 
34 #include "generated/signatures.h"
35 #include "macros.h"
36 #include "parser/ETSparser.h"
37 #include "compiler/lowering/util.h"
38 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
39 #include "checker/ETSchecker.h"
40 #include "util/options.h"
41 
42 namespace ark::es2panda::compiler {
43 
44 static constexpr std::size_t const WHILE_LOOP_POSITION = 1U;
45 static constexpr std::size_t const WHILE_LOOP_SIZE = 3U;
46 
Name() const47 std::string_view ObjectIteratorLowering::Name() const
48 {
49     static std::string const NAME = "ObjectIteratorLowering";
50     return NAME;
51 }
52 
TransferForOfLoopBody(ir::Statement * const forBody,ir::BlockStatement * const whileBody) const53 void ObjectIteratorLowering::TransferForOfLoopBody(ir::Statement *const forBody,
54                                                    ir::BlockStatement *const whileBody) const noexcept
55 {
56     ES2PANDA_ASSERT(forBody != nullptr && whileBody != nullptr);
57     auto &whileStatements = whileBody->Statements();
58 
59     //  Currently while loop body consists of 2 statements: 'x = it.value!' and 'it = ci.next()'
60     //  We need to insert the body of original for-of-loop between them, change their parent and
61     //  probably clean types for expressions and variables for identifier for subsequent re-check.
62     if (forBody->IsBlockStatement()) {
63         auto &forStatements = forBody->AsBlockStatement()->Statements();
64         std::size_t const forSize = forStatements.size();
65 
66         whileStatements.resize(WHILE_LOOP_SIZE + forSize);
67 
68         for (std::size_t i = 0U; i < forSize; ++i) {
69             auto &statement = forStatements[i];
70             statement->SetParent(whileBody);
71             ClearTypesVariablesAndScopes(statement);
72             whileStatements[WHILE_LOOP_SIZE + i] = statement;
73         }
74     } else {
75         whileStatements.resize(WHILE_LOOP_SIZE + 1U);
76 
77         forBody->SetParent(whileBody);
78         ClearTypesVariablesAndScopes(forBody);
79         whileStatements[WHILE_LOOP_SIZE] = forBody;
80     }
81 }
82 
83 // interface Iterator<T> maybe implements by other classes
84 // we need the instantiated <T>
85 // so to find in interface and super
FindInstantiatedTypeParamFromIterator(checker::ETSObjectType * itor)86 checker::Type *FindInstantiatedTypeParamFromIterator(checker::ETSObjectType *itor)
87 {
88     if (itor == nullptr) {
89         return nullptr;
90     }
91     if (itor->Name() == compiler::Signatures::ITERATOR_CLASS) {
92         return itor->TypeArguments().front();
93     }
94     for (auto interface : itor->Interfaces()) {
95         if (auto type = FindInstantiatedTypeParamFromIterator(interface); type != nullptr) {
96             return type;
97         }
98     }
99     if (auto type = FindInstantiatedTypeParamFromIterator(itor->SuperType()); type != nullptr) {
100         return type;
101     }
102     return nullptr;
103 }
104 
FindIterValueType(checker::ETSObjectType * type,ArenaAllocator * allocator)105 static ir::OpaqueTypeNode *FindIterValueType(checker::ETSObjectType *type, ArenaAllocator *allocator)
106 {
107     auto *itor = type->GetProperty(compiler::Signatures::ITERATOR_METHOD,
108                                    checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD |
109                                        checker::PropertySearchFlags::SEARCH_IN_INTERFACES |
110                                        checker::PropertySearchFlags::SEARCH_IN_BASE);
111     ES2PANDA_ASSERT(itor != nullptr);
112     auto const &sigs = itor->TsType()->AsETSFunctionType()->CallSignatures();
113     checker::ETSObjectType *itorReturnType = nullptr;
114     for (auto &sig : sigs) {
115         if (sig->Params().empty()) {
116             itorReturnType = sig->ReturnType()->AsETSObjectType();
117             break;
118         }
119     }
120     ES2PANDA_ASSERT(itorReturnType);
121     auto *valueType = FindInstantiatedTypeParamFromIterator(itorReturnType);
122     return allocator->New<ir::OpaqueTypeNode>(valueType, allocator);
123 }
124 
ProcessObjectIterator(public_lib::Context * ctx,ir::ForOfStatement * forOfStatement) const125 ir::Statement *ObjectIteratorLowering::ProcessObjectIterator(public_lib::Context *ctx,
126                                                              ir::ForOfStatement *forOfStatement) const
127 {
128     //  Note! We assume that parser, varbinder and checker phases have been already passed correctly, thus the
129     //  class has required accessible iterator method and all the types and scopes are properly resolved.
130     auto *const allocator = ctx->Allocator();
131 
132     auto *const varbinder = ctx->checker->VarBinder()->AsETSBinder();
133     ES2PANDA_ASSERT(varbinder != nullptr);
134     auto statementScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, NearestScope(forOfStatement));
135 
136     ir::Identifier *const iterIdent = Gensym(allocator);
137     ir::Identifier *const nextIdent = Gensym(allocator);
138     ir::Identifier *loopVariableIdent = nullptr;
139 
140     // find $_iterator->ReturnType->Iterator<number>->number
141     // we cannot simply use next().value! , because value itself maybe undefined or null
142     ir::AstNode *typeNode;
143     auto exprType = forOfStatement->Right()->TsType();
144     if (!exprType->IsETSObjectType()) {
145         return forOfStatement;
146     }
147     typeNode = FindIterValueType(exprType->AsETSObjectType(), allocator);
148 
149     //  Replace the for-of loop with the while loop using the provided iterator interface
150     std::string whileStatement = "let @@I1 = (@@E2)." + std::string {compiler::Signatures::ITERATOR_METHOD} + "(); ";
151     whileStatement += "while (true) { ";
152     whileStatement += "let @@I3 = @@I4.next(); ";
153     whileStatement += "if (@@I5.done) break;";
154 
155     if (auto *const left = forOfStatement->Left(); left->IsVariableDeclaration()) {
156         auto *const declaration = left->AsVariableDeclaration();
157         whileStatement +=
158             declaration->Kind() != ir::VariableDeclaration::VariableDeclarationKind::CONST ? "let " : "const ";
159         loopVariableIdent = declaration->Declarators().at(0U)->Id()->AsIdentifier()->Clone(allocator, nullptr);
160     } else if (left->IsIdentifier()) {
161         loopVariableIdent = Gensym(allocator);
162         loopVariableIdent->SetName(left->AsIdentifier()->Name());
163     } else {
164         ES2PANDA_UNREACHABLE();
165     }
166     whileStatement += "@@I6 = (@@I7.value as @@T8);}; ";
167 
168     // Parse ArkTS code string and create corresponding AST nodes
169     auto *const parser = ctx->parser->AsETSParser();
170     ES2PANDA_ASSERT(parser != nullptr);
171 
172     auto *const loweringResult = parser->CreateFormattedStatement(
173         whileStatement, iterIdent, forOfStatement->Right(), nextIdent, iterIdent->Clone(allocator, nullptr),
174         nextIdent->Clone(allocator, nullptr), loopVariableIdent, nextIdent->Clone(allocator, nullptr), typeNode);
175     ES2PANDA_ASSERT(loweringResult != nullptr);
176     loweringResult->SetParent(forOfStatement->Parent());
177     loweringResult->SetRange(forOfStatement->Range());
178 
179     TransferForOfLoopBody(forOfStatement->Body(), loweringResult->AsBlockStatement()
180                                                       ->Statements()[WHILE_LOOP_POSITION]
181                                                       ->AsWhileStatement()
182                                                       ->Body()
183                                                       ->AsBlockStatement());
184 
185     auto *const checker = ctx->checker->AsETSChecker();
186     ES2PANDA_ASSERT(checker != nullptr);
187     CheckLoweredNode(varbinder, checker, loweringResult);
188 
189     return loweringResult;
190 }
191 
PerformForModule(public_lib::Context * ctx,parser::Program * program)192 bool ObjectIteratorLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program)
193 {
194     auto hasIterator = [](checker::Type const *const exprType) -> bool {
195         return exprType != nullptr && (exprType->IsETSObjectType() || exprType->IsETSTypeParameter());
196     };
197 
198     program->Ast()->TransformChildrenRecursively(
199         // clang-format off
200         [this, ctx, &hasIterator](ir::AstNode *ast) -> ir::AstNode* {
201             // clang-format on
202             if (ast->IsForOfStatement()) {
203                 if (auto const *const exprType = ast->AsForOfStatement()->Right()->TsType();
204                     hasIterator(exprType) || (exprType != nullptr && exprType->IsETSUnionType() &&
205                                               exprType->AsETSUnionType()->AllOfConstituentTypes(hasIterator))) {
206                     return ProcessObjectIterator(ctx, ast->AsForOfStatement());
207                 }
208             }
209             return ast;
210         },
211         Name());
212 
213     return true;
214 }
215 }  // namespace ark::es2panda::compiler
216