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