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