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