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