• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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