1/** 2 * Copyright (c) 2021-2024 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 "ir/base/decorator.h" 19#include "util/helpers.h" 20#include "compiler/base/literals.h" 21#include "compiler/core/pandagen.h" 22#include "compiler/core/ETSGen.h" 23#include "checker/TSchecker.h" 24#include "checker/ETSchecker.h" 25#include "checker/ets/typeRelationContext.h" 26#include "checker/ts/destructuringContext.h" 27#include "ir/astDump.h" 28#include "ir/srcDump.h" 29#include "ir/typeNode.h" 30#include "ir/base/property.h" 31#include "ir/base/scriptFunction.h" 32#include "ir/base/spreadElement.h" 33#include "ir/expressions/arrayExpression.h" 34#include "ir/expressions/assignmentExpression.h" 35#include "ir/expressions/identifier.h" 36#include "ir/statements/variableDeclarator.h" 37#include "ir/validationInfo.h" 38 39namespace ark::es2panda::ir { 40ObjectExpression::ObjectExpression([[maybe_unused]] Tag const tag, ObjectExpression const &other, 41 ArenaAllocator *const allocator) 42 : AnnotatedExpression(static_cast<AnnotatedExpression const &>(other), allocator), 43 decorators_(allocator->Adapter()), 44 properties_(allocator->Adapter()) 45{ 46 preferredType_ = other.preferredType_; 47 isDeclaration_ = other.isDeclaration_; 48 trailingComma_ = other.trailingComma_; 49 optional_ = other.optional_; 50 51 for (auto *property : other.properties_) { 52 properties_.emplace_back(property->Clone(allocator, this)->AsExpression()); 53 } 54 55 for (auto *decorator : other.decorators_) { 56 decorators_.emplace_back(decorator->Clone(allocator, this)); 57 } 58} 59 60ObjectExpression *ObjectExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent) 61{ 62 if (auto *const clone = allocator->New<ObjectExpression>(Tag {}, *this, allocator); clone != nullptr) { 63 if (parent != nullptr) { 64 clone->SetParent(parent); 65 } 66 return clone; 67 } 68 throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR); 69} 70 71static std::pair<ValidationInfo, bool> ValidateProperty(Property *prop, bool &foundProto) 72{ 73 ValidationInfo info = prop->ValidateExpression(); 74 if (prop->Kind() == PropertyKind::PROTO) { 75 if (foundProto) { 76 return {{"Duplicate __proto__ fields are not allowed in object literals", prop->Key()->Start()}, true}; 77 } 78 79 foundProto = true; 80 } 81 82 return {info, false}; 83} 84 85ValidationInfo ObjectExpression::ValidateExpression() 86{ 87 if (optional_) { 88 return {"Unexpected token '?'.", Start()}; 89 } 90 91 if (TypeAnnotation() != nullptr) { 92 return {"Unexpected token.", TypeAnnotation()->Start()}; 93 } 94 95 ValidationInfo info; 96 bool foundProto = false; 97 98 for (auto *it : properties_) { 99 switch (it->Type()) { 100 case AstNodeType::OBJECT_EXPRESSION: 101 case AstNodeType::ARRAY_EXPRESSION: { 102 return {"Unexpected token.", it->Start()}; 103 } 104 case AstNodeType::SPREAD_ELEMENT: { 105 info = it->AsSpreadElement()->ValidateExpression(); 106 break; 107 } 108 case AstNodeType::PROPERTY: { 109 auto *prop = it->AsProperty(); 110 bool ret = false; 111 std::tie(info, ret) = ValidateProperty(prop, foundProto); 112 if (ret) { 113 return info; 114 } 115 break; 116 } 117 default: { 118 break; 119 } 120 } 121 122 if (info.Fail()) { 123 break; 124 } 125 } 126 127 return info; 128} 129 130bool ObjectExpression::ConvertibleToObjectPattern() 131{ 132 // NOTE: rsipka. throw more precise messages in case of false results 133 bool restFound = false; 134 bool convResult = true; 135 136 for (auto *it : properties_) { 137 switch (it->Type()) { 138 case AstNodeType::ARRAY_EXPRESSION: { 139 convResult = it->AsArrayExpression()->ConvertibleToArrayPattern(); 140 break; 141 } 142 case AstNodeType::SPREAD_ELEMENT: { 143 if (!restFound && it == properties_.back() && !trailingComma_) { 144 convResult = it->AsSpreadElement()->ConvertibleToRest(isDeclaration_, false); 145 } else { 146 convResult = false; 147 } 148 149 restFound = true; 150 break; 151 } 152 case AstNodeType::OBJECT_EXPRESSION: { 153 convResult = it->AsObjectExpression()->ConvertibleToObjectPattern(); 154 break; 155 } 156 case AstNodeType::ASSIGNMENT_EXPRESSION: { 157 convResult = it->AsAssignmentExpression()->ConvertibleToAssignmentPattern(); 158 break; 159 } 160 case AstNodeType::META_PROPERTY_EXPRESSION: 161 case AstNodeType::CHAIN_EXPRESSION: 162 case AstNodeType::SEQUENCE_EXPRESSION: { 163 convResult = false; 164 break; 165 } 166 case AstNodeType::PROPERTY: { 167 convResult = it->AsProperty()->ConvertibleToPatternProperty(); 168 break; 169 } 170 default: { 171 break; 172 } 173 } 174 175 if (!convResult) { 176 break; 177 } 178 } 179 180 SetType(AstNodeType::OBJECT_PATTERN); 181 return convResult; 182} 183 184void ObjectExpression::SetDeclaration() 185{ 186 isDeclaration_ = true; 187} 188 189void ObjectExpression::SetOptional(bool optional) 190{ 191 optional_ = optional; 192} 193 194void ObjectExpression::TransformChildren(const NodeTransformer &cb, std::string_view transformationName) 195{ 196 for (auto *&it : decorators_) { 197 if (auto *transformedNode = cb(it); it != transformedNode) { 198 it->SetTransformedNode(transformationName, transformedNode); 199 it = transformedNode->AsDecorator(); 200 } 201 } 202 203 for (auto *&it : properties_) { 204 if (auto *transformedNode = cb(it); it != transformedNode) { 205 it->SetTransformedNode(transformationName, transformedNode); 206 it = transformedNode->AsExpression(); 207 } 208 } 209 210 if (auto *typeAnnotation = TypeAnnotation(); typeAnnotation != nullptr) { 211 if (auto *transformedNode = cb(typeAnnotation); typeAnnotation != transformedNode) { 212 typeAnnotation->SetTransformedNode(transformationName, transformedNode); 213 SetTsTypeAnnotation(static_cast<TypeNode *>(transformedNode)); 214 } 215 } 216} 217 218void ObjectExpression::Iterate(const NodeTraverser &cb) const 219{ 220 for (auto *it : decorators_) { 221 cb(it); 222 } 223 224 for (auto *it : properties_) { 225 cb(it); 226 } 227 228 if (TypeAnnotation() != nullptr) { 229 cb(TypeAnnotation()); 230 } 231} 232 233void ObjectExpression::Dump(ir::AstDumper *dumper) const 234{ 235 dumper->Add({{"type", (type_ == AstNodeType::OBJECT_EXPRESSION) ? "ObjectExpression" : "ObjectPattern"}, 236 {"decorators", AstDumper::Optional(decorators_)}, 237 {"properties", properties_}, 238 {"typeAnnotation", AstDumper::Optional(TypeAnnotation())}, 239 {"optional", AstDumper::Optional(optional_)}}); 240} 241 242void ObjectExpression::Dump(ir::SrcDumper *dumper) const 243{ 244 dumper->Add("{"); 245 if (!properties_.empty()) { 246 dumper->IncrIndent(); 247 dumper->Endl(); 248 for (auto property : properties_) { 249 property->Dump(dumper); 250 dumper->Add(","); 251 if (property == properties_.back()) { 252 dumper->DecrIndent(); 253 } 254 dumper->Endl(); 255 } 256 } 257 dumper->Add("}"); 258} 259 260void ObjectExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const 261{ 262 pg->GetAstCompiler()->Compile(this); 263} 264 265checker::Type *ObjectExpression::CheckPattern(checker::TSChecker *checker) 266{ 267 checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator()); 268 269 bool isOptional = false; 270 271 for (auto it = properties_.rbegin(); it != properties_.rend(); it++) { 272 if ((*it)->IsRestElement()) { 273 ASSERT((*it)->AsRestElement()->Argument()->IsIdentifier()); 274 util::StringView indexInfoName("x"); 275 auto *newIndexInfo = 276 checker->Allocator()->New<checker::IndexInfo>(checker->GlobalAnyType(), indexInfoName, false); 277 desc->stringIndexInfo = newIndexInfo; 278 continue; 279 } 280 281 ASSERT((*it)->IsProperty()); 282 auto *prop = (*it)->AsProperty(); 283 284 if (prop->IsComputed()) { 285 continue; 286 } 287 288 varbinder::LocalVariable *foundVar = desc->FindProperty(prop->Key()->AsIdentifier()->Name()); 289 checker::Type *patternParamType = checker->GlobalAnyType(); 290 varbinder::Variable *bindingVar = nullptr; 291 292 if (prop->IsShorthand()) { 293 switch (prop->Value()->Type()) { 294 case ir::AstNodeType::IDENTIFIER: { 295 const ir::Identifier *ident = prop->Value()->AsIdentifier(); 296 ASSERT(ident->Variable()); 297 bindingVar = ident->Variable(); 298 break; 299 } 300 case ir::AstNodeType::ASSIGNMENT_PATTERN: { 301 auto *assignmentPattern = prop->Value()->AsAssignmentPattern(); 302 patternParamType = assignmentPattern->Right()->Check(checker); 303 ASSERT(assignmentPattern->Left()->AsIdentifier()->Variable()); 304 bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable(); 305 isOptional = true; 306 break; 307 } 308 default: { 309 UNREACHABLE(); 310 } 311 } 312 } else { 313 switch (prop->Value()->Type()) { 314 case ir::AstNodeType::IDENTIFIER: { 315 bindingVar = prop->Value()->AsIdentifier()->Variable(); 316 break; 317 } 318 case ir::AstNodeType::ARRAY_PATTERN: { 319 patternParamType = prop->Value()->AsArrayPattern()->CheckPattern(checker); 320 break; 321 } 322 case ir::AstNodeType::OBJECT_PATTERN: { 323 patternParamType = prop->Value()->AsObjectPattern()->CheckPattern(checker); 324 break; 325 } 326 case ir::AstNodeType::ASSIGNMENT_PATTERN: { 327 isOptional = CheckAssignmentPattern(prop, bindingVar, patternParamType, checker, foundVar); 328 break; 329 } 330 default: { 331 UNREACHABLE(); 332 } 333 } 334 } 335 336 if (bindingVar != nullptr) { 337 bindingVar->SetTsType(patternParamType); 338 } 339 340 if (foundVar != nullptr) { 341 continue; 342 } 343 344 varbinder::LocalVariable *patternVar = varbinder::Scope::CreateVar( 345 checker->Allocator(), prop->Key()->AsIdentifier()->Name(), varbinder::VariableFlags::PROPERTY, *it); 346 patternVar->SetTsType(patternParamType); 347 348 if (isOptional) { 349 patternVar->AddFlag(varbinder::VariableFlags::OPTIONAL); 350 } 351 352 desc->properties.insert(desc->properties.begin(), patternVar); 353 } 354 355 checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc); 356 returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS); 357 return returnType; 358} 359 360bool ObjectExpression::CheckAssignmentPattern(Property *prop, varbinder::Variable *&bindingVar, 361 checker::Type *&patternParamType, checker::TSChecker *checker, 362 varbinder::LocalVariable *foundVar) 363{ 364 auto *assignmentPattern = prop->Value()->AsAssignmentPattern(); 365 366 if (assignmentPattern->Left()->IsIdentifier()) { 367 bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable(); 368 patternParamType = checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker)); 369 return true; 370 } 371 372 if (assignmentPattern->Left()->IsArrayPattern()) { 373 auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); 374 auto destructuringContext = checker::ArrayDestructuringContext( 375 {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()}); 376 377 if (foundVar != nullptr) { 378 destructuringContext.SetInferredType( 379 checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferredType()})); 380 } 381 382 destructuringContext.Start(); 383 patternParamType = destructuringContext.InferredType(); 384 return true; 385 } 386 387 ASSERT(assignmentPattern->Left()->IsObjectPattern()); 388 auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); 389 auto destructuringContext = checker::ObjectDestructuringContext( 390 {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()}); 391 392 if (foundVar != nullptr) { 393 destructuringContext.SetInferredType( 394 checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferredType()})); 395 } 396 397 destructuringContext.Start(); 398 patternParamType = destructuringContext.InferredType(); 399 return true; 400} 401 402checker::Type *ObjectExpression::Check(checker::TSChecker *checker) 403{ 404 return checker->GetAnalyzer()->Check(this); 405} 406 407void ObjectExpression::Compile(compiler::ETSGen *etsg) const 408{ 409 etsg->GetAstCompiler()->Compile(this); 410} 411 412checker::Type *ObjectExpression::Check(checker::ETSChecker *checker) 413{ 414 return checker->GetAnalyzer()->Check(this); 415} 416} // namespace ark::es2panda::ir 417