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
40namespace ark::es2panda::checker {
41void 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
83void 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
105void 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
162void 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
174void 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
195void 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
230void 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}
245void 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
271varbinder::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
286varbinder::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
345Type *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
365IndexInfo *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
391Type *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
436ArenaVector<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
493void 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
504void 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
528bool 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