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
43namespace panda::es2panda::ir {
44
45static 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
68static 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
84ValidationInfo 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
126bool 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
179void ObjectExpression::SetDeclaration()
180{
181    isDeclaration_ = true;
182}
183
184void ObjectExpression::SetOptional(bool optional)
185{
186    optional_ = optional;
187}
188
189void ObjectExpression::SetTsTypeAnnotation(Expression *typeAnnotation)
190{
191    typeAnnotation_ = typeAnnotation;
192}
193
194void 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
205void 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
213void 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
228void 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
240static 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
273void 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
334void 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
360void 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
397void 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
445void 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
463checker::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
600const 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
614binder::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
629checker::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
649checker::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
766void 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