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/expressions/unaryExpression.h>
39 #include <ir/statements/classDeclaration.h>
40 #include <ir/validationInfo.h>
41 #include <util/bitset.h>
42 
43 namespace panda::es2panda::ir {
44 
IsAnonClassOrFuncExpr(const ir::Expression *expr)45 static bool IsAnonClassOrFuncExpr(const ir::Expression *expr)
46 {
47     const ir::Identifier *identifier;
48     switch (expr->Type()) {
49         case ir::AstNodeType::FUNCTION_EXPRESSION: {
50             identifier = expr->AsFunctionExpression()->Function()->Id();
51             break;
52         }
53         case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION: {
54             identifier = expr->AsArrowFunctionExpression()->Function()->Id();
55             break;
56         }
57         case ir::AstNodeType::CLASS_EXPRESSION: {
58             identifier = expr->AsClassExpression()->Definition()->Ident();
59             break;
60         }
61         default: {
62             return false;
63         }
64     }
65     return identifier == nullptr || identifier->Name().Empty();
66 }
67 
IsLegalNameFormat(const ir::Expression *expr)68 static bool IsLegalNameFormat(const ir::Expression *expr)
69 {
70     util::StringView name;
71     if (expr->IsIdentifier()) {
72         name = expr->AsIdentifier()->Name();
73     } else if (expr->IsStringLiteral()) {
74         name = expr->AsStringLiteral()->Str();
75     } else if (expr->IsNumberLiteral()) {
76         name = expr->AsNumberLiteral()->Str();
77     } else {
78         UNREACHABLE();
79     }
80     return name.Find(".") != std::string::npos && name.Find("\\") != std::string::npos;
81 }
82 
83 
ValidateExpression()84 ValidationInfo ObjectExpression::ValidateExpression()
85 {
86     ValidationInfo info;
87     bool foundProto = false;
88 
89     for (auto *it : properties_) {
90         switch (it->Type()) {
91             case AstNodeType::OBJECT_EXPRESSION:
92             case AstNodeType::ARRAY_EXPRESSION: {
93                 return {"Unexpected token.", it->Start()};
94             }
95             case AstNodeType::SPREAD_ELEMENT: {
96                 info = it->AsSpreadElement()->ValidateExpression();
97                 break;
98             }
99             case AstNodeType::PROPERTY: {
100                 auto *prop = it->AsProperty();
101                 info = prop->ValidateExpression();
102 
103                 if (prop->Kind() == PropertyKind::PROTO) {
104                     if (foundProto) {
105                         return {"Duplicate __proto__ fields are not allowed in object literals", prop->Key()->Start()};
106                     }
107 
108                     foundProto = true;
109                 }
110 
111                 break;
112             }
113             default: {
114                 break;
115             }
116         }
117 
118         if (info.Fail()) {
119             break;
120         }
121     }
122 
123     return info;
124 }
125 
ConvertibleToObjectPattern()126 bool ObjectExpression::ConvertibleToObjectPattern()
127 {
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     int32_t bufIdx = pg->AddLiteralBuffer(buf);
237     pg->CreateObjectWithBuffer(this, static_cast<uint32_t>(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     auto *buf = pg->NewLiteralBuffer();
277     std::vector<std::vector<const Literal *>> tempLiteralBuffer(properties_.size());
278     std::unordered_map<util::StringView, size_t> propNameMap;
279     std::unordered_map<util::StringView, size_t> getterIndxNameMap;
280     std::unordered_map<util::StringView, size_t> setterIndxNameMap;
281 
282     for (size_t i = 0; i < properties_.size(); i++) {
283         if (properties_[i]->IsSpreadElement()) {
284             break;
285         }
286 
287         const ir::Property *prop = properties_[i]->AsProperty();
288 
289         if (!util::Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed()) ||
290             prop->Kind() == ir::PropertyKind::PROTO) {
291             break;
292         }
293 
294         std::vector<const Literal *> propBuf;
295         util::StringView name = util::Helpers::LiteralToPropName(pg->Allocator(), prop->Key());
296         size_t propIndex = i;
297         auto res = propNameMap.insert({name, propIndex});
298         if (!res.second) {    // name is found in map
299             propIndex = res.first->second;
300 
301             if (prop->Kind() != ir::PropertyKind::SET && getterIndxNameMap.find(name) != getterIndxNameMap.end()) {
302                 compiled->Set(getterIndxNameMap[name]);
303             }
304 
305             if (prop->Kind() != ir::PropertyKind::GET && setterIndxNameMap.find(name) != setterIndxNameMap.end()) {
306                 compiled->Set(setterIndxNameMap[name]);
307             }
308         }
309 
310         if (prop->Kind() == ir::PropertyKind::GET) {
311             getterIndxNameMap[name] = i;
312         } else if (prop->Kind() == ir::PropertyKind::SET) {
313             setterIndxNameMap[name] = i;
314         }
315 
316         propBuf.push_back(pg->Allocator()->New<StringLiteral>(name));
317         propBuf.push_back(CreateLiteral(pg, prop, compiled, i));
318 
319         if (prop->IsMethod()) {
320             hasMethod = true;
321             const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
322             size_t paramNum = func->Function()->FormalParamsLength();
323             Literal *methodAffiliate = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHODAFFILIATE, paramNum);
324             propBuf.push_back(methodAffiliate);
325         }
326 
327         tempLiteralBuffer[propIndex] = propBuf;
328     }
329 
330     FillInLiteralBuffer(buf, tempLiteralBuffer);
331     EmitCreateObjectWithBuffer(pg, buf, hasMethod);
332 }
333 
CompilePropertyOfGetterOrSetter(compiler::PandaGen *pg, const ir::Property *prop, compiler::VReg objReg) const334 void ObjectExpression::CompilePropertyOfGetterOrSetter(compiler::PandaGen *pg, const ir::Property *prop,
335     compiler::VReg objReg) const
336 {
337     compiler::VReg key = pg->LoadPropertyKey(prop->Key(), prop->IsComputed());
338 
339     compiler::VReg undef = pg->AllocReg();
340     pg->LoadConst(this, compiler::Constant::JS_UNDEFINED);
341     pg->StoreAccumulator(this, undef);
342 
343     compiler::VReg getter = undef;
344     compiler::VReg setter = undef;
345 
346     compiler::VReg accessor = pg->AllocReg();
347     pg->LoadAccumulator(prop->Value(), objReg);
348     prop->Value()->Compile(pg);
349     pg->StoreAccumulator(prop->Value(), accessor);
350 
351     if (prop->Kind() == ir::PropertyKind::GET) {
352         getter = accessor;
353     } else {
354         setter = accessor;
355     }
356 
357     pg->DefineGetterSetterByValue(this, objReg, key, getter, setter, prop->IsComputed());
358 }
359 
CompilePropertyWithInit(compiler::PandaGen *pg, const ir::Property *prop, compiler::VReg objReg) const360 void ObjectExpression::CompilePropertyWithInit(compiler::PandaGen *pg, const ir::Property *prop,
361     compiler::VReg objReg) const
362 {
363     compiler::Operand key = pg->ToPropertyKey(prop->Key(), prop->IsComputed());
364     const auto *value = prop->Value();
365 
366     bool nameSetting = false;
367     if (prop->IsMethod()) {
368         pg->LoadAccumulator(value, objReg);
369         if (prop->IsComputed()) {
370             nameSetting = true;
371         }
372     } else {
373         if (prop->IsComputed()) {
374             nameSetting = IsAnonClassOrFuncExpr(value);
375         } else {
376             nameSetting = IsAnonClassOrFuncExpr(value) && IsLegalNameFormat(prop->Key());
377         }
378     }
379 
380     // This is for disallowing breakpoint on property with negative number as initializer
381     // TODO: remove setting invalid flag after puttting negative number into literal buffer
382     bool shouldSetInvalidFlag = value->IsUnaryExpression() && value->AsUnaryExpression()->IsNegativeNumber()
383         && !prop->IsComputed();
384     if (shouldSetInvalidFlag) {
385         pg->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION);
386     }
387 
388     value->Compile(pg);
389     if (!nameSetting && pg->Binder()->Program()->TargetApiVersion() > 10) {
390         pg->DefineOwnProperty(this, objReg, key);
391     } else {
392         pg->StoreOwnProperty(this, objReg, key, nameSetting);
393     }
394     pg->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
395 }
396 
CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled, compiler::VReg objReg) const397 void ObjectExpression::CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled,
398                                                   compiler::VReg objReg) const
399 {
400     for (size_t i = 0; i < properties_.size(); i++) {
401         if (compiled->Test(i)) {
402             continue;
403         }
404 
405         compiler::RegScope rs(pg);
406 
407         if (properties_[i]->IsSpreadElement()) {
408             const ir::SpreadElement *spread = properties_[i]->AsSpreadElement();
409 
410             spread->Argument()->Compile(pg);
411             // srcObj is now stored in acc
412             pg->CopyDataProperties(spread, objReg);
413             continue;
414         }
415 
416         const ir::Property *prop = properties_[i]->AsProperty();
417 
418         switch (prop->Kind()) {
419             case ir::PropertyKind::GET:
420             case ir::PropertyKind::SET: {
421                 CompilePropertyOfGetterOrSetter(pg, prop, objReg);
422                 break;
423             }
424             case ir::PropertyKind::INIT: {
425                 CompilePropertyWithInit(pg, prop, objReg);
426                 break;
427             }
428             case ir::PropertyKind::PROTO: {
429                 prop->Value()->Compile(pg);
430                 compiler::VReg proto = pg->AllocReg();
431                 pg->StoreAccumulator(this, proto);
432 
433                 pg->SetObjectWithProto(this, proto, objReg);
434                 break;
435             }
436             default: {
437                 UNREACHABLE();
438             }
439         }
440     }
441 
442     pg->LoadAccumulator(this, objReg);
443 }
444 
Compile(compiler::PandaGen *pg) const445 void ObjectExpression::Compile(compiler::PandaGen *pg) const
446 {
447     if (properties_.empty()) {
448         pg->CreateEmptyObject(this);
449         return;
450     }
451 
452     util::BitSet compiled(properties_.size());
453     CompileStaticProperties(pg, &compiled);
454 
455     compiler::RegScope rs(pg);
456     compiler::VReg objReg = pg->AllocReg();
457 
458     pg->StoreAccumulator(this, objReg);
459 
460     CompileRemainingProperties(pg, &compiled, objReg);
461 }
462 
CheckPattern(checker::Checker *checker) const463 checker::Type *ObjectExpression::CheckPattern(checker::Checker *checker) const
464 {
465     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
466     CHECK_NOT_NULL(desc);
467     bool isOptional = false;
468 
469     for (auto it = properties_.rbegin(); it != properties_.rend(); it++) {
470         if ((*it)->IsRestElement()) {
471             ASSERT((*it)->AsRestElement()->Argument()->IsIdentifier());
472             util::StringView indexInfoName("x");
473             auto *newIndexInfo =
474                 checker->Allocator()->New<checker::IndexInfo>(checker->GlobalAnyType(), indexInfoName, false);
475             desc->stringIndexInfo = newIndexInfo;
476             continue;
477         }
478 
479         ASSERT((*it)->IsProperty());
480         const ir::Property *prop = (*it)->AsProperty();
481 
482         if (prop->IsComputed()) {
483             // TODO(aszilagyi)
484             continue;
485         }
486 
487         binder::LocalVariable *foundVar = desc->FindProperty(prop->Key()->AsIdentifier()->Name());
488         checker::Type *patternParamType = checker->GlobalAnyType();
489         binder::Variable *bindingVar = nullptr;
490 
491         if (prop->IsShorthand()) {
492             switch (prop->Value()->Type()) {
493                 case ir::AstNodeType::IDENTIFIER: {
494                     const ir::Identifier *ident = prop->Value()->AsIdentifier();
495                     ASSERT(ident->Variable());
496                     bindingVar = ident->Variable();
497                     break;
498                 }
499                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
500                     const ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern();
501                     patternParamType = assignmentPattern->Right()->Check(checker);
502                     ASSERT(assignmentPattern->Left()->AsIdentifier()->Variable());
503                     bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
504                     isOptional = true;
505                     break;
506                 }
507                 default: {
508                     UNREACHABLE();
509                 }
510             }
511         } else {
512             switch (prop->Value()->Type()) {
513                 case ir::AstNodeType::IDENTIFIER: {
514                     bindingVar = prop->Value()->AsIdentifier()->Variable();
515                     break;
516                 }
517                 case ir::AstNodeType::ARRAY_PATTERN: {
518                     patternParamType = prop->Value()->AsArrayPattern()->CheckPattern(checker);
519                     break;
520                 }
521                 case ir::AstNodeType::OBJECT_PATTERN: {
522                     patternParamType = prop->Value()->AsObjectPattern()->CheckPattern(checker);
523                     break;
524                 }
525                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
526                     const ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern();
527 
528                     if (assignmentPattern->Left()->IsIdentifier()) {
529                         bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
530                         patternParamType =
531                             checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
532                         isOptional = true;
533                         break;
534                     }
535 
536                     if (assignmentPattern->Left()->IsArrayPattern()) {
537                         auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
538                         auto destructuringContext =
539                             checker::ArrayDestructuringContext(checker, assignmentPattern->Left()->AsArrayPattern(),
540                                                                false, true, nullptr, assignmentPattern->Right());
541 
542                         if (foundVar) {
543                             destructuringContext.SetInferedType(
544                                 checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferedType()}));
545                         }
546 
547                         destructuringContext.Start();
548                         patternParamType = destructuringContext.InferedType();
549                         isOptional = true;
550                         break;
551                     }
552 
553                     ASSERT(assignmentPattern->Left()->IsObjectPattern());
554                     auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
555                     auto destructuringContext =
556                         checker::ObjectDestructuringContext(checker, assignmentPattern->Left()->AsObjectPattern(),
557                                                             false, true, nullptr, assignmentPattern->Right());
558 
559                     if (foundVar) {
560                         destructuringContext.SetInferedType(
561                             checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferedType()}));
562                     }
563 
564                     destructuringContext.Start();
565                     patternParamType = destructuringContext.InferedType();
566                     isOptional = true;
567                     break;
568                 }
569                 default: {
570                     UNREACHABLE();
571                 }
572             }
573         }
574 
575         if (bindingVar) {
576             bindingVar->SetTsType(patternParamType);
577         }
578 
579         if (foundVar) {
580             continue;
581         }
582 
583         binder::LocalVariable *patternVar = binder::Scope::CreateVar(
584             checker->Allocator(), prop->Key()->AsIdentifier()->Name(), binder::VariableFlags::PROPERTY, *it);
585         patternVar->SetTsType(patternParamType);
586 
587         if (isOptional) {
588             patternVar->AddFlag(binder::VariableFlags::OPTIONAL);
589         }
590 
591         desc->properties.insert(desc->properties.begin(), patternVar);
592     }
593 
594     checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
595     CHECK_NOT_NULL(returnType);
596     returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS);
597     return returnType;
598 }
599 
GetPropertyName(const ir::Expression *key)600 const util::StringView &GetPropertyName(const ir::Expression *key)
601 {
602     if (key->IsIdentifier()) {
603         return key->AsIdentifier()->Name();
604     }
605 
606     if (key->IsStringLiteral()) {
607         return key->AsStringLiteral()->Str();
608     }
609 
610     ASSERT(key->IsNumberLiteral());
611     return key->AsNumberLiteral()->Str();
612 }
613 
GetFlagsForProperty(const ir::Property *prop)614 binder::VariableFlags GetFlagsForProperty(const ir::Property *prop)
615 {
616     if (!prop->IsMethod()) {
617         return binder::VariableFlags::PROPERTY;
618     }
619 
620     binder::VariableFlags propFlags = binder::VariableFlags::METHOD;
621 
622     if (prop->IsAccessor() && prop->Kind() == PropertyKind::GET) {
623         propFlags |= binder::VariableFlags::READONLY;
624     }
625 
626     return propFlags;
627 }
628 
GetTypeForProperty(const ir::Property *prop, checker::Checker *checker)629 checker::Type *GetTypeForProperty(const ir::Property *prop, checker::Checker *checker)
630 {
631     if (prop->IsAccessor()) {
632         checker::Type *funcType = prop->Value()->Check(checker);
633 
634         if (prop->Kind() == PropertyKind::SET) {
635             return checker->GlobalAnyType();
636         }
637 
638         ASSERT(funcType->IsObjectType() && funcType->AsObjectType()->IsFunctionType());
639         return funcType->AsObjectType()->CallSignatures()[0]->ReturnType();
640     }
641 
642     if (prop->IsShorthand()) {
643         return prop->Key()->Check(checker);
644     }
645 
646     return prop->Value()->Check(checker);
647 }
648 
Check(checker::Checker *checker) const649 checker::Type *ObjectExpression::Check(checker::Checker *checker) const
650 {
651     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
652     CHECK_NOT_NULL(desc);
653     std::unordered_map<util::StringView, lexer::SourcePosition> allPropertiesMap;
654     bool inConstContext = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
655     ArenaVector<checker::Type *> computedNumberPropTypes(checker->Allocator()->Adapter());
656     ArenaVector<checker::Type *> computedStringPropTypes(checker->Allocator()->Adapter());
657     bool hasComputedNumberProperty = false;
658     bool hasComputedStringProperty = false;
659     bool seenSpread = false;
660 
661     for (const auto *it : properties_) {
662         if (it->IsProperty()) {
663             const ir::Property *prop = it->AsProperty();
664 
665             if (prop->IsComputed()) {
666                 checker::Type *computedNameType = checker->CheckComputedPropertyName(prop->Key());
667 
668                 if (computedNameType->IsNumberType()) {
669                     hasComputedNumberProperty = true;
670                     computedNumberPropTypes.push_back(prop->Value()->Check(checker));
671                     continue;
672                 }
673 
674                 if (computedNameType->IsStringType()) {
675                     hasComputedStringProperty = true;
676                     computedStringPropTypes.push_back(prop->Value()->Check(checker));
677                     continue;
678                 }
679             }
680 
681             checker::Type *propType = GetTypeForProperty(prop, checker);
682             binder::VariableFlags flags = GetFlagsForProperty(prop);
683             const util::StringView &propName = GetPropertyName(prop->Key());
684 
685             auto *memberVar = binder::Scope::CreateVar(checker->Allocator(), propName, flags, it);
686             CHECK_NOT_NULL(memberVar);
687             if (inConstContext) {
688                 memberVar->AddFlag(binder::VariableFlags::READONLY);
689             } else {
690                 propType = checker->GetBaseTypeOfLiteralType(propType);
691             }
692 
693             memberVar->SetTsType(propType);
694 
695             if (prop->Key()->IsNumberLiteral()) {
696                 memberVar->AddFlag(binder::VariableFlags::NUMERIC_NAME);
697             }
698 
699             binder::LocalVariable *foundMember = desc->FindProperty(propName);
700             allPropertiesMap.insert({propName, it->Start()});
701 
702             if (foundMember) {
703                 foundMember->SetTsType(propType);
704                 continue;
705             }
706 
707             desc->properties.push_back(memberVar);
708             continue;
709         }
710 
711         ASSERT(it->IsSpreadElement());
712 
713         checker::Type *spreadType = it->AsSpreadElement()->Argument()->Check(checker);
714         seenSpread = true;
715 
716         // TODO(aszilagyi): handle union of object types
717         if (!spreadType->IsObjectType()) {
718             checker->ThrowTypeError("Spread types may only be created from object types.", it->Start());
719         }
720 
721         for (auto *spreadProp : spreadType->AsObjectType()->Properties()) {
722             auto found = allPropertiesMap.find(spreadProp->Name());
723             if (found != allPropertiesMap.end()) {
724                 checker->ThrowTypeError(
725                     {found->first, " is specified more than once, so this usage will be overwritten."}, found->second);
726             }
727 
728             binder::LocalVariable *foundMember = desc->FindProperty(spreadProp->Name());
729 
730             if (foundMember) {
731                 foundMember->SetTsType(spreadProp->TsType());
732                 continue;
733             }
734 
735             desc->properties.push_back(spreadProp);
736         }
737     }
738 
739     if (!seenSpread && (hasComputedNumberProperty || hasComputedStringProperty)) {
740         for (auto *it : desc->properties) {
741             computedStringPropTypes.push_back(it->TsType());
742 
743             if (hasComputedNumberProperty && it->HasFlag(binder::VariableFlags::NUMERIC_NAME)) {
744                 computedNumberPropTypes.push_back(it->TsType());
745             }
746         }
747 
748         if (hasComputedNumberProperty) {
749             desc->numberIndexInfo = checker->Allocator()->New<checker::IndexInfo>(
750                 checker->CreateUnionType(std::move(computedNumberPropTypes)), "x", inConstContext);
751         }
752 
753         if (hasComputedStringProperty) {
754             desc->stringIndexInfo = checker->Allocator()->New<checker::IndexInfo>(
755                 checker->CreateUnionType(std::move(computedStringPropTypes)), "x", inConstContext);
756         }
757     }
758 
759     checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
760     CHECK_NOT_NULL(returnType);
761     returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS |
762                                               checker::ObjectFlags::CHECK_EXCESS_PROPS);
763     return returnType;
764 }
765 
UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)766 void ObjectExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
767 {
768     for (auto iter = properties_.begin(); iter != properties_.end(); iter++) {
769         *iter = std::get<ir::AstNode *>(cb(*iter))->AsExpression();
770     }
771 
772     if (typeAnnotation_) {
773         typeAnnotation_ = std::get<ir::AstNode *>(cb(typeAnnotation_))->AsExpression();
774     }
775 }
776 
777 }  // namespace panda::es2panda::ir
778