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([[maybe_unused]] 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("{");
245     if (!properties_.empty()) {
246         dumper->IncrIndent();
247         dumper->Endl();
248         for (auto property : properties_) {
249             property->Dump(dumper);
250             dumper->Add(",");
251             if (property == properties_.back()) {
252                 dumper->DecrIndent();
253             }
254             dumper->Endl();
255         }
256     }
257     dumper->Add("}");
258 }
259 
Compile([[maybe_unused]] compiler::PandaGen *pg) const260 void ObjectExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const
261 {
262     pg->GetAstCompiler()->Compile(this);
263 }
264 
CheckPattern(checker::TSChecker *checker)265 checker::Type *ObjectExpression::CheckPattern(checker::TSChecker *checker)
266 {
267     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
268 
269     bool isOptional = false;
270 
271     for (auto it = properties_.rbegin(); it != properties_.rend(); it++) {
272         if ((*it)->IsRestElement()) {
273             ASSERT((*it)->AsRestElement()->Argument()->IsIdentifier());
274             util::StringView indexInfoName("x");
275             auto *newIndexInfo =
276                 checker->Allocator()->New<checker::IndexInfo>(checker->GlobalAnyType(), indexInfoName, false);
277             desc->stringIndexInfo = newIndexInfo;
278             continue;
279         }
280 
281         ASSERT((*it)->IsProperty());
282         auto *prop = (*it)->AsProperty();
283 
284         if (prop->IsComputed()) {
285             continue;
286         }
287 
288         varbinder::LocalVariable *foundVar = desc->FindProperty(prop->Key()->AsIdentifier()->Name());
289         checker::Type *patternParamType = checker->GlobalAnyType();
290         varbinder::Variable *bindingVar = nullptr;
291 
292         if (prop->IsShorthand()) {
293             switch (prop->Value()->Type()) {
294                 case ir::AstNodeType::IDENTIFIER: {
295                     const ir::Identifier *ident = prop->Value()->AsIdentifier();
296                     ASSERT(ident->Variable());
297                     bindingVar = ident->Variable();
298                     break;
299                 }
300                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
301                     auto *assignmentPattern = prop->Value()->AsAssignmentPattern();
302                     patternParamType = assignmentPattern->Right()->Check(checker);
303                     ASSERT(assignmentPattern->Left()->AsIdentifier()->Variable());
304                     bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
305                     isOptional = true;
306                     break;
307                 }
308                 default: {
309                     UNREACHABLE();
310                 }
311             }
312         } else {
313             switch (prop->Value()->Type()) {
314                 case ir::AstNodeType::IDENTIFIER: {
315                     bindingVar = prop->Value()->AsIdentifier()->Variable();
316                     break;
317                 }
318                 case ir::AstNodeType::ARRAY_PATTERN: {
319                     patternParamType = prop->Value()->AsArrayPattern()->CheckPattern(checker);
320                     break;
321                 }
322                 case ir::AstNodeType::OBJECT_PATTERN: {
323                     patternParamType = prop->Value()->AsObjectPattern()->CheckPattern(checker);
324                     break;
325                 }
326                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
327                     isOptional = CheckAssignmentPattern(prop, bindingVar, patternParamType, checker, foundVar);
328                     break;
329                 }
330                 default: {
331                     UNREACHABLE();
332                 }
333             }
334         }
335 
336         if (bindingVar != nullptr) {
337             bindingVar->SetTsType(patternParamType);
338         }
339 
340         if (foundVar != nullptr) {
341             continue;
342         }
343 
344         varbinder::LocalVariable *patternVar = varbinder::Scope::CreateVar(
345             checker->Allocator(), prop->Key()->AsIdentifier()->Name(), varbinder::VariableFlags::PROPERTY, *it);
346         patternVar->SetTsType(patternParamType);
347 
348         if (isOptional) {
349             patternVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
350         }
351 
352         desc->properties.insert(desc->properties.begin(), patternVar);
353     }
354 
355     checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
356     returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS);
357     return returnType;
358 }
359 
CheckAssignmentPattern(Property *prop, varbinder::Variable *&bindingVar, checker::Type *&patternParamType, checker::TSChecker *checker, varbinder::LocalVariable *foundVar)360 bool ObjectExpression::CheckAssignmentPattern(Property *prop, varbinder::Variable *&bindingVar,
361                                               checker::Type *&patternParamType, checker::TSChecker *checker,
362                                               varbinder::LocalVariable *foundVar)
363 {
364     auto *assignmentPattern = prop->Value()->AsAssignmentPattern();
365 
366     if (assignmentPattern->Left()->IsIdentifier()) {
367         bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
368         patternParamType = checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
369         return true;
370     }
371 
372     if (assignmentPattern->Left()->IsArrayPattern()) {
373         auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
374         auto destructuringContext = checker::ArrayDestructuringContext(
375             {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()});
376 
377         if (foundVar != nullptr) {
378             destructuringContext.SetInferredType(
379                 checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferredType()}));
380         }
381 
382         destructuringContext.Start();
383         patternParamType = destructuringContext.InferredType();
384         return true;
385     }
386 
387     ASSERT(assignmentPattern->Left()->IsObjectPattern());
388     auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
389     auto destructuringContext = checker::ObjectDestructuringContext(
390         {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()});
391 
392     if (foundVar != nullptr) {
393         destructuringContext.SetInferredType(
394             checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferredType()}));
395     }
396 
397     destructuringContext.Start();
398     patternParamType = destructuringContext.InferredType();
399     return true;
400 }
401 
Check(checker::TSChecker *checker)402 checker::Type *ObjectExpression::Check(checker::TSChecker *checker)
403 {
404     return checker->GetAnalyzer()->Check(this);
405 }
406 
Compile(compiler::ETSGen *etsg) const407 void ObjectExpression::Compile(compiler::ETSGen *etsg) const
408 {
409     etsg->GetAstCompiler()->Compile(this);
410 }
411 
Check(checker::ETSChecker *checker)412 checker::Type *ObjectExpression::Check(checker::ETSChecker *checker)
413 {
414     return checker->GetAnalyzer()->Check(this);
415 }
416 }  // namespace ark::es2panda::ir
417