• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021 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 <util/helpers.h>
19 #include <compiler/base/literals.h>
20 #include <compiler/core/pandagen.h>
21 #include <typescript/checker.h>
22 #include <typescript/core/destructuringContext.h>
23 #include <ir/astDump.h>
24 #include <ir/base/classDefinition.h>
25 #include <ir/base/property.h>
26 #include <ir/base/scriptFunction.h>
27 #include <ir/base/spreadElement.h>
28 #include <ir/expressions/arrayExpression.h>
29 #include <ir/expressions/arrowFunctionExpression.h>
30 #include <ir/expressions/assignmentExpression.h>
31 #include <ir/expressions/classExpression.h>
32 #include <ir/expressions/functionExpression.h>
33 #include <ir/expressions/identifier.h>
34 #include <ir/expressions/literals/nullLiteral.h>
35 #include <ir/expressions/literals/numberLiteral.h>
36 #include <ir/expressions/literals/stringLiteral.h>
37 #include <ir/expressions/literals/taggedLiteral.h>
38 #include <ir/statements/classDeclaration.h>
39 #include <ir/validationInfo.h>
40 #include <util/bitset.h>
41 
42 namespace panda::es2panda::ir {
43 
IsAnonClassOrFuncExpr(const ir::Expression * expr)44 static bool IsAnonClassOrFuncExpr(const ir::Expression *expr)
45 {
46     const ir::Identifier *identifier;
47     switch (expr->Type()) {
48         case ir::AstNodeType::FUNCTION_EXPRESSION: {
49             identifier = expr->AsFunctionExpression()->Function()->Id();
50             break;
51         }
52         case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION: {
53             identifier = expr->AsArrowFunctionExpression()->Function()->Id();
54             break;
55         }
56         case ir::AstNodeType::CLASS_EXPRESSION: {
57             identifier = expr->AsClassExpression()->Definition()->Ident();
58             break;
59         }
60         default: {
61             return false;
62         }
63     }
64     return identifier == nullptr || identifier->Name().Empty();
65 }
66 
IsLegalNameFormat(const ir::Expression * expr)67 static bool IsLegalNameFormat(const ir::Expression *expr)
68 {
69     util::StringView name;
70     if (expr->IsIdentifier()) {
71         name = expr->AsIdentifier()->Name();
72     } else if (expr->IsStringLiteral()) {
73         name = expr->AsStringLiteral()->Str();
74     } else if (expr->IsNumberLiteral()) {
75         name = expr->AsNumberLiteral()->Str();
76     } else {
77         UNREACHABLE();
78     }
79     return name.Find(".") != std::string::npos && name.Find("\\") != std::string::npos;
80 }
81 
82 
ValidateExpression()83 ValidationInfo ObjectExpression::ValidateExpression()
84 {
85     ValidationInfo info;
86     bool foundProto = false;
87 
88     for (auto *it : properties_) {
89         switch (it->Type()) {
90             case AstNodeType::OBJECT_EXPRESSION:
91             case AstNodeType::ARRAY_EXPRESSION: {
92                 return {"Unexpected token.", it->Start()};
93             }
94             case AstNodeType::SPREAD_ELEMENT: {
95                 info = it->AsSpreadElement()->ValidateExpression();
96                 break;
97             }
98             case AstNodeType::PROPERTY: {
99                 auto *prop = it->AsProperty();
100                 info = prop->ValidateExpression();
101 
102                 if (prop->Kind() == PropertyKind::PROTO) {
103                     if (foundProto) {
104                         return {"Duplicate __proto__ fields are not allowed in object literals", prop->Key()->Start()};
105                     }
106 
107                     foundProto = true;
108                 }
109 
110                 break;
111             }
112             default: {
113                 break;
114             }
115         }
116 
117         if (info.Fail()) {
118             break;
119         }
120     }
121 
122     return info;
123 }
124 
ConvertibleToObjectPattern()125 bool ObjectExpression::ConvertibleToObjectPattern()
126 {
127     // TODO(rsipka): throw more precise messages in case of false results
128     bool restFound = false;
129     bool convResult = true;
130 
131     for (auto *it : properties_) {
132         switch (it->Type()) {
133             case AstNodeType::ARRAY_EXPRESSION: {
134                 convResult = it->AsArrayExpression()->ConvertibleToArrayPattern();
135                 break;
136             }
137             case AstNodeType::SPREAD_ELEMENT: {
138                 if (!restFound && it == properties_.back() && !trailingComma_) {
139                     convResult = it->AsSpreadElement()->ConvertibleToRest(isDeclaration_, false);
140                 } else {
141                     convResult = false;
142                 }
143 
144                 restFound = true;
145                 break;
146             }
147             case AstNodeType::OBJECT_EXPRESSION: {
148                 convResult = it->AsObjectExpression()->ConvertibleToObjectPattern();
149                 break;
150             }
151             case AstNodeType::ASSIGNMENT_EXPRESSION: {
152                 convResult = it->AsAssignmentExpression()->ConvertibleToAssignmentPattern();
153                 break;
154             }
155             case AstNodeType::META_PROPERTY_EXPRESSION:
156             case AstNodeType::CHAIN_EXPRESSION:
157             case AstNodeType::SEQUENCE_EXPRESSION: {
158                 convResult = false;
159                 break;
160             }
161             case AstNodeType::PROPERTY: {
162                 convResult = it->AsProperty()->ConventibleToPatternProperty();
163                 break;
164             }
165             default: {
166                 break;
167             }
168         }
169 
170         if (!convResult) {
171             break;
172         }
173     }
174 
175     SetType(AstNodeType::OBJECT_PATTERN);
176     return convResult;
177 }
178 
SetDeclaration()179 void ObjectExpression::SetDeclaration()
180 {
181     isDeclaration_ = true;
182 }
183 
SetOptional(bool optional)184 void ObjectExpression::SetOptional(bool optional)
185 {
186     optional_ = optional;
187 }
188 
SetTsTypeAnnotation(Expression * typeAnnotation)189 void ObjectExpression::SetTsTypeAnnotation(Expression *typeAnnotation)
190 {
191     typeAnnotation_ = typeAnnotation;
192 }
193 
Iterate(const NodeTraverser & cb) const194 void ObjectExpression::Iterate(const NodeTraverser &cb) const
195 {
196     for (auto *it : properties_) {
197         cb(it);
198     }
199 
200     if (typeAnnotation_) {
201         cb(typeAnnotation_);
202     }
203 }
204 
Dump(ir::AstDumper * dumper) const205 void ObjectExpression::Dump(ir::AstDumper *dumper) const
206 {
207     dumper->Add({{"type", (type_ == AstNodeType::OBJECT_EXPRESSION) ? "ObjectExpression" : "ObjectPattern"},
208                  {"properties", properties_},
209                  {"typeAnnotation", AstDumper::Optional(typeAnnotation_)},
210                  {"optional", AstDumper::Optional(optional_)}});
211 }
212 
FillInLiteralBuffer(compiler::LiteralBuffer * buf,std::vector<std::vector<const Literal * >> & tempLiteralBuffer) const213 void ObjectExpression::FillInLiteralBuffer(compiler::LiteralBuffer *buf,
214                                            std::vector<std::vector<const Literal *>> &tempLiteralBuffer) const
215 {
216     for (size_t i = 0 ; i < tempLiteralBuffer.size(); i++) {
217         if (tempLiteralBuffer[i].size() == 0) {
218             continue;
219         }
220 
221         auto propBuf = tempLiteralBuffer[i];
222         for (size_t j = 0; j < propBuf.size(); j++) {
223             buf->Add(propBuf[j]);
224         }
225     }
226 }
227 
EmitCreateObjectWithBuffer(compiler::PandaGen * pg,compiler::LiteralBuffer * buf,bool hasMethod) const228 void ObjectExpression::EmitCreateObjectWithBuffer(compiler::PandaGen *pg, compiler::LiteralBuffer *buf,
229                                                   bool hasMethod) const
230 {
231     if (buf->IsEmpty()) {
232         pg->CreateEmptyObject(this);
233         return;
234     }
235 
236     uint32_t bufIdx = pg->AddLiteralBuffer(buf);
237     pg->CreateObjectWithBuffer(this, bufIdx);
238 }
239 
CreateLiteral(compiler::PandaGen * pg,const ir::Property * prop,util::BitSet * compiled,size_t propIndex)240 static const Literal *CreateLiteral(compiler::PandaGen *pg, const ir::Property *prop, util::BitSet *compiled,
241                                     size_t propIndex)
242 {
243     if (util::Helpers::IsConstantExpr(prop->Value())) {
244         compiled->Set(propIndex);
245         return prop->Value()->AsLiteral();
246     }
247 
248     if (prop->Kind() != ir::PropertyKind::INIT) {
249         ASSERT(prop->IsAccessor());
250         return pg->Allocator()->New<TaggedLiteral>(LiteralTag::ACCESSOR);
251     }
252 
253     if (prop->IsMethod()) {
254         const ir::ScriptFunction *method = prop->Value()->AsFunctionExpression()->Function();
255 
256         LiteralTag tag = LiteralTag::METHOD;
257 
258         if (method->IsGenerator()) {
259             tag = LiteralTag::GENERATOR_METHOD;
260 
261             if (method->IsAsync()) {
262                 tag = LiteralTag::ASYNC_GENERATOR_METHOD;
263             }
264         }
265 
266         compiled->Set(propIndex);
267         return pg->Allocator()->New<TaggedLiteral>(tag, method->Scope()->InternalName());
268     }
269 
270     return pg->Allocator()->New<NullLiteral>();
271 }
272 
CompileStaticProperties(compiler::PandaGen * pg,util::BitSet * compiled) const273 void ObjectExpression::CompileStaticProperties(compiler::PandaGen *pg, util::BitSet *compiled) const
274 {
275     bool hasMethod = false;
276     bool seenComputed = false;
277     auto *buf = pg->NewLiteralBuffer();
278     std::vector<std::vector<const Literal *>> tempLiteralBuffer(properties_.size());
279     std::unordered_map<util::StringView, size_t> propNameMap;
280     std::unordered_map<util::StringView, size_t> getterIndxNameMap;
281     std::unordered_map<util::StringView, size_t> setterIndxNameMap;
282 
283     for (size_t i = 0; i < properties_.size(); i++) {
284         if (properties_[i]->IsSpreadElement()) {
285             seenComputed = true;
286             continue;
287         }
288 
289         const ir::Property *prop = properties_[i]->AsProperty();
290 
291         if (!util::Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed()) ||
292             prop->Kind() == ir::PropertyKind::PROTO) {
293             seenComputed = true;
294             continue;
295         }
296 
297         std::vector<const Literal *> propBuf;
298         util::StringView name = util::Helpers::LiteralToPropName(pg->Allocator(), prop->Key());
299         size_t propIndex = i;
300         auto res = propNameMap.insert({name, propIndex});
301         if (res.second) {    // name not found in map
302             if (seenComputed) {
303                 break;
304             }
305         } else {
306             propIndex = res.first->second;
307 
308             if (prop->Kind() != ir::PropertyKind::SET && getterIndxNameMap.find(name) != getterIndxNameMap.end()) {
309                 compiled->Set(getterIndxNameMap[name]);
310             }
311 
312             if (prop->Kind() != ir::PropertyKind::GET && setterIndxNameMap.find(name) != setterIndxNameMap.end()) {
313                 compiled->Set(setterIndxNameMap[name]);
314             }
315         }
316 
317         if (prop->Kind() == ir::PropertyKind::GET) {
318             getterIndxNameMap[name] = i;
319         } else if (prop->Kind() == ir::PropertyKind::SET) {
320             setterIndxNameMap[name] = i;
321         }
322 
323         propBuf.push_back(pg->Allocator()->New<StringLiteral>(name));
324         propBuf.push_back(CreateLiteral(pg, prop, compiled, i));
325 
326         if (prop->IsMethod()) {
327             hasMethod = true;
328             const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
329             size_t paramNum = func->Function()->FormalParamsLength();
330             Literal *methodAffiliate = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHODAFFILIATE, paramNum);
331             propBuf.push_back(methodAffiliate);
332         }
333 
334         tempLiteralBuffer[propIndex] = propBuf;
335     }
336 
337     FillInLiteralBuffer(buf, tempLiteralBuffer);
338     EmitCreateObjectWithBuffer(pg, buf, hasMethod);
339 }
340 
CompileRemainingProperties(compiler::PandaGen * pg,const util::BitSet * compiled,compiler::VReg objReg) const341 void ObjectExpression::CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled,
342                                                   compiler::VReg objReg) const
343 {
344     for (size_t i = 0; i < properties_.size(); i++) {
345         // TODO: Compile and store only the last one of re-declared prop
346         if (compiled->Test(i)) {
347             continue;
348         }
349 
350         compiler::RegScope rs(pg);
351 
352         if (properties_[i]->IsSpreadElement()) {
353             const ir::SpreadElement *spread = properties_[i]->AsSpreadElement();
354 
355             spread->Argument()->Compile(pg);
356             // srcObj is now stored in acc
357             pg->CopyDataProperties(spread, objReg);
358             continue;
359         }
360 
361         const ir::Property *prop = properties_[i]->AsProperty();
362 
363         switch (prop->Kind()) {
364             case ir::PropertyKind::GET:
365             case ir::PropertyKind::SET: {
366                 compiler::VReg key = pg->LoadPropertyKey(prop->Key(), prop->IsComputed());
367 
368                 compiler::VReg undef = pg->AllocReg();
369                 pg->LoadConst(this, compiler::Constant::JS_UNDEFINED);
370                 pg->StoreAccumulator(this, undef);
371 
372                 compiler::VReg getter = undef;
373                 compiler::VReg setter = undef;
374 
375                 compiler::VReg accessor = pg->AllocReg();
376                 pg->LoadAccumulator(prop->Value(), objReg);
377                 prop->Value()->Compile(pg);
378                 pg->StoreAccumulator(prop->Value(), accessor);
379 
380                 if (prop->Kind() == ir::PropertyKind::GET) {
381                     getter = accessor;
382                 } else {
383                     setter = accessor;
384                 }
385 
386                 pg->DefineGetterSetterByValue(this, objReg, key, getter, setter, prop->IsComputed());
387                 break;
388             }
389             case ir::PropertyKind::INIT: {
390                 compiler::Operand key = pg->ToPropertyKey(prop->Key(), prop->IsComputed());
391 
392                 bool nameSetting = false;
393                 if (prop->IsMethod()) {
394                     pg->LoadAccumulator(prop->Value(), objReg);
395                     if (prop->IsComputed()) {
396                         nameSetting = true;
397                     }
398                 } else {
399                     if (prop->IsComputed()) {
400                         nameSetting = IsAnonClassOrFuncExpr(prop->Value());
401                     } else {
402                         nameSetting = IsAnonClassOrFuncExpr(prop->Value()) && IsLegalNameFormat(prop->Key());
403                     }
404                 }
405 
406                 prop->Value()->Compile(pg);
407                 pg->StoreOwnProperty(this, objReg, key, nameSetting);
408                 break;
409             }
410             case ir::PropertyKind::PROTO: {
411                 prop->Value()->Compile(pg);
412                 compiler::VReg proto = pg->AllocReg();
413                 pg->StoreAccumulator(this, proto);
414 
415                 pg->SetObjectWithProto(this, proto, objReg);
416                 break;
417             }
418             default: {
419                 UNREACHABLE();
420             }
421         }
422     }
423 
424     pg->LoadAccumulator(this, objReg);
425 }
426 
Compile(compiler::PandaGen * pg) const427 void ObjectExpression::Compile(compiler::PandaGen *pg) const
428 {
429     if (properties_.empty()) {
430         pg->CreateEmptyObject(this);
431         return;
432     }
433 
434     util::BitSet compiled(properties_.size());
435     CompileStaticProperties(pg, &compiled);
436 
437     compiler::RegScope rs(pg);
438     compiler::VReg objReg = pg->AllocReg();
439 
440     pg->StoreAccumulator(this, objReg);
441 
442     CompileRemainingProperties(pg, &compiled, objReg);
443 }
444 
CheckPattern(checker::Checker * checker) const445 checker::Type *ObjectExpression::CheckPattern(checker::Checker *checker) const
446 {
447     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
448 
449     bool isOptional = false;
450 
451     for (auto it = properties_.rbegin(); it != properties_.rend(); it++) {
452         if ((*it)->IsRestElement()) {
453             ASSERT((*it)->AsRestElement()->Argument()->IsIdentifier());
454             util::StringView indexInfoName("x");
455             auto *newIndexInfo =
456                 checker->Allocator()->New<checker::IndexInfo>(checker->GlobalAnyType(), indexInfoName, false);
457             desc->stringIndexInfo = newIndexInfo;
458             continue;
459         }
460 
461         ASSERT((*it)->IsProperty());
462         const ir::Property *prop = (*it)->AsProperty();
463 
464         if (prop->IsComputed()) {
465             // TODO(aszilagyi)
466             continue;
467         }
468 
469         binder::LocalVariable *foundVar = desc->FindProperty(prop->Key()->AsIdentifier()->Name());
470         checker::Type *patternParamType = checker->GlobalAnyType();
471         binder::Variable *bindingVar = nullptr;
472 
473         if (prop->IsShorthand()) {
474             switch (prop->Value()->Type()) {
475                 case ir::AstNodeType::IDENTIFIER: {
476                     const ir::Identifier *ident = prop->Value()->AsIdentifier();
477                     ASSERT(ident->Variable());
478                     bindingVar = ident->Variable();
479                     break;
480                 }
481                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
482                     const ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern();
483                     patternParamType = assignmentPattern->Right()->Check(checker);
484                     ASSERT(assignmentPattern->Left()->AsIdentifier()->Variable());
485                     bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
486                     isOptional = true;
487                     break;
488                 }
489                 default: {
490                     UNREACHABLE();
491                 }
492             }
493         } else {
494             switch (prop->Value()->Type()) {
495                 case ir::AstNodeType::IDENTIFIER: {
496                     bindingVar = prop->Value()->AsIdentifier()->Variable();
497                     break;
498                 }
499                 case ir::AstNodeType::ARRAY_PATTERN: {
500                     patternParamType = prop->Value()->AsArrayPattern()->CheckPattern(checker);
501                     break;
502                 }
503                 case ir::AstNodeType::OBJECT_PATTERN: {
504                     patternParamType = prop->Value()->AsObjectPattern()->CheckPattern(checker);
505                     break;
506                 }
507                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
508                     const ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern();
509 
510                     if (assignmentPattern->Left()->IsIdentifier()) {
511                         bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
512                         patternParamType =
513                             checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
514                         isOptional = true;
515                         break;
516                     }
517 
518                     if (assignmentPattern->Left()->IsArrayPattern()) {
519                         auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
520                         auto destructuringContext =
521                             checker::ArrayDestructuringContext(checker, assignmentPattern->Left()->AsArrayPattern(),
522                                                                false, true, nullptr, assignmentPattern->Right());
523 
524                         if (foundVar) {
525                             destructuringContext.SetInferedType(
526                                 checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferedType()}));
527                         }
528 
529                         destructuringContext.Start();
530                         patternParamType = destructuringContext.InferedType();
531                         isOptional = true;
532                         break;
533                     }
534 
535                     ASSERT(assignmentPattern->Left()->IsObjectPattern());
536                     auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
537                     auto destructuringContext =
538                         checker::ObjectDestructuringContext(checker, assignmentPattern->Left()->AsObjectPattern(),
539                                                             false, true, nullptr, assignmentPattern->Right());
540 
541                     if (foundVar) {
542                         destructuringContext.SetInferedType(
543                             checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferedType()}));
544                     }
545 
546                     destructuringContext.Start();
547                     patternParamType = destructuringContext.InferedType();
548                     isOptional = true;
549                     break;
550                 }
551                 default: {
552                     UNREACHABLE();
553                 }
554             }
555         }
556 
557         if (bindingVar) {
558             bindingVar->SetTsType(patternParamType);
559         }
560 
561         if (foundVar) {
562             continue;
563         }
564 
565         binder::LocalVariable *patternVar = binder::Scope::CreateVar(
566             checker->Allocator(), prop->Key()->AsIdentifier()->Name(), binder::VariableFlags::PROPERTY, *it);
567         patternVar->SetTsType(patternParamType);
568 
569         if (isOptional) {
570             patternVar->AddFlag(binder::VariableFlags::OPTIONAL);
571         }
572 
573         desc->properties.insert(desc->properties.begin(), patternVar);
574     }
575 
576     checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
577     returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS);
578     return returnType;
579 }
580 
GetPropertyName(const ir::Expression * key)581 const util::StringView &GetPropertyName(const ir::Expression *key)
582 {
583     if (key->IsIdentifier()) {
584         return key->AsIdentifier()->Name();
585     }
586 
587     if (key->IsStringLiteral()) {
588         return key->AsStringLiteral()->Str();
589     }
590 
591     ASSERT(key->IsNumberLiteral());
592     return key->AsNumberLiteral()->Str();
593 }
594 
GetFlagsForProperty(const ir::Property * prop)595 binder::VariableFlags GetFlagsForProperty(const ir::Property *prop)
596 {
597     if (!prop->IsMethod()) {
598         return binder::VariableFlags::PROPERTY;
599     }
600 
601     binder::VariableFlags propFlags = binder::VariableFlags::METHOD;
602 
603     if (prop->IsAccessor() && prop->Kind() == PropertyKind::GET) {
604         propFlags |= binder::VariableFlags::READONLY;
605     }
606 
607     return propFlags;
608 }
609 
GetTypeForProperty(const ir::Property * prop,checker::Checker * checker)610 checker::Type *GetTypeForProperty(const ir::Property *prop, checker::Checker *checker)
611 {
612     if (prop->IsAccessor()) {
613         checker::Type *funcType = prop->Value()->Check(checker);
614 
615         if (prop->Kind() == PropertyKind::SET) {
616             return checker->GlobalAnyType();
617         }
618 
619         ASSERT(funcType->IsObjectType() && funcType->AsObjectType()->IsFunctionType());
620         return funcType->AsObjectType()->CallSignatures()[0]->ReturnType();
621     }
622 
623     if (prop->IsShorthand()) {
624         return prop->Key()->Check(checker);
625     }
626 
627     return prop->Value()->Check(checker);
628 }
629 
Check(checker::Checker * checker) const630 checker::Type *ObjectExpression::Check(checker::Checker *checker) const
631 {
632     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
633     std::unordered_map<util::StringView, lexer::SourcePosition> allPropertiesMap;
634     bool inConstContext = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
635     ArenaVector<checker::Type *> computedNumberPropTypes(checker->Allocator()->Adapter());
636     ArenaVector<checker::Type *> computedStringPropTypes(checker->Allocator()->Adapter());
637     bool hasComputedNumberProperty = false;
638     bool hasComputedStringProperty = false;
639     bool seenSpread = false;
640 
641     for (const auto *it : properties_) {
642         if (it->IsProperty()) {
643             const ir::Property *prop = it->AsProperty();
644 
645             if (prop->IsComputed()) {
646                 checker::Type *computedNameType = checker->CheckComputedPropertyName(prop->Key());
647 
648                 if (computedNameType->IsNumberType()) {
649                     hasComputedNumberProperty = true;
650                     computedNumberPropTypes.push_back(prop->Value()->Check(checker));
651                     continue;
652                 }
653 
654                 if (computedNameType->IsStringType()) {
655                     hasComputedStringProperty = true;
656                     computedStringPropTypes.push_back(prop->Value()->Check(checker));
657                     continue;
658                 }
659             }
660 
661             checker::Type *propType = GetTypeForProperty(prop, checker);
662             binder::VariableFlags flags = GetFlagsForProperty(prop);
663             const util::StringView &propName = GetPropertyName(prop->Key());
664 
665             auto *memberVar = binder::Scope::CreateVar(checker->Allocator(), propName, flags, it);
666 
667             if (inConstContext) {
668                 memberVar->AddFlag(binder::VariableFlags::READONLY);
669             } else {
670                 propType = checker->GetBaseTypeOfLiteralType(propType);
671             }
672 
673             memberVar->SetTsType(propType);
674 
675             if (prop->Key()->IsNumberLiteral()) {
676                 memberVar->AddFlag(binder::VariableFlags::NUMERIC_NAME);
677             }
678 
679             binder::LocalVariable *foundMember = desc->FindProperty(propName);
680             allPropertiesMap.insert({propName, it->Start()});
681 
682             if (foundMember) {
683                 foundMember->SetTsType(propType);
684                 continue;
685             }
686 
687             desc->properties.push_back(memberVar);
688             continue;
689         }
690 
691         ASSERT(it->IsSpreadElement());
692 
693         checker::Type *spreadType = it->AsSpreadElement()->Argument()->Check(checker);
694         seenSpread = true;
695 
696         // TODO(aszilagyi): handle union of object types
697         if (!spreadType->IsObjectType()) {
698             checker->ThrowTypeError("Spread types may only be created from object types.", it->Start());
699         }
700 
701         for (auto *spreadProp : spreadType->AsObjectType()->Properties()) {
702             auto found = allPropertiesMap.find(spreadProp->Name());
703             if (found != allPropertiesMap.end()) {
704                 checker->ThrowTypeError(
705                     {found->first, " is specified more than once, so this usage will be overwritten."}, found->second);
706             }
707 
708             binder::LocalVariable *foundMember = desc->FindProperty(spreadProp->Name());
709 
710             if (foundMember) {
711                 foundMember->SetTsType(spreadProp->TsType());
712                 continue;
713             }
714 
715             desc->properties.push_back(spreadProp);
716         }
717     }
718 
719     if (!seenSpread && (hasComputedNumberProperty || hasComputedStringProperty)) {
720         for (auto *it : desc->properties) {
721             computedStringPropTypes.push_back(it->TsType());
722 
723             if (hasComputedNumberProperty && it->HasFlag(binder::VariableFlags::NUMERIC_NAME)) {
724                 computedNumberPropTypes.push_back(it->TsType());
725             }
726         }
727 
728         if (hasComputedNumberProperty) {
729             desc->numberIndexInfo = checker->Allocator()->New<checker::IndexInfo>(
730                 checker->CreateUnionType(std::move(computedNumberPropTypes)), "x", inConstContext);
731         }
732 
733         if (hasComputedStringProperty) {
734             desc->stringIndexInfo = checker->Allocator()->New<checker::IndexInfo>(
735                 checker->CreateUnionType(std::move(computedStringPropTypes)), "x", inConstContext);
736         }
737     }
738 
739     checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
740     returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS |
741                                               checker::ObjectFlags::CHECK_EXCESS_PROPS);
742     return returnType;
743 }
744 
UpdateSelf(const NodeUpdater & cb,binder::Binder * binder)745 void ObjectExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
746 {
747     for (auto iter = properties_.begin(); iter != properties_.end(); iter++) {
748         *iter = std::get<ir::AstNode *>(cb(*iter))->AsExpression();
749     }
750 
751     if (typeAnnotation_) {
752         typeAnnotation_ = std::get<ir::AstNode *>(cb(typeAnnotation_))->AsExpression();
753     }
754 }
755 
756 }  // namespace panda::es2panda::ir
757