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