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