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