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