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