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