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