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 "ir/expressions/literals/bigIntLiteral.h"
17 #include "ir/expressions/literals/numberLiteral.h"
18 #include "ir/expressions/literals/stringLiteral.h"
19 #include "ir/expressions/functionExpression.h"
20 #include "ir/expressions/memberExpression.h"
21 #include "ir/expressions/identifier.h"
22 #include "ir/base/property.h"
23 #include "ir/base/scriptFunction.h"
24 #include "ir/base/spreadElement.h"
25 #include "ir/base/tsIndexSignature.h"
26 #include "ir/base/tsMethodSignature.h"
27 #include "ir/base/tsPropertySignature.h"
28 #include "ir/base/tsSignatureDeclaration.h"
29 #include "ir/ts/tsTypeLiteral.h"
30 #include "ir/ts/tsInterfaceDeclaration.h"
31 #include "ir/ts/tsInterfaceHeritage.h"
32 #include "ir/ts/tsInterfaceBody.h"
33 #include "util/helpers.h"
34 #include "varbinder/variable.h"
35 #include "varbinder/scope.h"
36
37 #include "checker/TSchecker.h"
38 #include "checker/types/ts/indexInfo.h"
39
40 namespace ark::es2panda::checker {
CheckIndexConstraints(Type *type)41 void TSChecker::CheckIndexConstraints(Type *type)
42 {
43 if (!type->IsObjectType()) {
44 return;
45 }
46
47 ObjectType *objType = type->AsObjectType();
48 ResolveStructuredTypeMembers(objType);
49
50 IndexInfo *numberInfo = objType->NumberIndexInfo();
51 IndexInfo *stringInfo = objType->StringIndexInfo();
52 const ArenaVector<varbinder::LocalVariable *> &properties = objType->Properties();
53
54 if (numberInfo != nullptr) {
55 for (auto *it : properties) {
56 if (it->HasFlag(varbinder::VariableFlags::NUMERIC_NAME)) {
57 Type *propType = GetTypeOfVariable(it);
58 IsTypeAssignableTo(propType, numberInfo->GetType(),
59 {"Property '", it->Name(), "' of type '", propType,
60 "' is not assignable to numeric index type '", numberInfo->GetType(), "'."},
61 it->Declaration()->Node()->Start());
62 }
63 }
64 }
65
66 if (stringInfo != nullptr) {
67 for (auto *it : properties) {
68 Type *propType = GetTypeOfVariable(it);
69 IsTypeAssignableTo(propType, stringInfo->GetType(),
70 {"Property '", it->Name(), "' of type '", propType,
71 "' is not assignable to string index type '", stringInfo->GetType(), "'."},
72 it->Declaration()->Node()->Start());
73 }
74
75 if (numberInfo != nullptr && !IsTypeAssignableTo(numberInfo->GetType(), stringInfo->GetType())) {
76 ThrowTypeError({"Number index info type ", numberInfo->GetType(),
77 " is not assignable to string index info type ", stringInfo->GetType(), "."},
78 numberInfo->Pos());
79 }
80 }
81 }
82
ResolveStructuredTypeMembers(Type *type)83 void TSChecker::ResolveStructuredTypeMembers(Type *type)
84 {
85 if (type->IsObjectType()) {
86 ObjectType *objType = type->AsObjectType();
87
88 if (objType->IsObjectLiteralType()) {
89 ResolveObjectTypeMembers(objType);
90 return;
91 }
92
93 if (objType->IsInterfaceType()) {
94 ResolveInterfaceOrClassTypeMembers(objType->AsInterfaceType());
95 return;
96 }
97 }
98
99 if (type->IsUnionType()) {
100 ResolveUnionTypeMembers(type->AsUnionType());
101 return;
102 }
103 }
104
ResolveUnionTypeMembers(UnionType *type)105 void TSChecker::ResolveUnionTypeMembers(UnionType *type)
106 {
107 if (type->MergedObjectType() != nullptr) {
108 return;
109 }
110
111 ObjectDescriptor *desc = Allocator()->New<ObjectDescriptor>(Allocator());
112 ArenaVector<Type *> stringInfoTypes(Allocator()->Adapter());
113 ArenaVector<Type *> numberInfoTypes(Allocator()->Adapter());
114 ArenaVector<Signature *> callSignatures(Allocator()->Adapter());
115 ArenaVector<Signature *> constructSignatures(Allocator()->Adapter());
116
117 for (auto *it : type->AsUnionType()->ConstituentTypes()) {
118 if (!it->IsObjectType()) {
119 continue;
120 }
121
122 ObjectType *objType = it->AsObjectType();
123 ResolveObjectTypeMembers(objType);
124
125 if (!objType->CallSignatures().empty()) {
126 for (auto *signature : objType->CallSignatures()) {
127 callSignatures.push_back(signature);
128 }
129 }
130
131 if (!objType->ConstructSignatures().empty()) {
132 for (auto *signature : objType->ConstructSignatures()) {
133 constructSignatures.push_back(signature);
134 }
135 }
136
137 if (objType->StringIndexInfo() != nullptr) {
138 stringInfoTypes.push_back(objType->StringIndexInfo()->GetType());
139 }
140
141 if (objType->NumberIndexInfo() != nullptr) {
142 numberInfoTypes.push_back(objType->NumberIndexInfo()->GetType());
143 }
144 }
145
146 desc->callSignatures = callSignatures;
147 desc->constructSignatures = constructSignatures;
148
149 if (!stringInfoTypes.empty()) {
150 desc->stringIndexInfo = Allocator()->New<IndexInfo>(CreateUnionType(std::move(stringInfoTypes)), "x", false);
151 }
152
153 if (!numberInfoTypes.empty()) {
154 desc->numberIndexInfo = Allocator()->New<IndexInfo>(CreateUnionType(std::move(numberInfoTypes)), "x", false);
155 }
156
157 ObjectType *mergedType = Allocator()->New<ObjectLiteralType>(desc);
158 mergedType->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS);
159 type->SetMergedObjectType(mergedType);
160 }
161
ResolveInterfaceOrClassTypeMembers(InterfaceType *type)162 void TSChecker::ResolveInterfaceOrClassTypeMembers(InterfaceType *type)
163 {
164 if (type->HasObjectFlag(ObjectFlags::RESOLVED_MEMBERS)) {
165 return;
166 }
167
168 ResolveDeclaredMembers(type);
169 GetBaseTypes(type);
170
171 type->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS);
172 }
173
ResolveObjectTypeMembers(ObjectType *type)174 void TSChecker::ResolveObjectTypeMembers(ObjectType *type)
175 {
176 if (!type->IsObjectLiteralType() || type->HasObjectFlag(ObjectFlags::RESOLVED_MEMBERS)) {
177 return;
178 }
179
180 ASSERT(type->Variable() && type->Variable()->Declaration()->Node()->IsTSTypeLiteral());
181 auto *typeLiteral = type->Variable()->Declaration()->Node()->AsTSTypeLiteral();
182 ArenaVector<ir::TSSignatureDeclaration *> signatureDeclarations(Allocator()->Adapter());
183 ArenaVector<ir::TSIndexSignature *> indexDeclarations(Allocator()->Adapter());
184
185 for (auto *it : typeLiteral->Members()) {
186 ResolvePropertiesOfObjectType(type, it, signatureDeclarations, indexDeclarations, false);
187 }
188
189 type->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS);
190
191 ResolveSignaturesOfObjectType(type, signatureDeclarations);
192 ResolveIndexInfosOfObjectType(type, indexDeclarations);
193 }
194
ResolvePropertiesOfObjectType(ObjectType *type, ir::AstNode *member, ArenaVector<ir::TSSignatureDeclaration *> &signatureDeclarations, ArenaVector<ir::TSIndexSignature *> &indexDeclarations, bool isInterface)195 void TSChecker::ResolvePropertiesOfObjectType(ObjectType *type, ir::AstNode *member,
196 ArenaVector<ir::TSSignatureDeclaration *> &signatureDeclarations,
197 ArenaVector<ir::TSIndexSignature *> &indexDeclarations, bool isInterface)
198 {
199 if (member->IsTSPropertySignature()) {
200 varbinder::Variable *prop = member->AsTSPropertySignature()->Variable();
201
202 if (!isInterface ||
203 ValidateInterfaceMemberRedeclaration(type, prop, member->AsTSPropertySignature()->Key()->Start())) {
204 type->AddProperty(prop->AsLocalVariable());
205 }
206
207 return;
208 }
209
210 if (member->IsTSMethodSignature()) {
211 varbinder::Variable *method = member->AsTSMethodSignature()->Variable();
212
213 if (!isInterface ||
214 ValidateInterfaceMemberRedeclaration(type, method, member->AsTSMethodSignature()->Key()->Start())) {
215 type->AddProperty(method->AsLocalVariable());
216 }
217
218 return;
219 }
220
221 if (member->IsTSSignatureDeclaration()) {
222 signatureDeclarations.push_back(member->AsTSSignatureDeclaration());
223 return;
224 }
225
226 ASSERT(member->IsTSIndexSignature());
227 indexDeclarations.push_back(member->AsTSIndexSignature());
228 }
229
ResolveSignaturesOfObjectType(ObjectType *type, ArenaVector<ir::TSSignatureDeclaration *> &signatureDeclarations)230 void TSChecker::ResolveSignaturesOfObjectType(ObjectType *type,
231 ArenaVector<ir::TSSignatureDeclaration *> &signatureDeclarations)
232 {
233 for (auto *it : signatureDeclarations) {
234 Type *placeholderObj = it->Check(this);
235
236 if (it->AsTSSignatureDeclaration()->Kind() ==
237 ir::TSSignatureDeclaration::TSSignatureDeclarationKind::CALL_SIGNATURE) {
238 type->AddCallSignature(placeholderObj->AsObjectType()->CallSignatures()[0]);
239 continue;
240 }
241
242 type->AddConstructSignature(placeholderObj->AsObjectType()->ConstructSignatures()[0]);
243 }
244 }
ResolveIndexInfosOfObjectType(ObjectType *type, ArenaVector<ir::TSIndexSignature *> &indexDeclarations)245 void TSChecker::ResolveIndexInfosOfObjectType(ObjectType *type, ArenaVector<ir::TSIndexSignature *> &indexDeclarations)
246 {
247 for (auto *it : indexDeclarations) {
248 Type *placeholderObj = it->Check(this);
249
250 if (it->AsTSIndexSignature()->Kind() == ir::TSIndexSignature::TSIndexSignatureKind::NUMBER) {
251 IndexInfo *numberInfo = placeholderObj->AsObjectType()->NumberIndexInfo();
252
253 if (type->NumberIndexInfo() != nullptr) {
254 ThrowTypeError("Duplicated index signature for type 'number'", it->Start());
255 }
256
257 type->Desc()->numberIndexInfo = numberInfo;
258 continue;
259 }
260
261 IndexInfo *stringInfo = placeholderObj->AsObjectType()->StringIndexInfo();
262
263 if (type->StringIndexInfo() != nullptr) {
264 ThrowTypeError("Duplicated index signature for type 'string'", it->Start());
265 }
266
267 type->Desc()->stringIndexInfo = stringInfo;
268 }
269 }
270
GetPropertyOfType(Type *type, const util::StringView &name, bool getPartial, varbinder::VariableFlags propagateFlags)271 varbinder::Variable *TSChecker::GetPropertyOfType(Type *type, const util::StringView &name, bool getPartial,
272 varbinder::VariableFlags propagateFlags)
273 {
274 if (type->IsObjectType()) {
275 ResolveObjectTypeMembers(type->AsObjectType());
276 return type->AsObjectType()->GetProperty(name, true);
277 }
278
279 if (type->IsUnionType()) {
280 return GetPropertyOfUnionType(type->AsUnionType(), name, getPartial, propagateFlags);
281 }
282
283 return nullptr;
284 }
285
GetPropertyOfUnionType(UnionType *type, const util::StringView &name, bool getPartial, varbinder::VariableFlags propagateFlags)286 varbinder::Variable *TSChecker::GetPropertyOfUnionType(UnionType *type, const util::StringView &name, bool getPartial,
287 varbinder::VariableFlags propagateFlags)
288 {
289 auto found = type->CachedSyntheticProperties().find(name);
290 if (found != type->CachedSyntheticProperties().end()) {
291 return found->second;
292 }
293
294 varbinder::VariableFlags flags = varbinder::VariableFlags::PROPERTY;
295 ArenaVector<Type *> collectedTypes(Allocator()->Adapter());
296
297 for (auto *it : type->ConstituentTypes()) {
298 varbinder::Variable *prop = GetPropertyOfType(it, name);
299
300 if (prop == nullptr) {
301 if (it->IsArrayType()) {
302 collectedTypes.push_back(it->AsArrayType()->ElementType());
303 continue;
304 }
305
306 if (!it->IsObjectType() && getPartial) {
307 continue;
308 }
309 if (!it->IsObjectType() && !getPartial) {
310 return nullptr;
311 }
312
313 ObjectType *objType = it->AsObjectType();
314
315 if (objType->StringIndexInfo() == nullptr && getPartial) {
316 continue;
317 }
318 if (objType->StringIndexInfo() == nullptr && !getPartial) {
319 return nullptr;
320 }
321
322 collectedTypes.push_back(objType->StringIndexInfo()->GetType());
323 continue;
324 }
325
326 prop->AddFlag(propagateFlags);
327
328 if (prop->HasFlag(varbinder::VariableFlags::OPTIONAL)) {
329 flags |= varbinder::VariableFlags::OPTIONAL;
330 }
331
332 collectedTypes.push_back(GetTypeOfVariable(prop));
333 }
334
335 if (collectedTypes.empty()) {
336 return nullptr;
337 }
338
339 varbinder::Variable *syntheticProp = varbinder::Scope::CreateVar(Allocator(), name, flags, nullptr);
340 syntheticProp->SetTsType(CreateUnionType(std::move(collectedTypes)));
341 type->CachedSyntheticProperties().insert({name, syntheticProp});
342 return syntheticProp;
343 }
344
CheckComputedPropertyName(ir::Expression *key)345 Type *TSChecker::CheckComputedPropertyName(ir::Expression *key)
346 {
347 if (key->TsType() != nullptr) {
348 return key->TsType();
349 }
350
351 Type *keyType = key->Check(this);
352
353 if (!keyType->HasTypeFlag(TypeFlag::STRING_LIKE | TypeFlag::NUMBER_LIKE)) {
354 ThrowTypeError(
355 "A computed property name in a type literal must refer to an expression whose type is a literal "
356 "type "
357 "or a 'unique symbol' type",
358 key->Start());
359 }
360
361 key->SetTsType(keyType);
362 return keyType;
363 }
364
GetApplicableIndexInfo(Type *type, Type *indexType)365 IndexInfo *TSChecker::GetApplicableIndexInfo(Type *type, Type *indexType)
366 {
367 ResolveStructuredTypeMembers(type);
368 bool getNumberInfo = indexType->HasTypeFlag(TypeFlag::NUMBER_LIKE);
369
370 if (type->IsObjectType()) {
371 if (getNumberInfo) {
372 return type->AsObjectType()->NumberIndexInfo();
373 }
374
375 return type->AsObjectType()->StringIndexInfo();
376 }
377
378 if (type->IsUnionType()) {
379 ASSERT(type->AsUnionType()->MergedObjectType());
380
381 if (getNumberInfo) {
382 return type->AsUnionType()->MergedObjectType()->NumberIndexInfo();
383 }
384
385 return type->AsUnionType()->MergedObjectType()->StringIndexInfo();
386 }
387
388 return nullptr;
389 }
390
GetPropertyTypeForIndexType(Type *type, Type *indexType)391 Type *TSChecker::GetPropertyTypeForIndexType(Type *type, Type *indexType)
392 {
393 if (type->IsArrayType()) {
394 return type->AsArrayType()->ElementType();
395 }
396
397 if (indexType->HasTypeFlag(TypeFlag::STRING_LITERAL | TypeFlag::NUMBER_LITERAL)) {
398 varbinder::Variable *prop = nullptr;
399
400 if (indexType->IsStringLiteralType()) {
401 prop = GetPropertyOfType(type, indexType->AsStringLiteralType()->Value());
402 } else {
403 util::StringView propName =
404 util::Helpers::ToStringView(Allocator(), indexType->AsNumberLiteralType()->Value());
405 prop = GetPropertyOfType(type, propName);
406 }
407
408 if (prop != nullptr) {
409 Type *propType = GetTypeOfVariable(prop);
410
411 if (prop->HasFlag(varbinder::VariableFlags::READONLY)) {
412 propType->AddTypeFlag(TypeFlag::READONLY);
413 }
414
415 return propType;
416 }
417 }
418
419 if (indexType->HasTypeFlag(TypeFlag::STRING_LIKE | TypeFlag::NUMBER_LIKE)) {
420 IndexInfo *indexInfo = GetApplicableIndexInfo(type, indexType);
421
422 if (indexInfo != nullptr) {
423 Type *indexInfoType = indexInfo->GetType();
424
425 if (indexInfo->Readonly()) {
426 indexInfoType->AddTypeFlag(TypeFlag::READONLY);
427 }
428
429 return indexInfoType;
430 }
431 }
432
433 return nullptr;
434 }
435
GetBaseTypes(InterfaceType *type)436 ArenaVector<ObjectType *> TSChecker::GetBaseTypes(InterfaceType *type)
437 {
438 if (type->HasObjectFlag(ObjectFlags::RESOLVED_BASE_TYPES)) {
439 return type->Bases();
440 }
441
442 ASSERT(type->Variable() && type->Variable()->Declaration()->IsInterfaceDecl());
443 varbinder::InterfaceDecl *decl = type->Variable()->Declaration()->AsInterfaceDecl();
444
445 TypeStackElement tse(this, type, {"Type ", type->Name(), " recursively references itself as a base type."},
446 decl->Node()->AsTSInterfaceDeclaration()->Id()->Start());
447 if (tse.HasTypeError()) {
448 type->Bases().clear();
449 return type->Bases();
450 }
451
452 for (const auto *declaration : decl->Decls()) {
453 if (declaration->Extends().empty()) {
454 continue;
455 }
456
457 for (auto *extends : declaration->Extends()) {
458 Type *baseType = extends->Expr()->GetType(this);
459
460 if (!baseType->HasTypeFlag(TypeFlag::OBJECT | TypeFlag::NON_PRIMITIVE | TypeFlag::ANY)) {
461 ThrowTypeError(
462 "An interface can only extend an object type or intersection of object types with statically "
463 "known "
464 "members",
465 extends->Start());
466 }
467
468 if (!baseType->IsObjectType()) {
469 continue;
470 }
471
472 ObjectType *baseObj = baseType->AsObjectType();
473
474 if (baseType == type) {
475 ThrowTypeError({"Type ", type->Name(), " recursively references itself as a base type."},
476 decl->Node()->AsTSInterfaceDeclaration()->Id()->Start());
477 }
478
479 type->AddBase(baseObj);
480
481 if (!baseObj->IsInterfaceType()) {
482 continue;
483 }
484
485 CheckExtendsBases(baseObj, type, decl);
486 }
487 }
488
489 type->AddObjectFlag(ObjectFlags::RESOLVED_BASE_TYPES);
490 return type->Bases();
491 }
492
CheckExtendsBases(ObjectType *&baseObj, InterfaceType *&type, varbinder::InterfaceDecl *&decl)493 void TSChecker::CheckExtendsBases(ObjectType *&baseObj, InterfaceType *&type, varbinder::InterfaceDecl *&decl)
494 {
495 ArenaVector<ObjectType *> extendsBases = GetBaseTypes(baseObj->AsInterfaceType());
496 for (auto *extendBase : extendsBases) {
497 if (extendBase == type) {
498 ThrowTypeError({"Type ", type->Name(), " recursively references itself as a base type."},
499 decl->Node()->AsTSInterfaceDeclaration()->Id()->Start());
500 }
501 }
502 }
503
ResolveDeclaredMembers(InterfaceType *type)504 void TSChecker::ResolveDeclaredMembers(InterfaceType *type)
505 {
506 if (type->HasObjectFlag(ObjectFlags::RESOLVED_DECLARED_MEMBERS)) {
507 return;
508 }
509
510 ASSERT(type->Variable() && type->Variable()->Declaration()->IsInterfaceDecl());
511 varbinder::InterfaceDecl *decl = type->Variable()->Declaration()->AsInterfaceDecl();
512
513 ArenaVector<ir::TSSignatureDeclaration *> signatureDeclarations(Allocator()->Adapter());
514 ArenaVector<ir::TSIndexSignature *> indexDeclarations(Allocator()->Adapter());
515
516 for (const auto *declaration : decl->Decls()) {
517 for (auto *member : declaration->Body()->Body()) {
518 ResolvePropertiesOfObjectType(type, member, signatureDeclarations, indexDeclarations, true);
519 }
520
521 type->AddObjectFlag(ObjectFlags::RESOLVED_DECLARED_MEMBERS);
522
523 ResolveSignaturesOfObjectType(type, signatureDeclarations);
524 ResolveIndexInfosOfObjectType(type, indexDeclarations);
525 }
526 }
527
ValidateInterfaceMemberRedeclaration(ObjectType *type, varbinder::Variable *prop, const lexer::SourcePosition &locInfo)528 bool TSChecker::ValidateInterfaceMemberRedeclaration(ObjectType *type, varbinder::Variable *prop,
529 const lexer::SourcePosition &locInfo)
530 {
531 if (prop->HasFlag(varbinder::VariableFlags::COMPUTED)) {
532 return true;
533 }
534
535 varbinder::Variable *found = type->GetProperty(prop->Name(), false);
536
537 if (found == nullptr) {
538 return true;
539 }
540
541 Type *targetType = GetTypeOfVariable(prop);
542 Type *sourceType = GetTypeOfVariable(found);
543 IsTypeIdenticalTo(targetType, sourceType,
544 {"Subsequent property declarations must have the same type. Property ", prop->Name(),
545 " must be of type ", sourceType, ", but here has type ", targetType, "."},
546 locInfo);
547 return false;
548 }
549 } // namespace ark::es2panda::checker
550