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