• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "checker/ETSchecker.h"
17 
18 #include "ir/expressions/identifier.h"
19 #include "ir/ets/etsNullishTypes.h"
20 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
21 #include "ir/ets/etsUnionType.h"
22 #include "ir/expressions/literals/undefinedLiteral.h"
23 #include "varbinder/ETSBinder.h"
24 #include "checker/types/ets/etsPartialTypeParameter.h"
25 
26 namespace ark::es2panda::checker {
27 
GetUtilityTypeTypeParamNode(const ir::TSTypeParameterInstantiation * const typeParams,const std::string_view & utilityTypeName)28 std::optional<ir::TypeNode *> ETSChecker::GetUtilityTypeTypeParamNode(
29     const ir::TSTypeParameterInstantiation *const typeParams, const std::string_view &utilityTypeName)
30 {
31     if (typeParams->Params().size() != 1) {
32         LogError(diagnostic::UTIL_TYPE_INVALID_TYPE_PARAM_COUNT, {utilityTypeName}, typeParams->Start());
33         return std::nullopt;
34     }
35     return typeParams->Params().front();
36 }
37 
HandleUtilityTypeParameterNode(const ir::TSTypeParameterInstantiation * const typeParams,const ir::Identifier * const ident)38 Type *ETSChecker::HandleUtilityTypeParameterNode(const ir::TSTypeParameterInstantiation *const typeParams,
39                                                  const ir::Identifier *const ident)
40 {
41     if (typeParams == nullptr) {
42         LogError(diagnostic::USING_RESERVED_NAME_AS_VARIABLE_OR_TYPE_NAME, {ident->Name().Utf8()}, ident->Start());
43         return GlobalTypeError();
44     }
45     const std::string_view &utilityType = ident->Name().Utf8();
46     std::optional<ir::TypeNode *> possiblyTypeParam = GetUtilityTypeTypeParamNode(typeParams, utilityType);
47     if (!possiblyTypeParam.has_value()) {
48         return GlobalTypeError();
49     }
50 
51     Type *baseType = possiblyTypeParam.value()->Check(this);
52 
53     if (baseType->IsTypeError()) {
54         return baseType;
55     }
56 
57     if (!baseType->IsETSReferenceType()) {
58         LogError(diagnostic::UTIL_TYPE_OF_NONREFERENCE, {}, typeParams->Start());
59         return GlobalTypeError();
60     }
61 
62     if (baseType->IsETSAnyType()) {
63         return baseType;
64     }
65 
66     if (utilityType == compiler::Signatures::PARTIAL_TYPE_NAME) {
67         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
68         return CreatePartialType(baseType);
69     }
70 
71     if (utilityType == compiler::Signatures::READONLY_TYPE_NAME) {
72         return GetReadonlyType(baseType);
73     }
74 
75     if (utilityType == compiler::Signatures::REQUIRED_TYPE_NAME) {
76         return HandleRequiredType(baseType);
77     }
78 
79     LogError(diagnostic::UTILITY_TYPE_UNIMPLEMENTED, {}, typeParams->Start());
80     return baseType;
81 }
82 
83 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84 // Partial utility type
85 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86 
GetPartialClassName(ETSChecker * checker,ir::AstNode * typeNode)87 static std::pair<util::StringView, util::StringView> GetPartialClassName(ETSChecker *checker, ir::AstNode *typeNode)
88 {
89     // Partial class name for class 'T' will be 'T$partial'
90     auto const addSuffix = [checker](util::StringView name) {
91         return util::UString(name.Mutf8() + PARTIAL_CLASS_SUFFIX, checker->ProgramAllocator()).View();
92     };
93 
94     auto declIdent = typeNode->IsClassDefinition() ? typeNode->AsClassDefinition()->Ident()
95                                                    : typeNode->AsTSInterfaceDeclaration()->Id();
96     auto internalName = typeNode->IsClassDefinition() ? typeNode->AsClassDefinition()->InternalName()
97                                                       : typeNode->AsTSInterfaceDeclaration()->InternalName();
98     return {addSuffix(declIdent->Name()), addSuffix(internalName)};
99 }
100 
GetPartialClassProgram(ETSChecker * checker,ir::AstNode * typeNode)101 static std::pair<parser::Program *, varbinder::RecordTable *> GetPartialClassProgram(ETSChecker *checker,
102                                                                                      ir::AstNode *typeNode)
103 {
104     auto classDefProgram = typeNode->GetTopStatement()->AsETSModule()->Program();
105     if (classDefProgram == checker->VarBinder()->AsETSBinder()->GetGlobalRecordTable()->Program()) {
106         return {classDefProgram, checker->VarBinder()->AsETSBinder()->GetGlobalRecordTable()};
107     }
108     return {classDefProgram, checker->VarBinder()->AsETSBinder()->GetExternalRecordTable().at(classDefProgram)};
109 }
110 
111 template <typename T>
CloneNodeIfNotNullptr(T * node,ArenaAllocator * allocator)112 static T *CloneNodeIfNotNullptr(T *node, ArenaAllocator *allocator)
113 {
114     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
115     return node != nullptr ? node->Clone(allocator, nullptr) : nullptr;
116 }
117 
CreatePartialType(Type * const typeToBePartial)118 Type *ETSChecker::CreatePartialType(Type *const typeToBePartial)
119 {
120     ES2PANDA_ASSERT(typeToBePartial->IsETSReferenceType());
121     if (typeToBePartial->IsTypeError() || typeToBePartial->IsETSNeverType() || typeToBePartial->IsETSAnyType()) {
122         return typeToBePartial;
123     }
124 
125     if (typeToBePartial->IsETSTypeParameter()) {
126         return CreatePartialTypeParameter(typeToBePartial->AsETSTypeParameter());
127     }
128 
129     if (typeToBePartial->IsETSUnionType()) {
130         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
131         return HandleUnionForPartialType(typeToBePartial->AsETSUnionType());
132     }
133 
134     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
135     return CreatePartialTypeClass(typeToBePartial->AsETSObjectType(),
136                                   typeToBePartial->Variable()->Declaration()->Node());
137 }
138 
CreatePartialTypeParameter(ETSTypeParameter * typeToBePartial)139 Type *ETSChecker::CreatePartialTypeParameter(ETSTypeParameter *typeToBePartial)
140 {
141     return ProgramAllocator()->New<ETSPartialTypeParameter>(typeToBePartial, this);
142 }
143 
CreatePartialTypeClass(ETSObjectType * typeToBePartial,ir::AstNode * typeDeclNode)144 Type *ETSChecker::CreatePartialTypeClass(ETSObjectType *typeToBePartial, ir::AstNode *typeDeclNode)
145 {
146     auto const [partialName, partialQualifiedName] = GetPartialClassName(this, typeDeclNode);
147     auto const [partialProgram, recordTable] = GetPartialClassProgram(this, typeDeclNode);
148 
149     // Check if we've already generated the partial class, then don't do it again
150     const auto classNameToFind =
151         partialProgram == VarBinder()->Program() || VarBinder()->IsGenStdLib() || partialProgram->IsGenAbcForExternal()
152             ? partialName
153             : partialQualifiedName;
154     if (auto *var =
155             SearchNamesInMultiplePrograms({partialProgram, VarBinder()->Program()}, {classNameToFind, partialName});
156         var != nullptr) {
157         return var->TsType();
158     }
159 
160     if (typeDeclNode->IsTSInterfaceDeclaration()) {
161         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
162         return HandlePartialInterface(typeDeclNode->AsTSInterfaceDeclaration(), typeToBePartial);
163     }
164 
165     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
166     ir::ClassDefinition *partialClassDef = CreateClassPrototype(partialName, partialProgram);
167     partialClassDef->SetInternalName(partialQualifiedName);
168 
169     // Create only class 'header' (no properties and methods, but base type created)
170     BuildBasicClassProperties(partialClassDef);
171 
172     // If class prototype was created before, then we cached it's type. In that case return it.
173     // This handles cases where a Partial<T> presents in class T, because during generating T$partial we'd need the
174     // complete class T$partial which is not present at the time. Binding it's own type for it however will make it
175     // possible to resolve member references later, when the full T$partial class was created.
176     if (const auto found = NamedTypeStack().find(partialClassDef->TsType()); found != NamedTypeStack().end()) {
177         return *found;
178     }
179 
180     const varbinder::BoundContext boundCtx(recordTable, partialClassDef);
181 
182     NamedTypeStackElement ntse(this, partialClassDef->TsType());
183 
184     // If class is external, put partial of it in global scope for the varbinder
185     if (partialProgram != VarBinder()->Program()) {
186         VarBinder()->Program()->GlobalScope()->InsertBinding(partialClassDef->Ident()->Name(),
187                                                              partialClassDef->Variable());
188     }
189 
190     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
191     return CreatePartialTypeClassDef(partialClassDef, typeDeclNode->AsClassDefinition(), typeToBePartial, recordTable);
192 }
193 
HandlePartialInterface(ir::TSInterfaceDeclaration * interfaceDecl,ETSObjectType * typeToBePartial)194 Type *ETSChecker::HandlePartialInterface(ir::TSInterfaceDeclaration *interfaceDecl, ETSObjectType *typeToBePartial)
195 {
196     auto const [partialName, partialQualifiedName] = GetPartialClassName(this, interfaceDecl);
197     auto const [partialProgram, recordTable] = GetPartialClassProgram(this, interfaceDecl);
198 
199     auto *const partialInterDecl =
200         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
201         CreateInterfaceProto(partialName, partialProgram, interfaceDecl->IsStatic(), interfaceDecl->Modifiers());
202     partialInterDecl->SetInternalName(partialQualifiedName);
203 
204     if (const auto found = NamedTypeStack().find(partialInterDecl->TsType()); found != NamedTypeStack().end()) {
205         return *found;
206     }
207 
208     const varbinder::BoundContext boundCtx(recordTable, partialInterDecl);
209 
210     // If class is external, put partial of it in global scope for the varbinder
211     if (partialProgram != VarBinder()->Program()) {
212         VarBinder()->Program()->GlobalScope()->InsertBinding(partialInterDecl->Id()->Name(),
213                                                              partialInterDecl->Variable());
214     }
215 
216     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
217     auto *partialType = CreatePartialTypeInterfaceDecl(interfaceDecl, typeToBePartial, partialInterDecl);
218     ES2PANDA_ASSERT(partialType != nullptr);
219     NamedTypeStackElement ntse(this, partialType);
220 
221     return partialType;
222 }
223 
CreateNullishPropertyFromAccessor(ir::MethodDefinition * const accessor,ir::ClassDefinition * const newClassDefinition)224 ir::ClassProperty *ETSChecker::CreateNullishPropertyFromAccessor(ir::MethodDefinition *const accessor,
225                                                                  ir::ClassDefinition *const newClassDefinition)
226 {
227     auto *id = accessor->Id();
228     ES2PANDA_ASSERT(id != nullptr);
229     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
230     auto *ident = id->Clone(ProgramAllocator(), nullptr);
231     ES2PANDA_ASSERT(accessor->Function() != nullptr);
232     auto modifierFlag = accessor->Function()->IsGetter() && accessor->Overloads().empty() ? ir::ModifierFlags::READONLY
233                                                                                           : ir::ModifierFlags::NONE;
234 
235     auto *prop =
236         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
237         ProgramAllocator()->New<ir::ClassProperty>(ident, nullptr, nullptr, modifierFlag, ProgramAllocator(), false);
238 
239     ES2PANDA_ASSERT(prop != nullptr);
240     prop->SetParent(newClassDefinition);
241     ident->SetParent(prop);
242 
243     prop->SetTypeAnnotation(accessor->Function()->IsGetter()
244                                 ? accessor->Function()->ReturnTypeAnnotation()
245                                 : accessor->Function()->Params()[0]->AsETSParameterExpression()->TypeAnnotation());
246 
247     if (prop->TypeAnnotation() != nullptr) {
248         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
249         return CreateNullishProperty(prop, newClassDefinition);
250     }
251 
252     if (accessor->TsType() == nullptr) {
253         accessor->Parent()->Check(this);
254     }
255 
256     auto callSign = accessor->TsType()->AsETSFunctionType()->CallSignatures()[0];
257 
258     auto tsType = accessor->Function()->IsGetter() ? callSign->ReturnType() : callSign->Params()[0]->TsType();
259 
260     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
261     prop->SetTypeAnnotation(ProgramAllocator()->New<ir::OpaqueTypeNode>(tsType, ProgramAllocator()));
262 
263     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
264     return CreateNullishProperty(prop, newClassDefinition);
265 }
266 
CreateNullishProperty(ir::ClassProperty * const prop,ir::TSInterfaceDeclaration * const newTSInterfaceDefinition)267 ir::ClassProperty *ETSChecker::CreateNullishProperty(ir::ClassProperty *const prop,
268                                                      ir::TSInterfaceDeclaration *const newTSInterfaceDefinition)
269 {
270     auto *const propSavedValue = prop->Value();
271 
272     // Set value to nullptr to prevent cloning it (as for arrow functions that is not possible yet), we set it
273     // to 'undefined' anyway
274     prop->SetValue(nullptr);
275     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
276     auto *const propClone = prop->Clone(ProgramAllocator(), newTSInterfaceDefinition->Body())->AsClassProperty();
277 
278     // Revert original property value
279     prop->SetValue(propSavedValue);
280     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
281     propClone->SetValue(ProgramAllocator()->New<ir::UndefinedLiteral>());
282 
283     auto *propTypeAnn = propClone->TypeAnnotation();
284     ArenaVector<ir::TypeNode *> types(ProgramAllocator()->Adapter());
285 
286     // Handle implicit type annotation
287     if (propTypeAnn == nullptr) {
288         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
289         propTypeAnn = ProgramAllocator()->New<ir::OpaqueTypeNode>(prop->TsType(), ProgramAllocator());
290     }
291 
292     // Create new nullish type
293     types.push_back(propTypeAnn);
294     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
295     types.push_back(ProgramAllocNode<ir::ETSUndefinedType>(ProgramAllocator()));
296     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
297     auto *const unionType = ProgramAllocNode<ir::ETSUnionType>(std::move(types), ProgramAllocator());
298     propClone->SetTypeAnnotation(unionType);
299 
300     // Set new parents
301     unionType->SetParent(propClone);
302     propClone->SetParent(newTSInterfaceDefinition->Body());
303 
304     return propClone;
305 }
306 
CreateNullishProperty(ir::ClassProperty * const prop,ir::ClassDefinition * const newClassDefinition)307 ir::ClassProperty *ETSChecker::CreateNullishProperty(ir::ClassProperty *const prop,
308                                                      ir::ClassDefinition *const newClassDefinition)
309 {
310     auto *const propSavedValue = prop->Value();
311 
312     // Set value to nullptr to prevent cloning it (as for arrow functions that is not possible yet), we set it
313     // to 'undefined' anyway
314     prop->SetValue(nullptr);
315     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
316     auto *const propClone = prop->Clone(ProgramAllocator(), newClassDefinition)->AsClassProperty();
317 
318     // Revert original property value
319     prop->SetValue(propSavedValue);
320     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
321     propClone->SetValue(ProgramAllocator()->New<ir::UndefinedLiteral>());
322     propClone->AsClassProperty()->Value()->Check(this);
323 
324     ir::TypeNode *propertyTypeAnnotation = propClone->TypeAnnotation();
325     if (propertyTypeAnnotation == nullptr) {
326         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
327         propertyTypeAnnotation = ProgramAllocator()->New<ir::OpaqueTypeNode>(prop->Check(this), ProgramAllocator());
328     }
329 
330     // Create new nullish type annotation
331     ArenaVector<ir::TypeNode *> types(ProgramAllocator()->Adapter());
332     types.push_back(propertyTypeAnnotation);
333     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
334     types.push_back(ProgramAllocNode<ir::ETSUndefinedType>(ProgramAllocator()));
335     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
336     propertyTypeAnnotation = ProgramAllocNode<ir::ETSUnionType>(std::move(types), ProgramAllocator());
337     propClone->SetTypeAnnotation(propertyTypeAnnotation);
338     propClone->SetTsType(nullptr);
339 
340     // Set new parents
341     propertyTypeAnnotation->SetParent(propClone);
342     propClone->SetParent(newClassDefinition);
343 
344     return propClone;
345 }
346 
ProcessTypeParamAndGenSubstitution(ir::TSTypeParameterDeclaration const * const thisTypeParams,ArenaMap<ir::TSTypeParameter *,ir::TSTypeParameter * > * likeSubstitution,ir::TSTypeParameterDeclaration * newTypeParams=nullptr)347 ir::TSTypeParameterDeclaration *ETSChecker::ProcessTypeParamAndGenSubstitution(
348     ir::TSTypeParameterDeclaration const *const thisTypeParams,
349     ArenaMap<ir::TSTypeParameter *, ir::TSTypeParameter *> *likeSubstitution,
350     ir::TSTypeParameterDeclaration *newTypeParams = nullptr)
351 {
352     ArenaVector<ir::TSTypeParameter *> typeParams(ProgramAllocator()->Adapter());
353     if (newTypeParams == nullptr) {
354         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
355         newTypeParams = ProgramAllocNode<ir::TSTypeParameterDeclaration>(std::move(typeParams), typeParams.size());
356     }
357     for (auto *const classOrInterfaceDefTypeParam : thisTypeParams->Params()) {
358         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
359         auto *newTypeParam = ProgramAllocNode<ir::TSTypeParameter>(
360             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
361             CloneNodeIfNotNullptr(classOrInterfaceDefTypeParam->Name(), ProgramAllocator()),
362             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
363             CloneNodeIfNotNullptr(classOrInterfaceDefTypeParam->Constraint(), ProgramAllocator()),
364             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
365             CloneNodeIfNotNullptr(classOrInterfaceDefTypeParam->DefaultType(), ProgramAllocator()), ProgramAllocator());
366         ES2PANDA_ASSERT(newTypeParam != nullptr);
367         newTypeParams->AddParam(newTypeParam);
368         newTypeParam->SetParent(newTypeParams);
369         ES2PANDA_ASSERT(likeSubstitution != nullptr);
370         (*likeSubstitution)[classOrInterfaceDefTypeParam] = newTypeParam;
371     }
372     return newTypeParams;
373 }
374 
CreateNewSuperPartialRefTypeParamsDecl(ArenaMap<ir::TSTypeParameter *,ir::TSTypeParameter * > * likeSubstitution,const Type * const superPartialType,ir::Expression * superRef)375 ir::TSTypeParameterInstantiation *ETSChecker::CreateNewSuperPartialRefTypeParamsDecl(
376     ArenaMap<ir::TSTypeParameter *, ir::TSTypeParameter *> *likeSubstitution, const Type *const superPartialType,
377     ir::Expression *superRef)
378 {
379     ir::TSTypeParameterInstantiation *superPartialRefTypeParams = nullptr;
380     if (superPartialType == nullptr || superRef == nullptr ||
381         superRef->AsETSTypeReference()->Part()->TypeParams() == nullptr) {
382         return superPartialRefTypeParams;
383     }
384     superPartialRefTypeParams =
385         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
386         superRef->AsETSTypeReference()->Part()->TypeParams()->Clone(ProgramAllocator(), nullptr);
387     superPartialRefTypeParams->SetTsType(nullptr);
388     auto superRefParams = superPartialRefTypeParams->Params();
389     auto originRefParams = superRef->AsETSTypeReference()->Part()->TypeParams()->Params();
390     for (size_t ix = 0; ix < superRefParams.size(); ++ix) {
391         if (!originRefParams[ix]->IsETSTypeReference() ||
392             !originRefParams[ix]->AsETSTypeReference()->Part()->TsType()->IsETSTypeParameter()) {
393             continue;
394         }
395         auto it = likeSubstitution->find(
396             originRefParams[ix]->AsETSTypeReference()->Part()->TsType()->AsETSTypeParameter()->GetDeclNode());
397         if (it != likeSubstitution->end()) {
398             auto *typeParamRefPart =
399                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
400                 ProgramAllocNode<ir::ETSTypeReferencePart>(it->second->Name()->Clone(ProgramAllocator(), nullptr),
401                                                            ProgramAllocator());
402             ES2PANDA_ASSERT(typeParamRefPart != nullptr);
403             typeParamRefPart->Name()->SetParent(typeParamRefPart);
404             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
405             auto *typeParamRef = ProgramAllocNode<ir::ETSTypeReference>(typeParamRefPart, ProgramAllocator());
406             ES2PANDA_ASSERT(typeParamRef != nullptr);
407             typeParamRefPart->SetParent(typeParamRef);
408 
409             typeParamRef->SetParent(superPartialRefTypeParams);
410             superRefParams[ix] = typeParamRef;
411         }
412     }
413     return superPartialRefTypeParams;
414 }
415 
BuildSuperPartialTypeReference(Type * superPartialType,ir::TSTypeParameterInstantiation * superPartialRefTypeParams)416 ir::ETSTypeReference *ETSChecker::BuildSuperPartialTypeReference(
417     Type *superPartialType, ir::TSTypeParameterInstantiation *superPartialRefTypeParams)
418 {
419     ir::ETSTypeReference *superPartialRef = nullptr;
420     if (superPartialType != nullptr) {
421         auto *superPartialDeclNode = superPartialType->AsETSObjectType()->GetDeclNode();
422         auto *clonedId =
423             superPartialDeclNode->IsClassDefinition()
424                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
425                 ? superPartialDeclNode->AsClassDefinition()->Ident()->Clone(ProgramAllocator(), nullptr)
426                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
427                 : superPartialDeclNode->AsTSInterfaceDeclaration()->Id()->Clone(ProgramAllocator(), nullptr);
428         auto *superPartialRefPart =
429             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
430             ProgramAllocNode<ir::ETSTypeReferencePart>(clonedId, superPartialRefTypeParams, nullptr,
431                                                        ProgramAllocator());
432         ES2PANDA_ASSERT(superPartialRefPart != nullptr);
433         superPartialRefPart->Name()->SetParent(superPartialRefPart);
434         if (superPartialRefTypeParams != nullptr) {
435             superPartialRefTypeParams->SetParent(superPartialRefPart);
436         }
437 
438         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
439         superPartialRef = ProgramAllocNode<ir::ETSTypeReference>(superPartialRefPart, ProgramAllocator());
440         superPartialRefPart->SetParent(superPartialRef);
441     }
442     return superPartialRef;
443 }
444 
CreatePartialClassDeclaration(ir::ClassDefinition * const newClassDefinition,ir::ClassDefinition * classDef)445 void ETSChecker::CreatePartialClassDeclaration(ir::ClassDefinition *const newClassDefinition,
446                                                ir::ClassDefinition *classDef)
447 {
448     if (classDef->TypeParams() != nullptr) {
449         ArenaVector<ir::TSTypeParameter *> typeParams(ProgramAllocator()->Adapter());
450         for (auto *const classDefTypeParam : classDef->TypeParams()->Params()) {
451             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
452             auto *const newTypeParam = ProgramAllocNode<ir::TSTypeParameter>(
453                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
454                 CloneNodeIfNotNullptr(classDefTypeParam->Name(), ProgramAllocator()),
455                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
456                 CloneNodeIfNotNullptr(classDefTypeParam->Constraint(), ProgramAllocator()),
457                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
458                 CloneNodeIfNotNullptr(classDefTypeParam->DefaultType(), ProgramAllocator()), ProgramAllocator());
459             typeParams.emplace_back(newTypeParam);
460         }
461 
462         auto *const newTypeParams =
463             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
464             ProgramAllocNode<ir::TSTypeParameterDeclaration>(std::move(typeParams),
465                                                              classDef->TypeParams()->RequiredParams());
466         ES2PANDA_ASSERT(newTypeParams != nullptr);
467         newClassDefinition->SetTypeParams(newTypeParams);
468         newTypeParams->SetParent(newClassDefinition);
469     }
470 
471     newClassDefinition->SetVariable(newClassDefinition->Ident()->Variable());
472     newClassDefinition->AddModifier(static_cast<const ir::AstNode *>(classDef)->Modifiers());
473 
474     for (auto *const prop : classDef->Body()) {
475         // Only handle class properties (members)
476         // Method calls on partial classes will make the class not type safe, so we don't copy any methods
477         if (prop->IsClassProperty()) {
478             if (prop->AsClassProperty()->Id() == nullptr ||
479                 prop->AsClassProperty()->Id()->Name().Mutf8().find(compiler::Signatures::PROPERTY, 0) == 0) {
480                 continue;
481             }
482 
483             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
484             auto *const newProp = CreateNullishProperty(prop->AsClassProperty(), newClassDefinition);
485 
486             // Put the new property into the class declaration
487             newClassDefinition->Body().emplace_back(newProp);
488         }
489         if (prop->IsMethodDefinition() && prop->AsMethodDefinition()->Function() != nullptr &&
490             (prop->AsMethodDefinition()->Function()->IsGetter() ||
491              prop->AsMethodDefinition()->Function()->IsSetter())) {
492             auto *method = prop->AsMethodDefinition();
493             ES2PANDA_ASSERT(method->Id() != nullptr);
494             auto isBrokenSetter = method->Function()->IsSetter() && method->Function()->Params().size() != 1;
495             auto isBrokenGetter = method->Function()->IsGetter() && !method->Function()->Params().empty();
496             if (newClassDefinition->Scope()->FindLocal(method->Id()->Name(),
497                                                        varbinder::ResolveBindingOptions::VARIABLES) != nullptr ||
498                 isBrokenSetter || isBrokenGetter) {
499                 ES2PANDA_ASSERT(IsAnyError());
500                 continue;
501             }
502             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
503             newClassDefinition->Body().emplace_back(CreateNullishPropertyFromAccessor(method, newClassDefinition));
504         }
505     }
506     if (classDef->IsDeclare()) {
507         newClassDefinition->AddModifier(ir::ModifierFlags::DECLARE);
508     }
509 
510     // Run varbinder for new partial class to set scopes
511     compiler::InitScopesPhaseETS::RunExternalNode(newClassDefinition, VarBinder());
512 
513     newClassDefinition->SetTsType(nullptr);
514     newClassDefinition->Variable()->SetTsType(nullptr);
515 }
516 
SetupFunctionParams(ir::ScriptFunction * function,varbinder::FunctionParamScope * paramScope,checker::ETSChecker * checker)517 static void SetupFunctionParams(ir::ScriptFunction *function, varbinder::FunctionParamScope *paramScope,
518                                 checker::ETSChecker *checker)
519 {
520     for (auto *params : function->Params()) {
521         auto *paramExpr = params->AsETSParameterExpression();
522         if (paramExpr->Ident()->TypeAnnotation() == nullptr) {
523             paramExpr->Ident()->SetTsTypeAnnotation(nullptr);
524         } else {
525             auto *unionType =
526                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
527                 checker->AllocNode<ir::ETSUnionType>(
528                     ArenaVector<ir::TypeNode *>({paramExpr->Ident()->TypeAnnotation(),
529                                                  // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
530                                                  checker->AllocNode<ir::ETSUndefinedType>(checker->Allocator())},
531                                                 checker->Allocator()->Adapter()),
532                     checker->Allocator());
533             ES2PANDA_ASSERT(unionType != nullptr);
534             paramExpr->Ident()->SetTsTypeAnnotation(unionType);
535             unionType->SetParent(paramExpr->Ident());
536         }
537         auto [paramVar, node] = paramScope->AddParamDecl(checker->Allocator(), checker->VarBinder(), paramExpr);
538         if (node != nullptr) {
539             checker->VarBinder()->ThrowRedeclaration(node->Start(), paramVar->Name(), paramVar->Declaration()->Type());
540         }
541 
542         paramExpr->SetVariable(paramVar);
543     }
544 }
545 
546 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
CreateNullishAccessor(ir::MethodDefinition * const accessor,ir::TSInterfaceDeclaration * interface)547 ir::MethodDefinition *ETSChecker::CreateNullishAccessor(ir::MethodDefinition *const accessor,
548                                                         ir::TSInterfaceDeclaration *interface)
549 {
550     const auto interfaceCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(VarBinder(), interface->Scope());
551     auto *paramScope = ProgramAllocator()->New<varbinder::FunctionParamScope>(ProgramAllocator(), interface->Scope());
552     auto *functionScope = ProgramAllocator()->New<varbinder::FunctionScope>(ProgramAllocator(), paramScope);
553     ES2PANDA_ASSERT(functionScope != nullptr);
554     functionScope->BindParamScope(paramScope);
555     paramScope->BindFunctionScope(functionScope);
556 
557     {
558         auto paramScopeCtx = varbinder::LexicalScope<varbinder::FunctionParamScope>::Enter(VarBinder(), paramScope);
559         VarBinder()->AddMandatoryParam(varbinder::TypedBinder::MANDATORY_PARAM_THIS);
560     }
561 
562     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
563     ir::MethodDefinition *nullishAccessor = accessor->Clone(ProgramAllocator(), interface->Body());
564 
565     auto *decl = ProgramAllocator()->New<varbinder::FunctionDecl>(ProgramAllocator(), nullishAccessor->Id()->Name(),
566                                                                   nullishAccessor);
567     auto *var = ProgramAllocator()->New<varbinder::LocalVariable>(decl, varbinder::VariableFlags::VAR);
568     var->AddFlag(varbinder::VariableFlags::METHOD);
569     nullishAccessor->Id()->SetVariable(var);
570     nullishAccessor->SetVariable(var);
571 
572     functionScope->BindName(interface->InternalName());
573 
574     auto *function = nullishAccessor->Function();
575     function->SetVariable(var);
576 
577     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
578     function->SetIdent(nullishAccessor->Id()->Clone(ProgramAllocator(), function));
579     function->SetScope(functionScope);
580     paramScope->BindNode(function);
581     functionScope->BindNode(function);
582 
583     if (function->IsGetter()) {
584         auto *propTypeAnn = function->ReturnTypeAnnotation();
585 
586         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
587         function->SetReturnTypeAnnotation(ProgramAllocNode<ir::ETSUnionType>(
588             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
589             ArenaVector<ir::TypeNode *>({propTypeAnn, ProgramAllocNode<ir::ETSUndefinedType>(ProgramAllocator())},
590                                         ProgramAllocator()->Adapter()),
591             ProgramAllocator()));
592     } else {
593         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
594         SetupFunctionParams(function, paramScope, this);
595     }
596 
597     nullishAccessor->SetOverloads(ArenaVector<ir::MethodDefinition *>(ProgramAllocator()->Adapter()));
598 
599     return nullishAccessor;
600 }
601 
CreateInterfaceProto(util::StringView name,parser::Program * const interfaceDeclProgram,const bool isStatic,const ir::ModifierFlags flags)602 ir::TSInterfaceDeclaration *ETSChecker::CreateInterfaceProto(util::StringView name,
603                                                              parser::Program *const interfaceDeclProgram,
604                                                              const bool isStatic, const ir::ModifierFlags flags)
605 {
606     const auto globalCtx =
607         varbinder::LexicalScope<varbinder::GlobalScope>::Enter(VarBinder(), interfaceDeclProgram->GlobalScope());
608 
609     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
610     auto *const interfaceId = ProgramAllocNode<ir::Identifier>(name, ProgramAllocator());
611     ES2PANDA_ASSERT(interfaceId);
612     const auto [decl, var] = VarBinder()->NewVarDecl<varbinder::InterfaceDecl>(interfaceId->Start(), ProgramAllocator(),
613                                                                                interfaceId->Name());
614     ES2PANDA_ASSERT(interfaceId != nullptr);
615     interfaceId->SetVariable(var);
616 
617     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
618     auto *body = ProgramAllocNode<ir::TSInterfaceBody>(ArenaVector<ir::AstNode *>(ProgramAllocator()->Adapter()));
619     ArenaVector<ir::TSInterfaceHeritage *> extends(ProgramAllocator()->Adapter());
620 
621     ArenaVector<ir::TSTypeParameter *> typeParams(ProgramAllocator()->Adapter());
622     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
623     auto *newTypeParams = ProgramAllocNode<ir::TSTypeParameterDeclaration>(std::move(typeParams), typeParams.size());
624     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
625     auto partialInterface = ProgramAllocNode<ir::TSInterfaceDeclaration>(
626         ProgramAllocator(), std::move(extends),
627         ir::TSInterfaceDeclaration::ConstructorData {interfaceId, newTypeParams, body, isStatic,
628                                                      interfaceDeclProgram != VarBinder()->Program(),
629                                                      Language(Language::Id::ETS)});
630 
631     const auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>(VarBinder());
632     ES2PANDA_ASSERT(partialInterface != nullptr);
633     partialInterface->TypeParams()->SetParent(partialInterface);
634     partialInterface->SetScope(classCtx.GetScope());
635     partialInterface->SetVariable(var);
636     decl->BindNode(partialInterface);
637 
638     // Put class declaration in global scope, and in program AST
639     partialInterface->SetParent(interfaceDeclProgram->Ast());
640     interfaceDeclProgram->Ast()->Statements().push_back(partialInterface);
641     interfaceDeclProgram->GlobalScope()->InsertBinding(name, var);
642 
643     partialInterface->AddModifier(flags);
644 
645     return partialInterface;
646 }
647 
CreatePartialTypeInterfaceMethods(ir::TSInterfaceDeclaration * const interfaceDecl,ir::TSInterfaceDeclaration * partialInterface)648 void ETSChecker::CreatePartialTypeInterfaceMethods(ir::TSInterfaceDeclaration *const interfaceDecl,
649                                                    ir::TSInterfaceDeclaration *partialInterface)
650 {
651     auto &partialInterfaceMethods = partialInterface->Body()->Body();
652 
653     auto const addNullishAccessor = [&partialInterfaceMethods](ir::MethodDefinition *accessor) -> void {
654         auto const it = std::find_if(partialInterfaceMethods.begin(), partialInterfaceMethods.end(),
655                                      [accessor](ir::AstNode const *node) -> bool {
656                                          return node->AsMethodDefinition()->Id()->Name() == accessor->Id()->Name();
657                                      });
658         if (it == partialInterfaceMethods.end()) {
659             accessor->Function()->ClearFlag(ir::ScriptFunctionFlags::OVERLOAD);
660             partialInterfaceMethods.emplace_back(accessor);
661         } else if (accessor->Function()->IsSetter()) {
662             ES2PANDA_ASSERT_POS((*it)->AsMethodDefinition()->Function()->IsGetter(), (*it)->Start());
663             (*it)->AsMethodDefinition()->AddOverload(accessor);
664             accessor->SetParent(*it);
665             accessor->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD);
666         } else {
667             ES2PANDA_ASSERT_POS((*it)->AsMethodDefinition()->Function()->IsSetter(), (*it)->Start());
668             auto setter = (*it)->AsMethodDefinition();
669             accessor->AddOverload(setter);
670             setter->SetParent(accessor);
671             setter->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD);
672             partialInterfaceMethods.erase(it);
673             partialInterfaceMethods.emplace_back(accessor);
674         }
675     };
676 
677     for (auto *const prop : interfaceDecl->Body()->Body()) {
678         if (!prop->IsMethodDefinition()) {
679             continue;
680         }
681 
682         auto *const method = prop->AsMethodDefinition();
683         auto *func = method->Function();
684         ES2PANDA_ASSERT(func != nullptr);
685         ES2PANDA_ASSERT((func->Flags() & ir::ScriptFunctionFlags::OVERLOAD) == 0U);
686 
687         if (func->IsGetter() || func->IsSetter()) {
688             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
689             addNullishAccessor(CreateNullishAccessor(method, partialInterface));
690         }
691 
692         for (auto *overload : method->Overloads()) {
693             ES2PANDA_ASSERT(overload->Function() != nullptr);
694             if (overload->Function()->IsGetter() || overload->Function()->IsSetter()) {
695                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
696                 addNullishAccessor(CreateNullishAccessor(overload, partialInterface));
697             }
698         }
699     }
700 }
701 
CreatePartialTypeInterfaceDecl(ir::TSInterfaceDeclaration * const interfaceDecl,ETSObjectType * const typeToBePartial,ir::TSInterfaceDeclaration * partialInterface)702 Type *ETSChecker::CreatePartialTypeInterfaceDecl(ir::TSInterfaceDeclaration *const interfaceDecl,
703                                                  ETSObjectType *const typeToBePartial,
704                                                  ir::TSInterfaceDeclaration *partialInterface)
705 {
706     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
707     CreatePartialTypeInterfaceMethods(interfaceDecl, partialInterface);
708     // Create nullish properties of the partial class
709     // Build the new Partial class based on the 'T' type parameter of 'Partial<T>'
710     auto *likeSubstitution =
711         ProgramAllocator()->New<ArenaMap<ir::TSTypeParameter *, ir::TSTypeParameter *>>(ProgramAllocator()->Adapter());
712 
713     if (interfaceDecl->TypeParams() != nullptr) {
714         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
715         ProcessTypeParamAndGenSubstitution(interfaceDecl->TypeParams(), likeSubstitution,
716                                            partialInterface->TypeParams());
717     }
718 
719     compiler::InitScopesPhaseETS::RunExternalNode(partialInterface, VarBinder());
720 
721     auto methodscope = partialInterface->Scope()->AsClassScope()->InstanceMethodScope();
722     // Add getter methods to instancemethodscope.
723     for (auto *const prop : partialInterface->Body()->Body()) {
724         auto *func = prop->AsMethodDefinition()->Function();
725         ES2PANDA_ASSERT(func != nullptr);
726         if (prop->IsMethodDefinition() && func->IsGetter()) {
727             auto *decl = ProgramAllocator()->New<varbinder::FunctionDecl>(
728                 ProgramAllocator(), prop->AsMethodDefinition()->Key()->AsIdentifier()->Name(), prop);
729             methodscope->AddDecl(ProgramAllocator(), decl, ScriptExtension::ETS);
730         }
731     }
732 
733     // Create partial type for super type
734     for (auto *extend : interfaceDecl->Extends()) {
735         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
736         auto *t = extend->Expr()->AsETSTypeReference()->Part()->GetType(this);
737         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
738         if (auto *superPartialType = CreatePartialType(t); superPartialType != nullptr) {
739             ir::TSTypeParameterInstantiation *superPartialRefTypeParams =
740                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
741                 CreateNewSuperPartialRefTypeParamsDecl(likeSubstitution, superPartialType, extend->Expr());
742 
743             ir::ETSTypeReference *superPartialRef =
744                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
745                 BuildSuperPartialTypeReference(superPartialType, superPartialRefTypeParams);
746             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
747             partialInterface->Extends().push_back(ProgramAllocNode<ir::TSInterfaceHeritage>(superPartialRef));
748             partialInterface->Extends().back()->SetParent(partialInterface);
749         }
750     }
751 
752     auto *const partialType = partialInterface->Check(this)->AsETSObjectType();
753     partialType->SetBaseType(typeToBePartial);
754 
755     return partialType;
756 }
757 
CreateConstructorForPartialType(ir::ClassDefinition * const partialClassDef,checker::ETSObjectType * const partialType,varbinder::RecordTable * const recordTable)758 void ETSChecker::CreateConstructorForPartialType(ir::ClassDefinition *const partialClassDef,
759                                                  checker::ETSObjectType *const partialType,
760                                                  varbinder::RecordTable *const recordTable)
761 {
762     // Create scopes
763     auto *const scope = partialClassDef->Scope()->AsClassScope();
764     const auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(VarBinder(), scope);
765 
766     // Create ctor
767     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
768     auto *const ctor = CreateNonStaticClassInitializer(classCtx.GetScope(), recordTable);
769     auto *const ctorFunc = ctor->Function();
770     if (partialClassDef->IsDeclare()) {
771         ctorFunc->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
772     }
773     auto *const ctorId = ctor->Function()->Id();
774 
775     // Handle bindings, create method decl for ctor
776     ctorFunc->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable->SetTsType(partialType);
777     partialType->AddConstructSignature(ctorFunc->Signature());
778     ctorFunc->Signature()->SetOwner(partialType);
779     ctor->SetParent(partialClassDef);
780     ctorId->SetVariable(ProgramAllocator()->New<varbinder::LocalVariable>(
781         ProgramAllocator()->New<varbinder::MethodDecl>(ctorId->Name()), varbinder::VariableFlags::METHOD));
782     ctor->Id()->SetVariable(ctorId->Variable());
783 
784     // Put ctor in partial class body
785     partialClassDef->Body().emplace_back(ctor);
786 }
787 
CreateClassPrototype(util::StringView name,parser::Program * const classDeclProgram)788 ir::ClassDefinition *ETSChecker::CreateClassPrototype(util::StringView name, parser::Program *const classDeclProgram)
789 {
790     const auto globalCtx =
791         varbinder::LexicalScope<varbinder::GlobalScope>::Enter(VarBinder(), classDeclProgram->GlobalScope());
792 
793     // Create class name, and declaration variable
794     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
795     auto *const classId = ProgramAllocNode<ir::Identifier>(name, ProgramAllocator());
796     ES2PANDA_ASSERT(classId != nullptr);
797     const auto [decl, var] = VarBinder()->NewVarDecl<varbinder::ClassDecl>(classId->Start(), classId->Name());
798     classId->SetVariable(var);
799 
800     // Create class definition node
801     const auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>(VarBinder());
802     auto *const classDef =
803         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
804         ProgramAllocNode<ir::ClassDefinition>(ProgramAllocator(), classId, ir::ClassDefinitionModifiers::DECLARATION,
805                                               ir::ModifierFlags::NONE, Language(Language::Id::ETS));
806     ES2PANDA_ASSERT(classDef != nullptr);
807     classDef->SetScope(classCtx.GetScope());
808     classDef->SetVariable(var);
809 
810     // Create class declaration node
811     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
812     auto *const classDecl = ProgramAllocNode<ir::ClassDeclaration>(classDef, ProgramAllocator());
813     ES2PANDA_ASSERT(classDecl != nullptr);
814     classDecl->SetParent(classDeclProgram->Ast());
815 
816     // Class definition is scope bearer, not class declaration
817     classDef->Scope()->BindNode(classDecl->Definition());
818     decl->BindNode(classDef);
819 
820     // Put class declaration in global scope, and in program AST
821     classDeclProgram->Ast()->Statements().push_back(classDecl);
822     classDeclProgram->GlobalScope()->InsertBinding(name, var);
823 
824     return classDef;
825 }
826 
SearchNamesInMultiplePrograms(const std::set<const parser::Program * > & programs,const std::set<util::StringView> & classNamesToFind)827 varbinder::Variable *ETSChecker::SearchNamesInMultiplePrograms(const std::set<const parser::Program *> &programs,
828                                                                const std::set<util::StringView> &classNamesToFind)
829 {
830     for (const auto *const program : programs) {
831         for (const auto &className : classNamesToFind) {
832             auto *const var = program->GlobalScope()->Find(className, varbinder::ResolveBindingOptions::ALL).variable;
833             if (var == nullptr) {
834                 continue;
835             }
836 
837             if (var->TsType() == nullptr) {
838                 var->Declaration()->Node()->Check(this);
839             }
840 
841             return var;
842         }
843     }
844 
845     return nullptr;
846 }
847 
HandleUnionForPartialType(ETSUnionType * const typeToBePartial)848 Type *ETSChecker::HandleUnionForPartialType(ETSUnionType *const typeToBePartial)
849 {
850     // Convert a union type to partial, by converting all types in it to partial, and making a new union
851     // type out of them
852     const auto *const unionTypeNode = typeToBePartial->AsETSUnionType();
853     ArenaVector<checker::Type *> newTypesForUnion(ProgramAllocator()->Adapter());
854 
855     for (auto *const typeFromUnion : unionTypeNode->ConstituentTypes()) {
856         if ((typeFromUnion->Variable() != nullptr) && (typeFromUnion->Variable()->Declaration() != nullptr)) {
857             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
858             newTypesForUnion.emplace_back(CreatePartialType(typeFromUnion));
859         } else {
860             newTypesForUnion.emplace_back(typeFromUnion);
861         }
862     }
863 
864     return CreateETSUnionType(std::move(newTypesForUnion));
865 }
866 
CreatePartialTypeClassDef(ir::ClassDefinition * const partialClassDef,ir::ClassDefinition * const classDef,ETSObjectType * const typeToBePartial,varbinder::RecordTable * const recordTableToUse)867 Type *ETSChecker::CreatePartialTypeClassDef(ir::ClassDefinition *const partialClassDef,
868                                             ir::ClassDefinition *const classDef, ETSObjectType *const typeToBePartial,
869                                             varbinder::RecordTable *const recordTableToUse)
870 {
871     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
872     CreatePartialClassDeclaration(partialClassDef, classDef);
873 
874     // Run checker
875     auto *const partialType = partialClassDef->Check(this)->AsETSObjectType();
876 
877     for (auto *interface : typeToBePartial->Interfaces()) {
878         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
879         partialType->AddInterface(CreatePartialType(interface)->AsETSObjectType());
880     }
881 
882     partialType->SetBaseType(typeToBePartial);
883 
884     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
885     CreateConstructorForPartialType(partialClassDef, partialType, recordTableToUse);
886 
887     // Create partial type for super type
888     if (typeToBePartial != GlobalETSObjectType()) {
889         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
890         auto *const partialSuper = CreatePartialType((classDef->Super() == nullptr || !classDef->Super()->IsTypeNode())
891                                                          ? GlobalETSObjectType()
892                                                          : classDef->Super()->TsType());
893 
894         if (partialSuper == partialType) {
895             LogError(diagnostic::CYCLIC_CLASS_SUPER_TYPE, {}, classDef->Start());
896             return partialType;
897         }
898         if (partialSuper->IsTypeError()) {
899             return partialType;
900         }
901         partialType->SetSuperType(partialSuper->AsETSObjectType());
902     }
903 
904     return partialType;
905 }
906 
CreateScriptFunctionForConstructor(varbinder::FunctionScope * const scope)907 std::pair<ir::ScriptFunction *, ir::Identifier *> ETSChecker::CreateScriptFunctionForConstructor(
908     varbinder::FunctionScope *const scope)
909 {
910     ArenaVector<ir::Statement *> statements(ProgramAllocator()->Adapter());
911     ArenaVector<ir::Expression *> params(ProgramAllocator()->Adapter());
912 
913     ir::ScriptFunction *func {};
914     ir::Identifier *id {};
915 
916     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
917     auto *const body = ProgramAllocNode<ir::BlockStatement>(ProgramAllocator(), std::move(statements));
918     ES2PANDA_ASSERT(body != nullptr);
919     body->SetScope(scope);
920     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
921     id = ProgramAllocNode<ir::Identifier>(util::UString(std::string("constructor"), ProgramAllocator()).View(),
922                                           ProgramAllocator());
923     auto funcSignature = ir::FunctionSignature(nullptr, std::move(params), nullptr);
924     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
925     func = ProgramAllocNode<ir::ScriptFunction>(
926         ProgramAllocator(), ir::ScriptFunction::ScriptFunctionData {body, std::move(funcSignature),
927                                                                     ir::ScriptFunctionFlags::CONSTRUCTOR |
928                                                                         ir::ScriptFunctionFlags::EXPRESSION,
929                                                                     ir::ModifierFlags::PUBLIC});
930     ES2PANDA_ASSERT(func != nullptr);
931     func->SetScope(scope);
932     scope->BindNode(func);
933     func->SetIdent(id);
934     VarBinder()->AsETSBinder()->AddFunctionThisParam(func);
935 
936     return std::make_pair(func, id);
937 }
938 
CreateNonStaticClassInitializer(varbinder::ClassScope * classScope,varbinder::RecordTable * const recordTable)939 ir::MethodDefinition *ETSChecker::CreateNonStaticClassInitializer(varbinder::ClassScope *classScope,
940                                                                   varbinder::RecordTable *const recordTable)
941 {
942     const auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(VarBinder(), classScope);
943 
944     auto *paramScope = ProgramAllocator()->New<varbinder::FunctionParamScope>(ProgramAllocator(), classScope);
945     auto *const functionScope = ProgramAllocator()->New<varbinder::FunctionScope>(ProgramAllocator(), paramScope);
946     functionScope->BindParamScope(paramScope);
947     paramScope->BindFunctionScope(functionScope);
948 
949     const auto funcParamCtx = varbinder::LexicalScope<varbinder::FunctionParamScope>::Enter(VarBinder(), paramScope);
950 
951     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
952     auto [func, id] = CreateScriptFunctionForConstructor(functionScope);
953 
954     paramScope->BindNode(func);
955     functionScope->BindNode(func);
956 
957     auto *const signatureInfo = CreateSignatureInfo();
958     auto *const signature = CreateSignature(signatureInfo, GlobalVoidType(), func);
959     func->SetSignature(signature);
960 
961     VarBinder()->AsETSBinder()->BuildInternalNameWithCustomRecordTable(func, recordTable);
962     VarBinder()->AsETSBinder()->BuildFunctionName(func);
963     VarBinder()->Functions().push_back(functionScope);
964 
965     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
966     auto *funcExpr = ProgramAllocNode<ir::FunctionExpression>(func);
967     auto *const ctor =
968         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
969         ProgramAllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::CONSTRUCTOR,
970                                                // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
971                                                id->Clone(ProgramAllocator(), classScope->Node()), funcExpr,
972                                                ir::ModifierFlags::NONE, ProgramAllocator(), false);
973 
974     auto *const funcType = CreateETSMethodType(id->Name(), {{signature}, ProgramAllocator()->Adapter()});
975     ctor->SetTsType(funcType);
976 
977     return ctor;
978 }
979 
GetReadonlyType(Type * type)980 Type *ETSChecker::GetReadonlyType(Type *type)
981 {
982     if (const auto found = NamedTypeStack().find(type); found != NamedTypeStack().end()) {
983         return *found;
984     }
985 
986     NamedTypeStackElement ntse(this, type);
987     ES2PANDA_ASSERT(type != nullptr);
988     if (type->IsETSArrayType()) {
989         ETSArrayType *const clonedArrayType =
990             ProgramAllocator()->New<ETSArrayType>(type->AsETSArrayType()->ElementType());
991         ES2PANDA_ASSERT(clonedArrayType != nullptr);
992         clonedArrayType->AddTypeFlag(TypeFlag::READONLY);
993         return clonedArrayType;
994     }
995     if (type->IsETSTupleType()) {
996         Type *const clonedType = type->Clone(this);
997         clonedType->AddTypeFlag(TypeFlag::READONLY);
998         return clonedType;
999     }
1000 
1001     if (type->IsETSObjectType()) {
1002         type->AsETSObjectType()->InstanceFields();
1003         auto *clonedType = type->Clone(this)->AsETSObjectType();
1004         MakePropertiesReadonly(clonedType);
1005         return clonedType;
1006     }
1007     if (type->IsETSTypeParameter()) {
1008         return ProgramAllocator()->New<ETSReadonlyType>(type->AsETSTypeParameter());
1009     }
1010     if (type->IsETSUnionType()) {
1011         ArenaVector<Type *> unionTypes(ProgramAllocator()->Adapter());
1012         for (auto *t : type->AsETSUnionType()->ConstituentTypes()) {
1013             unionTypes.emplace_back(t->IsETSObjectType() ? GetReadonlyType(t) : t->Clone(this));
1014         }
1015         return CreateETSUnionType(std::move(unionTypes));
1016     }
1017     return type;
1018 }
1019 
MakePropertiesReadonly(ETSObjectType * const classType)1020 void ETSChecker::MakePropertiesReadonly(ETSObjectType *const classType)
1021 {
1022     classType->UpdateTypeProperties(this, [this](auto *property, auto *propType) {
1023         auto *newDecl =
1024             ProgramAllocator()->New<varbinder::ReadonlyDecl>(property->Name(), property->Declaration()->Node());
1025         auto *const propCopy = property->Copy(ProgramAllocator(), newDecl);
1026         propCopy->AddFlag(varbinder::VariableFlags::READONLY);
1027         propCopy->SetTsType(propType);
1028         return propCopy;
1029     });
1030 }
1031 
1032 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1033 // Required utility type
1034 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
HandleRequiredType(Type * typeToBeRequired)1035 Type *ETSChecker::HandleRequiredType(Type *typeToBeRequired)
1036 {
1037     if (const auto found = NamedTypeStack().find(typeToBeRequired); found != NamedTypeStack().end()) {
1038         return *found;
1039     }
1040 
1041     NamedTypeStackElement ntse(this, typeToBeRequired);
1042 
1043     if (typeToBeRequired->IsETSTypeParameter()) {
1044         auto *const requiredClone = typeToBeRequired->Clone(this);
1045         requiredClone->AddTypeFlag(TypeFlag::ETS_REQUIRED_TYPE_PARAMETER);
1046         return requiredClone;
1047     }
1048 
1049     if (typeToBeRequired->IsETSUnionType()) {
1050         ArenaVector<Type *> unionTypes(ProgramAllocator()->Adapter());
1051         for (auto *type : typeToBeRequired->AsETSUnionType()->ConstituentTypes()) {
1052             if (type->IsETSObjectType()) {
1053                 type = type->Clone(this);
1054                 ES2PANDA_ASSERT(type != nullptr);
1055                 MakePropertiesNonNullish(type->AsETSObjectType());
1056             }
1057 
1058             if (type->IsETSNullType() || type->IsETSUndefinedType()) {
1059                 continue;
1060             }
1061 
1062             unionTypes.emplace_back(type);
1063         }
1064 
1065         return CreateETSUnionType(std::move(unionTypes));
1066     }
1067 
1068     if (typeToBeRequired->IsETSObjectType()) {
1069         typeToBeRequired->AsETSObjectType()->InstanceFields();  // call to instantiate properties
1070     }
1071 
1072     typeToBeRequired = typeToBeRequired->Clone(this);
1073 
1074     MakePropertiesNonNullish(typeToBeRequired->AsETSObjectType());
1075 
1076     return typeToBeRequired;
1077 }
1078 
MakePropertiesNonNullish(ETSObjectType * const classType)1079 void ETSChecker::MakePropertiesNonNullish(ETSObjectType *const classType)
1080 {
1081     classType->AddObjectFlag(ETSObjectFlags::REQUIRED);
1082     classType->InstanceFields();
1083 
1084     for (const auto &[_, propVar] : classType->InstanceFields()) {
1085         MakePropertyNonNullish<PropertyType::INSTANCE_FIELD>(classType, propVar);
1086     }
1087 
1088     for (const auto &[_, propVar] : classType->StaticFields()) {
1089         MakePropertyNonNullish<PropertyType::STATIC_FIELD>(classType, propVar);
1090     }
1091 
1092     if (classType->SuperType() != nullptr) {
1093         auto *const superRequired = classType->SuperType()->Clone(this)->AsETSObjectType();
1094         MakePropertiesNonNullish(superRequired);
1095         classType->SetSuperType(superRequired);
1096     }
1097 }
1098 
1099 template <PropertyType PROP_TYPE>
MakePropertyNonNullish(ETSObjectType * const classType,varbinder::LocalVariable * const prop)1100 void ETSChecker::MakePropertyNonNullish(ETSObjectType *const classType, varbinder::LocalVariable *const prop)
1101 {
1102     auto *const propType = prop->TsType();
1103     auto *const nonNullishPropType = GetNonNullishType(propType);
1104 
1105     auto *const propCopy = prop->Copy(ProgramAllocator(), prop->Declaration());
1106     ES2PANDA_ASSERT(propCopy != nullptr);
1107     propCopy->SetTsType(nonNullishPropType);
1108     classType->RemoveProperty<PROP_TYPE>(prop);
1109     classType->AddProperty<PROP_TYPE>(propCopy);
1110 }
1111 
StringEqualsPropertyName(const util::StringView pname1,const ir::Expression * const prop2Key)1112 static bool StringEqualsPropertyName(const util::StringView pname1, const ir::Expression *const prop2Key)
1113 {
1114     util::StringView pname2;
1115     if (prop2Key->IsStringLiteral()) {
1116         pname2 = prop2Key->AsStringLiteral()->Str();
1117     } else if (prop2Key->IsIdentifier()) {
1118         pname2 = prop2Key->AsIdentifier()->Name();
1119     }
1120 
1121     return pname1 == pname2;
1122 }
1123 
ValidateObjectLiteralForRequiredType(const ETSObjectType * const requiredType,const ir::ObjectExpression * const initObjExpr)1124 void ETSChecker::ValidateObjectLiteralForRequiredType(const ETSObjectType *const requiredType,
1125                                                       const ir::ObjectExpression *const initObjExpr)
1126 {
1127     auto initObjExprContainsField = [&initObjExpr](const util::StringView pname1) {
1128         return std::find_if(initObjExpr->Properties().begin(), initObjExpr->Properties().end(),
1129                             [&pname1](const ir::Expression *const initProp) {
1130                                 return StringEqualsPropertyName(pname1, initProp->AsProperty()->Key());
1131                             }) != initObjExpr->Properties().end();
1132     };
1133 
1134     if (requiredType->HasObjectFlag(ETSObjectFlags::INTERFACE)) {
1135         for (const auto *method : requiredType->GetDeclNode()->AsTSInterfaceDeclaration()->Body()->Body()) {
1136             if (!method->IsMethodDefinition()) {
1137                 continue;
1138             }
1139             auto *func = method->AsMethodDefinition()->Function();
1140             ES2PANDA_ASSERT(func != nullptr);
1141             if (!func->IsGetter()) {
1142                 continue;
1143             }
1144 
1145             auto fieldname = method->AsMethodDefinition()->Key()->AsIdentifier()->Name();
1146             if (!initObjExprContainsField(fieldname)) {
1147                 LogError(diagnostic::REQUIRED_PROP_MISSING_INIT, {fieldname, requiredType->Name()},
1148                          initObjExpr->Start());
1149             }
1150         }
1151 
1152         return;
1153     }
1154 
1155     for (const auto &[propName, _] : requiredType->InstanceFields()) {
1156         if (!initObjExprContainsField(propName)) {
1157             LogError(diagnostic::REQUIRED_PROP_MISSING_INIT, {propName, requiredType->Name()}, initObjExpr->Start());
1158         }
1159     }
1160 }
1161 
1162 }  // namespace ark::es2panda::checker
1163