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