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