• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "objectExpression.h"
17 
18 #include "ir/base/decorator.h"
19 #include "util/helpers.h"
20 #include "compiler/base/literals.h"
21 #include "compiler/core/pandagen.h"
22 #include "compiler/core/ETSGen.h"
23 #include "checker/TSchecker.h"
24 #include "checker/ETSchecker.h"
25 #include "checker/ets/typeRelationContext.h"
26 #include "checker/ts/destructuringContext.h"
27 #include "ir/astDump.h"
28 #include "ir/srcDump.h"
29 #include "ir/typeNode.h"
30 #include "ir/base/property.h"
31 #include "ir/base/scriptFunction.h"
32 #include "ir/base/spreadElement.h"
33 #include "ir/expressions/arrayExpression.h"
34 #include "ir/expressions/assignmentExpression.h"
35 #include "ir/expressions/identifier.h"
36 #include "ir/statements/variableDeclarator.h"
37 #include "ir/validationInfo.h"
38 
39 namespace ark::es2panda::ir {
ObjectExpression(Tag const tag,ObjectExpression const & other,ArenaAllocator * const allocator)40 ObjectExpression::ObjectExpression([[maybe_unused]] Tag const tag, ObjectExpression const &other,
41                                    ArenaAllocator *const allocator)
42     : AnnotatedExpression(static_cast<AnnotatedExpression const &>(other), allocator),
43       decorators_(allocator->Adapter()),
44       properties_(allocator->Adapter())
45 {
46     preferredType_ = other.preferredType_;
47     isDeclaration_ = other.isDeclaration_;
48     trailingComma_ = other.trailingComma_;
49     optional_ = other.optional_;
50 
51     for (auto *property : other.properties_) {
52         properties_.emplace_back(property->Clone(allocator, this)->AsExpression());
53     }
54 
55     for (auto *decorator : other.decorators_) {
56         decorators_.emplace_back(decorator->Clone(allocator, this));
57     }
58 }
59 
Clone(ArenaAllocator * const allocator,AstNode * const parent)60 ObjectExpression *ObjectExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
61 {
62     if (auto *const clone = allocator->New<ObjectExpression>(Tag {}, *this, allocator); clone != nullptr) {
63         if (parent != nullptr) {
64             clone->SetParent(parent);
65         }
66         return clone;
67     }
68     throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
69 }
70 
ValidateProperty(Property * prop,bool & foundProto)71 static std::pair<ValidationInfo, bool> ValidateProperty(Property *prop, bool &foundProto)
72 {
73     ValidationInfo info = prop->ValidateExpression();
74     if (prop->Kind() == PropertyKind::PROTO) {
75         if (foundProto) {
76             return {{"Duplicate __proto__ fields are not allowed in object literals", prop->Key()->Start()}, true};
77         }
78 
79         foundProto = true;
80     }
81 
82     return {info, false};
83 }
84 
ValidateExpression()85 ValidationInfo ObjectExpression::ValidateExpression()
86 {
87     if (optional_) {
88         return {"Unexpected token '?'.", Start()};
89     }
90 
91     if (TypeAnnotation() != nullptr) {
92         return {"Unexpected token.", TypeAnnotation()->Start()};
93     }
94 
95     ValidationInfo info;
96     bool foundProto = false;
97 
98     for (auto *it : properties_) {
99         switch (it->Type()) {
100             case AstNodeType::OBJECT_EXPRESSION:
101             case AstNodeType::ARRAY_EXPRESSION: {
102                 return {"Unexpected token.", it->Start()};
103             }
104             case AstNodeType::SPREAD_ELEMENT: {
105                 info = it->AsSpreadElement()->ValidateExpression();
106                 break;
107             }
108             case AstNodeType::PROPERTY: {
109                 auto *prop = it->AsProperty();
110                 bool ret = false;
111                 std::tie(info, ret) = ValidateProperty(prop, foundProto);
112                 if (ret) {
113                     return info;
114                 }
115                 break;
116             }
117             default: {
118                 break;
119             }
120         }
121 
122         if (info.Fail()) {
123             break;
124         }
125     }
126 
127     return info;
128 }
129 
ConvertibleToObjectPattern()130 bool ObjectExpression::ConvertibleToObjectPattern()
131 {
132     // NOTE: rsipka. throw more precise messages in case of false results
133     bool restFound = false;
134     bool convResult = true;
135 
136     for (auto *it : properties_) {
137         switch (it->Type()) {
138             case AstNodeType::ARRAY_EXPRESSION: {
139                 convResult = it->AsArrayExpression()->ConvertibleToArrayPattern();
140                 break;
141             }
142             case AstNodeType::SPREAD_ELEMENT: {
143                 if (!restFound && it == properties_.back() && !trailingComma_) {
144                     convResult = it->AsSpreadElement()->ConvertibleToRest(isDeclaration_, false);
145                 } else {
146                     convResult = false;
147                 }
148 
149                 restFound = true;
150                 break;
151             }
152             case AstNodeType::OBJECT_EXPRESSION: {
153                 convResult = it->AsObjectExpression()->ConvertibleToObjectPattern();
154                 break;
155             }
156             case AstNodeType::ASSIGNMENT_EXPRESSION: {
157                 convResult = it->AsAssignmentExpression()->ConvertibleToAssignmentPattern();
158                 break;
159             }
160             case AstNodeType::META_PROPERTY_EXPRESSION:
161             case AstNodeType::CHAIN_EXPRESSION:
162             case AstNodeType::SEQUENCE_EXPRESSION: {
163                 convResult = false;
164                 break;
165             }
166             case AstNodeType::PROPERTY: {
167                 convResult = it->AsProperty()->ConvertibleToPatternProperty();
168                 break;
169             }
170             default: {
171                 break;
172             }
173         }
174 
175         if (!convResult) {
176             break;
177         }
178     }
179 
180     SetType(AstNodeType::OBJECT_PATTERN);
181     return convResult;
182 }
183 
SetDeclaration()184 void ObjectExpression::SetDeclaration()
185 {
186     isDeclaration_ = true;
187 }
188 
SetOptional(bool optional)189 void ObjectExpression::SetOptional(bool optional)
190 {
191     optional_ = optional;
192 }
193 
TransformChildren(const NodeTransformer & cb,std::string_view transformationName)194 void ObjectExpression::TransformChildren(const NodeTransformer &cb, std::string_view transformationName)
195 {
196     for (auto *&it : decorators_) {
197         if (auto *transformedNode = cb(it); it != transformedNode) {
198             it->SetTransformedNode(transformationName, transformedNode);
199             it = transformedNode->AsDecorator();
200         }
201     }
202 
203     for (auto *&it : properties_) {
204         if (auto *transformedNode = cb(it); it != transformedNode) {
205             it->SetTransformedNode(transformationName, transformedNode);
206             it = transformedNode->AsExpression();
207         }
208     }
209 
210     if (auto *typeAnnotation = TypeAnnotation(); typeAnnotation != nullptr) {
211         if (auto *transformedNode = cb(typeAnnotation); typeAnnotation != transformedNode) {
212             typeAnnotation->SetTransformedNode(transformationName, transformedNode);
213             SetTsTypeAnnotation(static_cast<TypeNode *>(transformedNode));
214         }
215     }
216 }
217 
Iterate(const NodeTraverser & cb) const218 void ObjectExpression::Iterate(const NodeTraverser &cb) const
219 {
220     for (auto *it : decorators_) {
221         cb(it);
222     }
223 
224     for (auto *it : properties_) {
225         cb(it);
226     }
227 
228     if (TypeAnnotation() != nullptr) {
229         cb(TypeAnnotation());
230     }
231 }
232 
Dump(ir::AstDumper * dumper) const233 void ObjectExpression::Dump(ir::AstDumper *dumper) const
234 {
235     dumper->Add({{"type", (type_ == AstNodeType::OBJECT_EXPRESSION) ? "ObjectExpression" : "ObjectPattern"},
236                  {"decorators", AstDumper::Optional(decorators_)},
237                  {"properties", properties_},
238                  {"typeAnnotation", AstDumper::Optional(TypeAnnotation())},
239                  {"optional", AstDumper::Optional(optional_)}});
240 }
241 
Dump(ir::SrcDumper * dumper) const242 void ObjectExpression::Dump(ir::SrcDumper *dumper) const
243 {
244     dumper->Add("ObjectExpression");
245 }
246 
Compile(compiler::PandaGen * pg) const247 void ObjectExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const
248 {
249     pg->GetAstCompiler()->Compile(this);
250 }
251 
CheckPattern(checker::TSChecker * checker)252 checker::Type *ObjectExpression::CheckPattern(checker::TSChecker *checker)
253 {
254     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
255 
256     bool isOptional = false;
257 
258     for (auto it = properties_.rbegin(); it != properties_.rend(); it++) {
259         if ((*it)->IsRestElement()) {
260             ASSERT((*it)->AsRestElement()->Argument()->IsIdentifier());
261             util::StringView indexInfoName("x");
262             auto *newIndexInfo =
263                 checker->Allocator()->New<checker::IndexInfo>(checker->GlobalAnyType(), indexInfoName, false);
264             desc->stringIndexInfo = newIndexInfo;
265             continue;
266         }
267 
268         ASSERT((*it)->IsProperty());
269         auto *prop = (*it)->AsProperty();
270 
271         if (prop->IsComputed()) {
272             continue;
273         }
274 
275         varbinder::LocalVariable *foundVar = desc->FindProperty(prop->Key()->AsIdentifier()->Name());
276         checker::Type *patternParamType = checker->GlobalAnyType();
277         varbinder::Variable *bindingVar = nullptr;
278 
279         if (prop->IsShorthand()) {
280             switch (prop->Value()->Type()) {
281                 case ir::AstNodeType::IDENTIFIER: {
282                     const ir::Identifier *ident = prop->Value()->AsIdentifier();
283                     ASSERT(ident->Variable());
284                     bindingVar = ident->Variable();
285                     break;
286                 }
287                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
288                     auto *assignmentPattern = prop->Value()->AsAssignmentPattern();
289                     patternParamType = assignmentPattern->Right()->Check(checker);
290                     ASSERT(assignmentPattern->Left()->AsIdentifier()->Variable());
291                     bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
292                     isOptional = true;
293                     break;
294                 }
295                 default: {
296                     UNREACHABLE();
297                 }
298             }
299         } else {
300             switch (prop->Value()->Type()) {
301                 case ir::AstNodeType::IDENTIFIER: {
302                     bindingVar = prop->Value()->AsIdentifier()->Variable();
303                     break;
304                 }
305                 case ir::AstNodeType::ARRAY_PATTERN: {
306                     patternParamType = prop->Value()->AsArrayPattern()->CheckPattern(checker);
307                     break;
308                 }
309                 case ir::AstNodeType::OBJECT_PATTERN: {
310                     patternParamType = prop->Value()->AsObjectPattern()->CheckPattern(checker);
311                     break;
312                 }
313                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
314                     isOptional = CheckAssignmentPattern(prop, bindingVar, patternParamType, checker, foundVar);
315                     break;
316                 }
317                 default: {
318                     UNREACHABLE();
319                 }
320             }
321         }
322 
323         if (bindingVar != nullptr) {
324             bindingVar->SetTsType(patternParamType);
325         }
326 
327         if (foundVar != nullptr) {
328             continue;
329         }
330 
331         varbinder::LocalVariable *patternVar = varbinder::Scope::CreateVar(
332             checker->Allocator(), prop->Key()->AsIdentifier()->Name(), varbinder::VariableFlags::PROPERTY, *it);
333         patternVar->SetTsType(patternParamType);
334 
335         if (isOptional) {
336             patternVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
337         }
338 
339         desc->properties.insert(desc->properties.begin(), patternVar);
340     }
341 
342     checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
343     returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS);
344     return returnType;
345 }
346 
CheckAssignmentPattern(Property * prop,varbinder::Variable * & bindingVar,checker::Type * & patternParamType,checker::TSChecker * checker,varbinder::LocalVariable * foundVar)347 bool ObjectExpression::CheckAssignmentPattern(Property *prop, varbinder::Variable *&bindingVar,
348                                               checker::Type *&patternParamType, checker::TSChecker *checker,
349                                               varbinder::LocalVariable *foundVar)
350 {
351     auto *assignmentPattern = prop->Value()->AsAssignmentPattern();
352 
353     if (assignmentPattern->Left()->IsIdentifier()) {
354         bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
355         patternParamType = checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
356         return true;
357     }
358 
359     if (assignmentPattern->Left()->IsArrayPattern()) {
360         auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
361         auto destructuringContext = checker::ArrayDestructuringContext(
362             {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()});
363 
364         if (foundVar != nullptr) {
365             destructuringContext.SetInferredType(
366                 checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferredType()}));
367         }
368 
369         destructuringContext.Start();
370         patternParamType = destructuringContext.InferredType();
371         return true;
372     }
373 
374     ASSERT(assignmentPattern->Left()->IsObjectPattern());
375     auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
376     auto destructuringContext = checker::ObjectDestructuringContext(
377         {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()});
378 
379     if (foundVar != nullptr) {
380         destructuringContext.SetInferredType(
381             checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferredType()}));
382     }
383 
384     destructuringContext.Start();
385     patternParamType = destructuringContext.InferredType();
386     return true;
387 }
388 
Check(checker::TSChecker * checker)389 checker::Type *ObjectExpression::Check(checker::TSChecker *checker)
390 {
391     return checker->GetAnalyzer()->Check(this);
392 }
393 
Compile(compiler::ETSGen * etsg) const394 void ObjectExpression::Compile(compiler::ETSGen *etsg) const
395 {
396     etsg->GetAstCompiler()->Compile(this);
397 }
398 
Check(checker::ETSChecker * checker)399 checker::Type *ObjectExpression::Check(checker::ETSChecker *checker)
400 {
401     return checker->GetAnalyzer()->Check(this);
402 }
403 }  // namespace ark::es2panda::ir
404