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 "arrayExpression.h"
17
18 #include "checker/ETSchecker.h"
19 #include "checker/TSchecker.h"
20 #include "checker/types/ets/etsTupleType.h"
21 #include "checker/ets/typeRelationContext.h"
22 #include "checker/ts/destructuringContext.h"
23 #include "compiler/core/ETSGen.h"
24 #include "compiler/core/pandagen.h"
25 #include "ir/astDump.h"
26 #include "ir/base/decorator.h"
27 #include "ir/srcDump.h"
28 #include "ir/typeNode.h"
29 #include "ir/base/spreadElement.h"
30 #include "ir/expressions/assignmentExpression.h"
31 #include "ir/expressions/identifier.h"
32 #include "ir/expressions/objectExpression.h"
33 #include "util/helpers.h"
34
35 namespace ark::es2panda::ir {
ArrayExpression(Tag const tag,ArrayExpression const & other,ArenaAllocator * const allocator)36 ArrayExpression::ArrayExpression([[maybe_unused]] Tag const tag, ArrayExpression const &other,
37 ArenaAllocator *const allocator)
38 : AnnotatedExpression(static_cast<AnnotatedExpression const &>(other), allocator),
39 decorators_(allocator->Adapter()),
40 elements_(allocator->Adapter())
41 {
42 preferredType_ = other.preferredType_;
43 isDeclaration_ = other.isDeclaration_;
44 trailingComma_ = other.trailingComma_;
45 optional_ = other.optional_;
46
47 for (auto *element : other.elements_) {
48 elements_.emplace_back(element->Clone(allocator, this)->AsExpression());
49 }
50
51 for (auto *decorator : other.decorators_) {
52 decorators_.emplace_back(decorator->Clone(allocator, this));
53 }
54 }
55
Clone(ArenaAllocator * const allocator,AstNode * const parent)56 ArrayExpression *ArrayExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
57 {
58 if (auto *const clone = allocator->New<ArrayExpression>(Tag {}, *this, allocator); clone != nullptr) {
59 if (parent != nullptr) {
60 clone->SetParent(parent);
61 }
62 return clone;
63 }
64 throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
65 }
66
ConvertibleToArrayPattern()67 bool ArrayExpression::ConvertibleToArrayPattern()
68 {
69 bool restFound = false;
70 bool convResult = true;
71 for (auto *it : elements_) {
72 switch (it->Type()) {
73 case AstNodeType::ARRAY_EXPRESSION: {
74 convResult = it->AsArrayExpression()->ConvertibleToArrayPattern();
75 break;
76 }
77 case AstNodeType::SPREAD_ELEMENT: {
78 if (!restFound && it == elements_.back() && !trailingComma_) {
79 convResult = it->AsSpreadElement()->ConvertibleToRest(isDeclaration_);
80 } else {
81 convResult = false;
82 }
83 restFound = true;
84 break;
85 }
86 case AstNodeType::OBJECT_EXPRESSION: {
87 convResult = it->AsObjectExpression()->ConvertibleToObjectPattern();
88 break;
89 }
90 case AstNodeType::ASSIGNMENT_EXPRESSION: {
91 convResult = it->AsAssignmentExpression()->ConvertibleToAssignmentPattern();
92 break;
93 }
94 case AstNodeType::MEMBER_EXPRESSION:
95 case AstNodeType::OMITTED_EXPRESSION:
96 case AstNodeType::IDENTIFIER:
97 case AstNodeType::ARRAY_PATTERN:
98 case AstNodeType::OBJECT_PATTERN:
99 case AstNodeType::ASSIGNMENT_PATTERN:
100 case AstNodeType::REST_ELEMENT: {
101 break;
102 }
103 default: {
104 convResult = false;
105 break;
106 }
107 }
108
109 if (!convResult) {
110 break;
111 }
112 }
113
114 SetType(AstNodeType::ARRAY_PATTERN);
115 return convResult;
116 }
117
ValidateExpression()118 ValidationInfo ArrayExpression::ValidateExpression()
119 {
120 if (optional_) {
121 return {"Unexpected token '?'.", Start()};
122 }
123
124 if (TypeAnnotation() != nullptr) {
125 return {"Unexpected token.", TypeAnnotation()->Start()};
126 }
127
128 ValidationInfo info;
129
130 for (auto *it : elements_) {
131 switch (it->Type()) {
132 case AstNodeType::OBJECT_EXPRESSION: {
133 info = it->AsObjectExpression()->ValidateExpression();
134 break;
135 }
136 case AstNodeType::ARRAY_EXPRESSION: {
137 info = it->AsArrayExpression()->ValidateExpression();
138 break;
139 }
140 case AstNodeType::ASSIGNMENT_EXPRESSION: {
141 auto *assignmentExpr = it->AsAssignmentExpression();
142
143 if (assignmentExpr->Left()->IsArrayExpression()) {
144 info = assignmentExpr->Left()->AsArrayExpression()->ValidateExpression();
145 } else if (assignmentExpr->Left()->IsObjectExpression()) {
146 info = assignmentExpr->Left()->AsObjectExpression()->ValidateExpression();
147 }
148
149 break;
150 }
151 case AstNodeType::SPREAD_ELEMENT: {
152 info = it->AsSpreadElement()->ValidateExpression();
153 break;
154 }
155 default: {
156 break;
157 }
158 }
159
160 if (info.Fail()) {
161 break;
162 }
163 }
164
165 return info;
166 }
167
TransformChildren(const NodeTransformer & cb,std::string_view const transformationName)168 void ArrayExpression::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
169 {
170 for (auto *&it : decorators_) {
171 if (auto *transformedNode = cb(it); it != transformedNode) {
172 it->SetTransformedNode(transformationName, transformedNode);
173 it = transformedNode->AsDecorator();
174 }
175 }
176
177 for (auto *&it : elements_) {
178 if (auto *transformedNode = cb(it); it != transformedNode) {
179 it->SetTransformedNode(transformationName, transformedNode);
180 it = transformedNode->AsExpression();
181 }
182 }
183
184 if (auto *typeAnnotation = TypeAnnotation(); typeAnnotation != nullptr) {
185 if (auto *transformedNode = cb(typeAnnotation); typeAnnotation != transformedNode) {
186 typeAnnotation->SetTransformedNode(transformationName, transformedNode);
187 SetTsTypeAnnotation(static_cast<TypeNode *>(transformedNode));
188 }
189 }
190 }
191
Iterate(const NodeTraverser & cb) const192 void ArrayExpression::Iterate(const NodeTraverser &cb) const
193 {
194 for (auto *it : decorators_) {
195 cb(it);
196 }
197
198 for (auto *it : elements_) {
199 cb(it);
200 }
201
202 if (TypeAnnotation() != nullptr) {
203 cb(TypeAnnotation());
204 }
205 }
206
Dump(ir::AstDumper * dumper) const207 void ArrayExpression::Dump(ir::AstDumper *dumper) const
208 {
209 dumper->Add({{"type", type_ == AstNodeType::ARRAY_EXPRESSION ? "ArrayExpression" : "ArrayPattern"},
210 {"decorators", AstDumper::Optional(decorators_)},
211 {"elements", elements_},
212 {"typeAnnotation", AstDumper::Optional(TypeAnnotation())},
213 {"optional", AstDumper::Optional(optional_)}});
214 }
215
Dump(ir::SrcDumper * dumper) const216 void ArrayExpression::Dump(ir::SrcDumper *dumper) const
217 {
218 dumper->Add("[");
219 for (auto element : elements_) {
220 element->Dump(dumper);
221 if (element != elements_.back()) {
222 dumper->Add(", ");
223 }
224 }
225 dumper->Add("]");
226 }
227
Compile(compiler::PandaGen * pg) const228 void ArrayExpression::Compile(compiler::PandaGen *pg) const
229 {
230 pg->GetAstCompiler()->Compile(this);
231 }
232
Compile(compiler::ETSGen * const etsg) const233 void ArrayExpression::Compile(compiler::ETSGen *const etsg) const
234 {
235 etsg->GetAstCompiler()->Compile(this);
236 }
237
Check(checker::TSChecker * checker)238 checker::Type *ArrayExpression::Check(checker::TSChecker *checker)
239 {
240 return checker->GetAnalyzer()->Check(this);
241 }
242
CheckAssignmentPattern(Expression * it,checker::TSChecker * checker,checker::Type * elementType,bool & addOptional,checker::ElementFlags & memberFlag)243 checker::Type *CheckAssignmentPattern(Expression *it, checker::TSChecker *checker, checker::Type *elementType,
244 bool &addOptional, checker::ElementFlags &memberFlag)
245 {
246 auto *assignmentPattern = it->AsAssignmentPattern();
247 if (assignmentPattern->Left()->IsIdentifier()) {
248 const ir::Identifier *ident = assignmentPattern->Left()->AsIdentifier();
249 ASSERT(ident->Variable());
250 varbinder::Variable *bindingVar = ident->Variable();
251 checker::Type *initializerType = checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
252 bindingVar->SetTsType(initializerType);
253 elementType = initializerType;
254 } else if (assignmentPattern->Left()->IsArrayPattern()) {
255 auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
256 auto destructuringContext = checker::ArrayDestructuringContext(
257 {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()});
258 destructuringContext.Start();
259 elementType = destructuringContext.InferredType();
260 } else {
261 ASSERT(assignmentPattern->Left()->IsObjectPattern());
262 auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
263 auto destructuringContext = checker::ObjectDestructuringContext(
264 {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()});
265 destructuringContext.Start();
266 elementType = destructuringContext.InferredType();
267 }
268
269 if (addOptional) {
270 memberFlag = checker::ElementFlags::OPTIONAL;
271 } else {
272 memberFlag = checker::ElementFlags::REQUIRED;
273 }
274 return elementType;
275 }
276
CheckElementPattern(Expression * it,checker::Type * elementType,checker::TSChecker * checker,bool & addOptional,checker::ElementFlags & memberFlag)277 checker::Type *CheckElementPattern(Expression *it, checker::Type *elementType, checker::TSChecker *checker,
278 bool &addOptional, checker::ElementFlags &memberFlag)
279 {
280 switch (it->Type()) {
281 case ir::AstNodeType::REST_ELEMENT: {
282 elementType = checker->Allocator()->New<checker::ArrayType>(checker->GlobalAnyType());
283 memberFlag = checker::ElementFlags::REST;
284 addOptional = false;
285 return elementType;
286 }
287 case ir::AstNodeType::OBJECT_PATTERN: {
288 elementType = it->AsObjectPattern()->CheckPattern(checker);
289 memberFlag = checker::ElementFlags::REQUIRED;
290 addOptional = false;
291 return elementType;
292 }
293 case ir::AstNodeType::ARRAY_PATTERN: {
294 elementType = it->AsArrayPattern()->CheckPattern(checker);
295 memberFlag = checker::ElementFlags::REQUIRED;
296 addOptional = false;
297 return elementType;
298 }
299 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
300 return CheckAssignmentPattern(it, checker, elementType, addOptional, memberFlag);
301 }
302 case ir::AstNodeType::OMITTED_EXPRESSION: {
303 elementType = checker->GlobalAnyType();
304 memberFlag = checker::ElementFlags::REQUIRED;
305 addOptional = false;
306 return elementType;
307 }
308 case ir::AstNodeType::IDENTIFIER: {
309 const ir::Identifier *ident = it->AsIdentifier();
310 ASSERT(ident->Variable());
311 elementType = checker->GlobalAnyType();
312 ident->Variable()->SetTsType(elementType);
313 memberFlag = checker::ElementFlags::REQUIRED;
314 addOptional = false;
315 return elementType;
316 }
317 default: {
318 UNREACHABLE();
319 }
320 }
321 }
322
CheckPattern(checker::TSChecker * checker)323 checker::Type *ArrayExpression::CheckPattern(checker::TSChecker *checker)
324 {
325 checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
326 ArenaVector<checker::ElementFlags> elementFlags(checker->Allocator()->Adapter());
327 checker::ElementFlags combinedFlags = checker::ElementFlags::NO_OPTS;
328 uint32_t minLength = 0;
329 uint32_t index = elements_.size();
330 bool addOptional = true;
331
332 for (auto it = elements_.rbegin(); it != elements_.rend(); it++) {
333 checker::Type *elementType = nullptr;
334 checker::ElementFlags memberFlag = checker::ElementFlags::NO_OPTS;
335
336 elementType = CheckElementPattern(*it, elementType, checker, addOptional, memberFlag);
337
338 util::StringView memberIndex = util::Helpers::ToStringView(checker->Allocator(), index - 1);
339
340 auto *memberVar =
341 varbinder::Scope::CreateVar(checker->Allocator(), memberIndex, varbinder::VariableFlags::PROPERTY, *it);
342
343 if (memberFlag == checker::ElementFlags::OPTIONAL) {
344 memberVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
345 } else {
346 minLength++;
347 }
348
349 memberVar->SetTsType(elementType);
350 elementFlags.push_back(memberFlag);
351 desc->properties.insert(desc->properties.begin(), memberVar);
352
353 combinedFlags |= memberFlag;
354 index--;
355 }
356
357 const checker::TupleTypeInfo tupleTypeInfo = {combinedFlags, minLength,
358 static_cast<uint32_t>(desc->properties.size()), false};
359 return checker->CreateTupleType(desc, std::move(elementFlags), tupleTypeInfo);
360 }
361
HandleNestedArrayExpression(checker::ETSChecker * const checker,ArrayExpression * const currentElement,const bool isPreferredTuple,const std::size_t idx)362 bool ArrayExpression::HandleNestedArrayExpression(checker::ETSChecker *const checker,
363 ArrayExpression *const currentElement, const bool isPreferredTuple,
364 const std::size_t idx)
365 {
366 if (isPreferredTuple) {
367 currentElement->SetPreferredType(preferredType_->AsETSTupleType()->GetTypeAtIndex(idx));
368
369 if (currentElement->GetPreferredType()->IsETSTupleType()) {
370 if (!checker->ValidateTupleMinElementSize(currentElement,
371 currentElement->GetPreferredType()->AsETSTupleType())) {
372 return false;
373 }
374 }
375
376 return true;
377 }
378
379 if (preferredType_->IsETSArrayType()) {
380 if (preferredType_->AsETSArrayType()->ElementType()->IsETSTupleType()) {
381 if (!checker->ValidateTupleMinElementSize(
382 // CC-OFFNXT(G.FMT.06-CPP) project code style
383 currentElement, preferredType_->AsETSArrayType()->ElementType()->AsETSTupleType())) {
384 return false;
385 }
386 }
387
388 currentElement->SetPreferredType(preferredType_->AsETSArrayType()->ElementType());
389 return true;
390 }
391
392 if (currentElement->GetPreferredType() == nullptr) {
393 currentElement->SetPreferredType(preferredType_);
394 }
395 return true;
396 }
397
Check(checker::ETSChecker * checker)398 checker::Type *ArrayExpression::Check(checker::ETSChecker *checker)
399 {
400 return checker->GetAnalyzer()->Check(this);
401 }
402
GetPrefferedTypeFromFuncParam(checker::ETSChecker * checker,Expression * param,checker::TypeRelationFlag flags)403 void ArrayExpression::GetPrefferedTypeFromFuncParam(checker::ETSChecker *checker, Expression *param,
404 checker::TypeRelationFlag flags)
405 {
406 if (preferredType_ != nullptr) {
407 return;
408 }
409 auto paramType = param->Check(checker);
410 if (paramType->IsETSArrayType()) {
411 auto *elementType = paramType->AsETSArrayType()->ElementType();
412 bool isAssignable = true;
413 for (auto elem : elements_) {
414 auto assignCtx =
415 checker::AssignmentContext(checker->Relation(), elem, elem->Check(checker), elementType, elem->Start(),
416 {""}, checker::TypeRelationFlag::NO_THROW | flags);
417 isAssignable &= assignCtx.IsAssignable();
418 }
419 if (isAssignable) {
420 preferredType_ = paramType;
421 }
422 }
423 }
424
425 } // namespace ark::es2panda::ir
426