• 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/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     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 
CompilePropertyOfGetterOrSetter(compiler::PandaGen * pg,const ir::Property * prop,compiler::VReg objReg) const341 void ObjectExpression::CompilePropertyOfGetterOrSetter(compiler::PandaGen *pg, const ir::Property *prop,
342     compiler::VReg objReg) const
343 {
344     compiler::VReg key = pg->LoadPropertyKey(prop->Key(), prop->IsComputed());
345 
346     compiler::VReg undef = pg->AllocReg();
347     pg->LoadConst(this, compiler::Constant::JS_UNDEFINED);
348     pg->StoreAccumulator(this, undef);
349 
350     compiler::VReg getter = undef;
351     compiler::VReg setter = undef;
352 
353     compiler::VReg accessor = pg->AllocReg();
354     pg->LoadAccumulator(prop->Value(), objReg);
355     prop->Value()->Compile(pg);
356     pg->StoreAccumulator(prop->Value(), accessor);
357 
358     if (prop->Kind() == ir::PropertyKind::GET) {
359         getter = accessor;
360     } else {
361         setter = accessor;
362     }
363 
364     pg->DefineGetterSetterByValue(this, objReg, key, getter, setter, prop->IsComputed());
365 }
366 
CompilePropertyWithInit(compiler::PandaGen * pg,const ir::Property * prop,compiler::VReg objReg) const367 void ObjectExpression::CompilePropertyWithInit(compiler::PandaGen *pg, const ir::Property *prop,
368     compiler::VReg objReg) const
369 {
370     compiler::Operand key = pg->ToPropertyKey(prop->Key(), prop->IsComputed());
371     const auto *value = prop->Value();
372 
373     bool nameSetting = false;
374     if (prop->IsMethod()) {
375         pg->LoadAccumulator(value, objReg);
376         if (prop->IsComputed()) {
377             nameSetting = true;
378         }
379     } else {
380         if (prop->IsComputed()) {
381             nameSetting = IsAnonClassOrFuncExpr(value);
382         } else {
383             nameSetting = IsAnonClassOrFuncExpr(value) && IsLegalNameFormat(prop->Key());
384         }
385     }
386 
387     // This is for disallowing breakpoint on property with negative number as initializer
388     // TODO: remove setting invalid flag after puttting negative number into literal buffer
389     bool shouldSetInvalidFlag = value->IsUnaryExpression() && value->AsUnaryExpression()->IsNegativeNumber()
390         && !prop->IsComputed();
391     if (shouldSetInvalidFlag) {
392         pg->SetSourceLocationFlag(lexer::SourceLocationFlag::INVALID_SOURCE_LOCATION);
393     }
394 
395     value->Compile(pg);
396     if (!nameSetting && pg->Binder()->Program()->TargetApiVersion() > 10) {
397         pg->DefineOwnProperty(this, objReg, key);
398     } else {
399         pg->StoreOwnProperty(this, objReg, key, nameSetting);
400     }
401     pg->SetSourceLocationFlag(lexer::SourceLocationFlag::VALID_SOURCE_LOCATION);
402 }
403 
CompileRemainingProperties(compiler::PandaGen * pg,const util::BitSet * compiled,compiler::VReg objReg) const404 void ObjectExpression::CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled,
405                                                   compiler::VReg objReg) const
406 {
407     for (size_t i = 0; i < properties_.size(); i++) {
408         if (compiled->Test(i)) {
409             continue;
410         }
411 
412         compiler::RegScope rs(pg);
413 
414         if (properties_[i]->IsSpreadElement()) {
415             const ir::SpreadElement *spread = properties_[i]->AsSpreadElement();
416 
417             spread->Argument()->Compile(pg);
418             // srcObj is now stored in acc
419             pg->CopyDataProperties(spread, objReg);
420             continue;
421         }
422 
423         const ir::Property *prop = properties_[i]->AsProperty();
424 
425         switch (prop->Kind()) {
426             case ir::PropertyKind::GET:
427             case ir::PropertyKind::SET: {
428                 CompilePropertyOfGetterOrSetter(pg, prop, objReg);
429                 break;
430             }
431             case ir::PropertyKind::INIT: {
432                 CompilePropertyWithInit(pg, prop, objReg);
433                 break;
434             }
435             case ir::PropertyKind::PROTO: {
436                 prop->Value()->Compile(pg);
437                 compiler::VReg proto = pg->AllocReg();
438                 pg->StoreAccumulator(this, proto);
439 
440                 pg->SetObjectWithProto(this, proto, objReg);
441                 break;
442             }
443             default: {
444                 UNREACHABLE();
445             }
446         }
447     }
448 
449     pg->LoadAccumulator(this, objReg);
450 }
451 
Compile(compiler::PandaGen * pg) const452 void ObjectExpression::Compile(compiler::PandaGen *pg) const
453 {
454     if (properties_.empty()) {
455         pg->CreateEmptyObject(this);
456         return;
457     }
458 
459     util::BitSet compiled(properties_.size());
460     CompileStaticProperties(pg, &compiled);
461 
462     compiler::RegScope rs(pg);
463     compiler::VReg objReg = pg->AllocReg();
464 
465     pg->StoreAccumulator(this, objReg);
466 
467     CompileRemainingProperties(pg, &compiled, objReg);
468 }
469 
CheckPattern(checker::Checker * checker) const470 checker::Type *ObjectExpression::CheckPattern(checker::Checker *checker) const
471 {
472     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
473     CHECK_NOT_NULL(desc);
474     bool isOptional = false;
475 
476     for (auto it = properties_.rbegin(); it != properties_.rend(); it++) {
477         if ((*it)->IsRestElement()) {
478             ASSERT((*it)->AsRestElement()->Argument()->IsIdentifier());
479             util::StringView indexInfoName("x");
480             auto *newIndexInfo =
481                 checker->Allocator()->New<checker::IndexInfo>(checker->GlobalAnyType(), indexInfoName, false);
482             desc->stringIndexInfo = newIndexInfo;
483             continue;
484         }
485 
486         ASSERT((*it)->IsProperty());
487         const ir::Property *prop = (*it)->AsProperty();
488 
489         if (prop->IsComputed()) {
490             // TODO(aszilagyi)
491             continue;
492         }
493 
494         binder::LocalVariable *foundVar = desc->FindProperty(prop->Key()->AsIdentifier()->Name());
495         checker::Type *patternParamType = checker->GlobalAnyType();
496         binder::Variable *bindingVar = nullptr;
497 
498         if (prop->IsShorthand()) {
499             switch (prop->Value()->Type()) {
500                 case ir::AstNodeType::IDENTIFIER: {
501                     const ir::Identifier *ident = prop->Value()->AsIdentifier();
502                     ASSERT(ident->Variable());
503                     bindingVar = ident->Variable();
504                     break;
505                 }
506                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
507                     const ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern();
508                     patternParamType = assignmentPattern->Right()->Check(checker);
509                     ASSERT(assignmentPattern->Left()->AsIdentifier()->Variable());
510                     bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
511                     isOptional = true;
512                     break;
513                 }
514                 default: {
515                     UNREACHABLE();
516                 }
517             }
518         } else {
519             switch (prop->Value()->Type()) {
520                 case ir::AstNodeType::IDENTIFIER: {
521                     bindingVar = prop->Value()->AsIdentifier()->Variable();
522                     break;
523                 }
524                 case ir::AstNodeType::ARRAY_PATTERN: {
525                     patternParamType = prop->Value()->AsArrayPattern()->CheckPattern(checker);
526                     break;
527                 }
528                 case ir::AstNodeType::OBJECT_PATTERN: {
529                     patternParamType = prop->Value()->AsObjectPattern()->CheckPattern(checker);
530                     break;
531                 }
532                 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
533                     const ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern();
534 
535                     if (assignmentPattern->Left()->IsIdentifier()) {
536                         bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
537                         patternParamType =
538                             checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
539                         isOptional = true;
540                         break;
541                     }
542 
543                     if (assignmentPattern->Left()->IsArrayPattern()) {
544                         auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
545                         auto destructuringContext =
546                             checker::ArrayDestructuringContext(checker, assignmentPattern->Left()->AsArrayPattern(),
547                                                                false, true, nullptr, assignmentPattern->Right());
548 
549                         if (foundVar) {
550                             destructuringContext.SetInferedType(
551                                 checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferedType()}));
552                         }
553 
554                         destructuringContext.Start();
555                         patternParamType = destructuringContext.InferedType();
556                         isOptional = true;
557                         break;
558                     }
559 
560                     ASSERT(assignmentPattern->Left()->IsObjectPattern());
561                     auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
562                     auto destructuringContext =
563                         checker::ObjectDestructuringContext(checker, assignmentPattern->Left()->AsObjectPattern(),
564                                                             false, true, nullptr, assignmentPattern->Right());
565 
566                     if (foundVar) {
567                         destructuringContext.SetInferedType(
568                             checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferedType()}));
569                     }
570 
571                     destructuringContext.Start();
572                     patternParamType = destructuringContext.InferedType();
573                     isOptional = true;
574                     break;
575                 }
576                 default: {
577                     UNREACHABLE();
578                 }
579             }
580         }
581 
582         if (bindingVar) {
583             bindingVar->SetTsType(patternParamType);
584         }
585 
586         if (foundVar) {
587             continue;
588         }
589 
590         binder::LocalVariable *patternVar = binder::Scope::CreateVar(
591             checker->Allocator(), prop->Key()->AsIdentifier()->Name(), binder::VariableFlags::PROPERTY, *it);
592         patternVar->SetTsType(patternParamType);
593 
594         if (isOptional) {
595             patternVar->AddFlag(binder::VariableFlags::OPTIONAL);
596         }
597 
598         desc->properties.insert(desc->properties.begin(), patternVar);
599     }
600 
601     checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
602     CHECK_NOT_NULL(returnType);
603     returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS);
604     return returnType;
605 }
606 
GetPropertyName(const ir::Expression * key)607 const util::StringView &GetPropertyName(const ir::Expression *key)
608 {
609     if (key->IsIdentifier()) {
610         return key->AsIdentifier()->Name();
611     }
612 
613     if (key->IsStringLiteral()) {
614         return key->AsStringLiteral()->Str();
615     }
616 
617     ASSERT(key->IsNumberLiteral());
618     return key->AsNumberLiteral()->Str();
619 }
620 
GetFlagsForProperty(const ir::Property * prop)621 binder::VariableFlags GetFlagsForProperty(const ir::Property *prop)
622 {
623     if (!prop->IsMethod()) {
624         return binder::VariableFlags::PROPERTY;
625     }
626 
627     binder::VariableFlags propFlags = binder::VariableFlags::METHOD;
628 
629     if (prop->IsAccessor() && prop->Kind() == PropertyKind::GET) {
630         propFlags |= binder::VariableFlags::READONLY;
631     }
632 
633     return propFlags;
634 }
635 
GetTypeForProperty(const ir::Property * prop,checker::Checker * checker)636 checker::Type *GetTypeForProperty(const ir::Property *prop, checker::Checker *checker)
637 {
638     if (prop->IsAccessor()) {
639         checker::Type *funcType = prop->Value()->Check(checker);
640 
641         if (prop->Kind() == PropertyKind::SET) {
642             return checker->GlobalAnyType();
643         }
644 
645         ASSERT(funcType->IsObjectType() && funcType->AsObjectType()->IsFunctionType());
646         return funcType->AsObjectType()->CallSignatures()[0]->ReturnType();
647     }
648 
649     if (prop->IsShorthand()) {
650         return prop->Key()->Check(checker);
651     }
652 
653     return prop->Value()->Check(checker);
654 }
655 
Check(checker::Checker * checker) const656 checker::Type *ObjectExpression::Check(checker::Checker *checker) const
657 {
658     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
659     CHECK_NOT_NULL(desc);
660     std::unordered_map<util::StringView, lexer::SourcePosition> allPropertiesMap;
661     bool inConstContext = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
662     ArenaVector<checker::Type *> computedNumberPropTypes(checker->Allocator()->Adapter());
663     ArenaVector<checker::Type *> computedStringPropTypes(checker->Allocator()->Adapter());
664     bool hasComputedNumberProperty = false;
665     bool hasComputedStringProperty = false;
666     bool seenSpread = false;
667 
668     for (const auto *it : properties_) {
669         if (it->IsProperty()) {
670             const ir::Property *prop = it->AsProperty();
671 
672             if (prop->IsComputed()) {
673                 checker::Type *computedNameType = checker->CheckComputedPropertyName(prop->Key());
674 
675                 if (computedNameType->IsNumberType()) {
676                     hasComputedNumberProperty = true;
677                     computedNumberPropTypes.push_back(prop->Value()->Check(checker));
678                     continue;
679                 }
680 
681                 if (computedNameType->IsStringType()) {
682                     hasComputedStringProperty = true;
683                     computedStringPropTypes.push_back(prop->Value()->Check(checker));
684                     continue;
685                 }
686             }
687 
688             checker::Type *propType = GetTypeForProperty(prop, checker);
689             binder::VariableFlags flags = GetFlagsForProperty(prop);
690             const util::StringView &propName = GetPropertyName(prop->Key());
691 
692             auto *memberVar = binder::Scope::CreateVar(checker->Allocator(), propName, flags, it);
693             CHECK_NOT_NULL(memberVar);
694             if (inConstContext) {
695                 memberVar->AddFlag(binder::VariableFlags::READONLY);
696             } else {
697                 propType = checker->GetBaseTypeOfLiteralType(propType);
698             }
699 
700             memberVar->SetTsType(propType);
701 
702             if (prop->Key()->IsNumberLiteral()) {
703                 memberVar->AddFlag(binder::VariableFlags::NUMERIC_NAME);
704             }
705 
706             binder::LocalVariable *foundMember = desc->FindProperty(propName);
707             allPropertiesMap.insert({propName, it->Start()});
708 
709             if (foundMember) {
710                 foundMember->SetTsType(propType);
711                 continue;
712             }
713 
714             desc->properties.push_back(memberVar);
715             continue;
716         }
717 
718         ASSERT(it->IsSpreadElement());
719 
720         checker::Type *spreadType = it->AsSpreadElement()->Argument()->Check(checker);
721         seenSpread = true;
722 
723         // TODO(aszilagyi): handle union of object types
724         if (!spreadType->IsObjectType()) {
725             checker->ThrowTypeError("Spread types may only be created from object types.", it->Start());
726         }
727 
728         for (auto *spreadProp : spreadType->AsObjectType()->Properties()) {
729             auto found = allPropertiesMap.find(spreadProp->Name());
730             if (found != allPropertiesMap.end()) {
731                 checker->ThrowTypeError(
732                     {found->first, " is specified more than once, so this usage will be overwritten."}, found->second);
733             }
734 
735             binder::LocalVariable *foundMember = desc->FindProperty(spreadProp->Name());
736 
737             if (foundMember) {
738                 foundMember->SetTsType(spreadProp->TsType());
739                 continue;
740             }
741 
742             desc->properties.push_back(spreadProp);
743         }
744     }
745 
746     if (!seenSpread && (hasComputedNumberProperty || hasComputedStringProperty)) {
747         for (auto *it : desc->properties) {
748             computedStringPropTypes.push_back(it->TsType());
749 
750             if (hasComputedNumberProperty && it->HasFlag(binder::VariableFlags::NUMERIC_NAME)) {
751                 computedNumberPropTypes.push_back(it->TsType());
752             }
753         }
754 
755         if (hasComputedNumberProperty) {
756             desc->numberIndexInfo = checker->Allocator()->New<checker::IndexInfo>(
757                 checker->CreateUnionType(std::move(computedNumberPropTypes)), "x", inConstContext);
758         }
759 
760         if (hasComputedStringProperty) {
761             desc->stringIndexInfo = checker->Allocator()->New<checker::IndexInfo>(
762                 checker->CreateUnionType(std::move(computedStringPropTypes)), "x", inConstContext);
763         }
764     }
765 
766     checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
767     CHECK_NOT_NULL(returnType);
768     returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS |
769                                               checker::ObjectFlags::CHECK_EXCESS_PROPS);
770     return returnType;
771 }
772 
UpdateSelf(const NodeUpdater & cb,binder::Binder * binder)773 void ObjectExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
774 {
775     for (auto iter = properties_.begin(); iter != properties_.end(); iter++) {
776         *iter = std::get<ir::AstNode *>(cb(*iter))->AsExpression();
777     }
778 
779     if (typeAnnotation_) {
780         typeAnnotation_ = std::get<ir::AstNode *>(cb(typeAnnotation_))->AsExpression();
781     }
782 }
783 
784 }  // namespace panda::es2panda::ir
785