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