1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "util/helpers.h"
17 #include "macros.h"
18 #include "varbinder/ETSBinder.h"
19 #include "parser/ETSparser.h"
20
21 #include "checker/types/ets/etsTupleType.h"
22 #include "checker/types/ets/etsReadonlyType.h"
23 #include "checker/ets/typeRelationContext.h"
24 #include "checker/ETSchecker.h"
25 #include "checker/types/globalTypesHolder.h"
26 #include "evaluate/scopedDebugInfoPlugin.h"
27
28 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
29 #include "generated/signatures.h"
30
31 namespace ark::es2panda::checker {
FindVariableInFunctionScope(const util::StringView name,const varbinder::ResolveBindingOptions options)32 varbinder::Variable *ETSChecker::FindVariableInFunctionScope(const util::StringView name,
33 const varbinder::ResolveBindingOptions options)
34 {
35 return Scope()->FindInFunctionScope(name, options).variable;
36 }
37
FindVariableInClassOrEnclosing(const util::StringView name,const ETSObjectType * classType)38 std::pair<varbinder::Variable *, const ETSObjectType *> ETSChecker::FindVariableInClassOrEnclosing(
39 const util::StringView name, const ETSObjectType *classType)
40 {
41 const auto searchFlags = PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE |
42 PropertySearchFlags::SEARCH_IN_INTERFACES;
43 auto *resolved = classType->GetProperty(name, searchFlags);
44 while (classType->EnclosingType() != nullptr && resolved == nullptr) {
45 classType = classType->EnclosingType();
46 resolved = classType->GetProperty(name, searchFlags);
47 }
48
49 return {resolved, classType};
50 }
51
FindVariableInGlobal(const ir::Identifier * const identifier,const varbinder::ResolveBindingOptions options)52 varbinder::Variable *ETSChecker::FindVariableInGlobal(const ir::Identifier *const identifier,
53 const varbinder::ResolveBindingOptions options)
54 {
55 return Scope()->FindInGlobal(identifier->Name(), options).variable;
56 }
57
IsVariableStatic(const varbinder::Variable * var)58 bool ETSChecker::IsVariableStatic(const varbinder::Variable *var)
59 {
60 if (var->HasFlag(varbinder::VariableFlags::METHOD)) {
61 return var->TsType()->AsETSFunctionType()->CallSignatures()[0]->HasSignatureFlag(SignatureFlags::STATIC);
62 }
63 return var->HasFlag(varbinder::VariableFlags::STATIC);
64 }
65
IsVariableGetterSetter(const varbinder::Variable * var)66 bool ETSChecker::IsVariableGetterSetter(const varbinder::Variable *var)
67 {
68 return var->TsType() != nullptr && var->TsType()->HasTypeFlag(TypeFlag::GETTER_SETTER);
69 }
70
LogUnresolvedReferenceError(ir::Identifier * const ident)71 void ETSChecker::LogUnresolvedReferenceError(ir::Identifier *const ident)
72 {
73 LogTypeError({"Unresolved reference ", ident->Name()}, ident->Start());
74 }
75
WrongContextErrorClassifyByType(ir::Identifier * ident)76 void ETSChecker::WrongContextErrorClassifyByType(ir::Identifier *ident)
77 {
78 std::string identCategoryName;
79 switch (static_cast<varbinder::VariableFlags>(
80 ident->Variable()->Flags() &
81 (varbinder::VariableFlags::CLASS_OR_INTERFACE_OR_ENUM | varbinder::VariableFlags::METHOD))) {
82 case varbinder::VariableFlags::CLASS: {
83 identCategoryName = "Class";
84 break;
85 }
86 case varbinder::VariableFlags::METHOD: {
87 identCategoryName = "Function";
88 break;
89 }
90 case varbinder::VariableFlags::INTERFACE: {
91 identCategoryName = "Interface";
92 break;
93 }
94 case varbinder::VariableFlags::ENUM_LITERAL: {
95 identCategoryName = "Enum";
96 break;
97 }
98 default: {
99 LogUnresolvedReferenceError(ident);
100 return;
101 }
102 }
103 LogTypeError({identCategoryName.c_str(), " name \"", ident->Name(), "\" used in the wrong context"},
104 ident->Start());
105 }
106
NotResolvedError(ir::Identifier * const ident,const varbinder::Variable * classVar,const ETSObjectType * classType)107 void ETSChecker::NotResolvedError(ir::Identifier *const ident, const varbinder::Variable *classVar,
108 const ETSObjectType *classType)
109 {
110 if (classVar == nullptr) {
111 LogUnresolvedReferenceError(ident);
112 return;
113 }
114
115 if (IsVariableStatic(classVar)) {
116 LogTypeError(
117 {"Static property '", ident->Name(), "' must be accessed through it's class '", classType->Name(), "'"},
118 ident->Start());
119 } else {
120 LogTypeError({"Property '", ident->Name(), "' must be accessed through 'this'"}, ident->Start());
121 }
122 }
123
GetTargetIdentifierAndType(ir::Identifier * const ident)124 std::pair<const ir::Identifier *, ir::TypeNode *> ETSChecker::GetTargetIdentifierAndType(ir::Identifier *const ident)
125 {
126 if (ident->Parent()->IsClassProperty()) {
127 const auto *const classProp = ident->Parent()->AsClassProperty();
128 ASSERT(classProp->Value() && classProp->Value() == ident);
129 return std::make_pair(classProp->Key()->AsIdentifier(), classProp->TypeAnnotation());
130 }
131 const auto *const variableDecl = ident->Parent()->AsVariableDeclarator();
132 ASSERT(variableDecl->Init() && variableDecl->Init() == ident);
133 return std::make_pair(variableDecl->Id()->AsIdentifier(), variableDecl->Id()->AsIdentifier()->TypeAnnotation());
134 }
135
ExtraCheckForResolvedError(ir::Identifier * const ident)136 varbinder::Variable *ETSChecker::ExtraCheckForResolvedError(ir::Identifier *const ident)
137 {
138 const auto [class_var, class_type] = FindVariableInClassOrEnclosing(ident->Name(), Context().ContainingClass());
139 auto *parentClass = FindAncestorGivenByType(ident, ir::AstNodeType::CLASS_DEFINITION);
140 if (parentClass != nullptr && parentClass->AsClassDefinition()->IsLocal()) {
141 if (parentClass != class_type->GetDeclNode()) {
142 LogTypeError({"Property '", ident->Name(), "' of enclosing class '", class_type->Name(),
143 "' is not allowed to be captured from the local class '",
144 parentClass->AsClassDefinition()->Ident()->Name(), "'"},
145 ident->Start());
146 }
147 }
148 NotResolvedError(ident, class_var, class_type);
149 return class_var;
150 }
151
SaveCapturedVariableInLocalClass(varbinder::Variable * const var,ir::Identifier * ident)152 bool ETSChecker::SaveCapturedVariableInLocalClass(varbinder::Variable *const var, ir::Identifier *ident)
153 {
154 const auto &pos = ident->Start();
155
156 if (!HasStatus(CheckerStatus::IN_LOCAL_CLASS)) {
157 return false;
158 }
159
160 if (!var->HasFlag(varbinder::VariableFlags::LOCAL)) {
161 return false;
162 }
163
164 LOG(DEBUG, ES2PANDA) << "Checking variable (line:" << pos.line << "): " << var->Name();
165 auto *scopeIter = Scope();
166 bool inStaticMethod = false;
167
168 auto captureVariable = [this, var, ident, &scopeIter, &inStaticMethod, &pos]() {
169 if (inStaticMethod) {
170 LogTypeError({"Not allowed to capture variable '", var->Name(), "' in static method"}, pos);
171 return false;
172 }
173 if (scopeIter->Node()->AsClassDefinition()->CaptureVariable(var)) {
174 LOG(DEBUG, ES2PANDA) << " Captured in class:" << scopeIter->Node()->AsClassDefinition()->Ident()->Name();
175 }
176
177 auto *parent = ident->Parent();
178
179 if (parent->IsVariableDeclarator()) {
180 parent = parent->Parent()->Parent();
181 }
182
183 if (!(parent->IsUpdateExpression() ||
184 (parent->IsAssignmentExpression() && parent->AsAssignmentExpression()->Left() == ident)) ||
185 var->Declaration() == nullptr) {
186 return false;
187 }
188
189 if (var->Declaration()->IsParameterDecl()) {
190 LOG(DEBUG, ES2PANDA) << " - Modified parameter ";
191 scopeIter->Node()->AsClassDefinition()->AddToLocalVariableIsNeeded(var);
192 }
193 return true;
194 };
195
196 while (scopeIter != var->GetScope()) {
197 if (scopeIter->Node() != nullptr) {
198 if (scopeIter->Node()->IsScriptFunction() && scopeIter->Node()->AsScriptFunction()->IsStatic()) {
199 inStaticMethod = true;
200 }
201
202 if (scopeIter->Node()->IsClassDefinition()) {
203 captureVariable();
204 return true;
205 }
206 }
207 scopeIter = scopeIter->Parent();
208 }
209
210 return false;
211 }
212
SaveCapturedVariable(varbinder::Variable * const var,ir::Identifier * ident)213 void ETSChecker::SaveCapturedVariable(varbinder::Variable *const var, ir::Identifier *ident)
214 {
215 const auto &pos = ident->Start();
216
217 if (!HasStatus(CheckerStatus::IN_LAMBDA)) {
218 return;
219 }
220
221 if (var->HasFlag(varbinder::VariableFlags::PROPERTY)) {
222 Context().AddCapturedVar(var, pos);
223 return;
224 }
225
226 if ((!var->HasFlag(varbinder::VariableFlags::LOCAL) && !var->HasFlag(varbinder::VariableFlags::METHOD)) ||
227 Context().ContainingLambda()->IsVarFromSubscope(var)) {
228 return;
229 }
230
231 const auto *scopeIter = Scope();
232 while (scopeIter != var->GetScope()) {
233 if (scopeIter->IsFunctionScope()) {
234 Context().AddCapturedVar(var, pos);
235 return;
236 }
237 scopeIter = scopeIter->Parent();
238 }
239 }
240
ResolveIdentifier(ir::Identifier * ident)241 Type *ETSChecker::ResolveIdentifier(ir::Identifier *ident)
242 {
243 if (ident->Variable() != nullptr) {
244 auto *const resolved = ident->Variable();
245 SaveCapturedVariable(resolved, ident);
246 return GetTypeOfVariable(resolved);
247 }
248
249 auto options = ident->Parent()->IsTSTypeAliasDeclaration() ? varbinder::ResolveBindingOptions::TYPE_ALIASES
250 : varbinder::ResolveBindingOptions::ALL_NON_TYPE;
251
252 auto *resolved = FindVariableInFunctionScope(ident->Name(), options);
253 if (resolved == nullptr) {
254 // If the reference is not found already in the current class, then it is not bound to the class, so we have to
255 // find the reference in the global class first, then in the global scope
256 resolved = FindVariableInGlobal(ident, options);
257 if (UNLIKELY(resolved == nullptr && debugInfoPlugin_ != nullptr)) {
258 resolved = debugInfoPlugin_->FindIdentifier(ident);
259 }
260 }
261
262 if (resolved == nullptr) {
263 resolved = ExtraCheckForResolvedError(ident);
264 ident->SetVariable(resolved);
265 if (resolved == nullptr) {
266 return GlobalTypeError();
267 }
268 return GetTypeOfVariable(resolved);
269 }
270
271 ident->SetVariable(resolved);
272 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
273 ValidateResolvedIdentifier(ident);
274
275 ValidatePropertyAccess(resolved, Context().ContainingClass(), ident->Start());
276 SaveCapturedVariable(resolved, ident);
277
278 return GetTypeOfVariable(resolved);
279 }
280
CheckLeftRightType(checker::ETSChecker * checker,checker::Type * unboxedL,checker::Type * unboxedR)281 std::optional<checker::Type *> CheckLeftRightType(checker::ETSChecker *checker, checker::Type *unboxedL,
282 checker::Type *unboxedR)
283 {
284 if (unboxedL->IsDoubleType() || unboxedR->IsDoubleType()) {
285 return checker->GlobalDoubleType();
286 }
287 if (unboxedL->IsFloatType() || unboxedR->IsFloatType()) {
288 return checker->GlobalFloatType();
289 }
290 if (unboxedL->IsLongType() || unboxedR->IsLongType()) {
291 return checker->GlobalLongType();
292 }
293 if (unboxedL->IsIntType() || unboxedR->IsIntType() || unboxedL->IsCharType() || unboxedR->IsCharType()) {
294 return checker->GlobalIntType();
295 }
296 if (unboxedL->IsShortType() || unboxedR->IsShortType()) {
297 return checker->GlobalShortType();
298 }
299 if (unboxedL->IsByteType() || unboxedR->IsByteType()) {
300 return checker->GlobalByteType();
301 }
302 return std::nullopt;
303 }
ApplyConditionalOperatorPromotion(checker::ETSChecker * checker,checker::Type * unboxedL,checker::Type * unboxedR)304 checker::Type *ETSChecker::ApplyConditionalOperatorPromotion(checker::ETSChecker *checker, checker::Type *unboxedL,
305 checker::Type *unboxedR)
306 {
307 if ((unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedL->IsIntType()) ||
308 (unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedR->IsIntType())) {
309 int value = unboxedL->IsIntType() ? unboxedL->AsIntType()->GetValue() : unboxedR->AsIntType()->GetValue();
310 checker::Type *otherType = !unboxedL->IsIntType() ? unboxedL : unboxedR;
311
312 switch (checker::ETSChecker::ETSType(otherType)) {
313 case checker::TypeFlag::BYTE:
314 case checker::TypeFlag::CHAR: {
315 if (value <= static_cast<int>(std::numeric_limits<char>::max()) &&
316 value >= static_cast<int>(std::numeric_limits<char>::min())) {
317 return checker->GetNonConstantType(otherType);
318 }
319 break;
320 }
321 case checker::TypeFlag::SHORT: {
322 if (value <= std::numeric_limits<int16_t>::max() && value >= std::numeric_limits<int16_t>::min()) {
323 return checker->GlobalShortType();
324 }
325 break;
326 }
327 default: {
328 return otherType;
329 }
330 }
331 return checker->GlobalIntType();
332 }
333
334 auto checkLeftRight = CheckLeftRightType(checker, unboxedL, unboxedR);
335 if (checkLeftRight.has_value()) {
336 return checkLeftRight.value();
337 }
338 UNREACHABLE();
339 }
340
ApplyUnaryOperatorPromotion(Type * type,const bool createConst,const bool doPromotion,const bool isCondExpr)341 Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, const bool createConst, const bool doPromotion,
342 const bool isCondExpr)
343 {
344 Type *unboxedType = isCondExpr ? MaybeUnboxConditionalInRelation(type) : MaybeUnboxInRelation(type);
345
346 if (unboxedType == nullptr) {
347 return nullptr;
348 }
349 if (doPromotion) {
350 switch (ETSType(unboxedType)) {
351 case TypeFlag::BYTE:
352 case TypeFlag::SHORT:
353 case TypeFlag::CHAR: {
354 if (!createConst) {
355 return GlobalIntType();
356 }
357
358 return CreateIntTypeFromType(unboxedType);
359 }
360 default: {
361 break;
362 }
363 }
364 }
365 return unboxedType;
366 }
367
IsNullLikeOrVoidExpression(const ir::Expression * expr) const368 bool ETSChecker::IsNullLikeOrVoidExpression(const ir::Expression *expr) const
369 {
370 // NOTE(vpukhov): #19701 void refactoring
371 return expr->TsType()->DefinitelyETSNullish() || expr->TsType()->IsETSVoidType();
372 }
373
IsResolvedAndValue(const ir::Expression * expr,Type * type) const374 std::tuple<bool, bool> ETSChecker::IsResolvedAndValue(const ir::Expression *expr, Type *type) const
375 {
376 auto [isResolve, isValue] =
377 IsNullLikeOrVoidExpression(expr) ? std::make_tuple(true, false) : type->ResolveConditionExpr();
378
379 const Type *tsType = expr->TsType();
380 if (tsType->DefinitelyNotETSNullish() && !type->IsETSPrimitiveType()) {
381 isResolve = true;
382 isValue = true;
383 }
384 return std::make_tuple(isResolve, isValue);
385 }
386
HandleBooleanLogicalOperatorsExtended(Type * leftType,Type * rightType,ir::BinaryExpression * expr)387 Type *ETSChecker::HandleBooleanLogicalOperatorsExtended(Type *leftType, Type *rightType, ir::BinaryExpression *expr)
388 {
389 ASSERT(leftType->IsConditionalExprType() && rightType->IsConditionalExprType());
390
391 auto [resolveLeft, leftValue] = IsResolvedAndValue(expr->Left(), leftType);
392 auto [resolveRight, rightValue] = IsResolvedAndValue(expr->Right(), rightType);
393
394 if (!resolveLeft && !resolveRight && IsTypeIdenticalTo(leftType, rightType)) {
395 return leftType;
396 }
397
398 switch (expr->OperatorType()) {
399 case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
400 if (leftValue) {
401 expr->SetResult(expr->Left());
402 return leftType->IsETSBooleanType() ? CreateETSBooleanType(true) : leftType;
403 }
404
405 expr->SetResult(expr->Right());
406 return rightType->IsETSBooleanType() && resolveRight ? CreateETSBooleanType(rightValue) : rightType;
407 }
408 case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
409 if (leftValue) {
410 expr->SetResult(expr->Right());
411 return rightType->IsETSBooleanType() && resolveRight ? CreateETSBooleanType(rightValue) : rightType;
412 }
413
414 expr->SetResult(expr->Left());
415 return leftType->IsETSBooleanType() ? CreateETSBooleanType(false) : leftType;
416 }
417 default: {
418 break;
419 }
420 }
421
422 UNREACHABLE();
423 }
424
HandleBooleanLogicalOperators(Type * leftType,Type * rightType,lexer::TokenType tokenType)425 Type *ETSChecker::HandleBooleanLogicalOperators(Type *leftType, Type *rightType, lexer::TokenType tokenType)
426 {
427 using UType = typename ETSBooleanType::UType;
428 ASSERT(leftType->IsETSBooleanType() && rightType->IsETSBooleanType());
429
430 if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
431 return GlobalETSBooleanType();
432 }
433
434 UType leftValue = leftType->AsETSBooleanType()->GetValue();
435 UType rightValue = rightType->AsETSBooleanType()->GetValue();
436
437 switch (tokenType) {
438 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
439 return CreateETSBooleanType(leftValue ^ rightValue);
440 }
441 case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
442 return CreateETSBooleanType((static_cast<uint8_t>(leftValue) & static_cast<uint8_t>(rightValue)) != 0);
443 }
444 case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
445 return CreateETSBooleanType((static_cast<uint8_t>(leftValue) | static_cast<uint8_t>(rightValue)) != 0);
446 }
447 case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
448 return CreateETSBooleanType(leftValue || rightValue);
449 }
450 case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
451 return CreateETSBooleanType(leftValue && rightValue);
452 }
453 default: {
454 break;
455 }
456 }
457
458 UNREACHABLE();
459 return nullptr;
460 }
461
ResolveReturnStatement(checker::Type * funcReturnType,checker::Type * argumentType,ir::ScriptFunction * containingFunc,ir::ReturnStatement * st)462 void ETSChecker::ResolveReturnStatement(checker::Type *funcReturnType, checker::Type *argumentType,
463 ir::ScriptFunction *containingFunc, ir::ReturnStatement *st)
464 {
465 if (funcReturnType->IsETSReferenceType() || argumentType->IsETSReferenceType()) {
466 // function return type should be of reference (object) type
467 Relation()->SetFlags(checker::TypeRelationFlag::NONE);
468
469 if (!argumentType->IsETSReferenceType()) {
470 argumentType = MaybeBoxInRelation(argumentType);
471 if (argumentType == nullptr) {
472 LogTypeError("Invalid return statement expression", st->Argument()->Start());
473 } else {
474 st->Argument()->AddBoxingUnboxingFlags(GetBoxingFlag(argumentType));
475 }
476 }
477
478 if (!funcReturnType->IsETSReferenceType()) {
479 funcReturnType = MaybeBoxInRelation(funcReturnType);
480 if (funcReturnType == nullptr) {
481 LogTypeError("Invalid return function expression", st->Start());
482 }
483 }
484 if (argumentType != nullptr && funcReturnType != nullptr) {
485 funcReturnType = CreateETSUnionType({funcReturnType, argumentType});
486 containingFunc->Signature()->SetReturnType(funcReturnType);
487 containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
488 }
489 } else if (funcReturnType->IsETSPrimitiveType() && argumentType->IsETSPrimitiveType()) {
490 // function return type is of primitive type (including enums):
491 Relation()->SetFlags(checker::TypeRelationFlag::DIRECT_RETURN |
492 checker::TypeRelationFlag::IN_ASSIGNMENT_CONTEXT |
493 checker::TypeRelationFlag::ASSIGNMENT_CONTEXT);
494 if (Relation()->IsAssignableTo(funcReturnType, argumentType)) {
495 funcReturnType = argumentType;
496 containingFunc->Signature()->SetReturnType(funcReturnType);
497 containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
498 } else if (!Relation()->IsAssignableTo(argumentType, funcReturnType)) {
499 const Type *targetType = TryGettingFunctionTypeFromInvokeFunction(funcReturnType);
500 const Type *sourceType = TryGettingFunctionTypeFromInvokeFunction(argumentType);
501
502 LogTypeError({"Function cannot have different primitive return types, require '", targetType, "', found '",
503 sourceType, "'"},
504 st->Argument()->Start());
505 }
506 } else {
507 // Should never in this branch.
508 UNREACHABLE();
509 return;
510 }
511 }
512
CheckArrayElements(ir::ArrayExpression * init)513 checker::Type *ETSChecker::CheckArrayElements(ir::ArrayExpression *init)
514 {
515 ArenaVector<checker::Type *> elementTypes(Allocator()->Adapter());
516 for (auto e : init->AsArrayExpression()->Elements()) {
517 Type *eType = e->Check(this);
518 if (eType->HasTypeFlag(TypeFlag::TYPE_ERROR)) {
519 return eType;
520 }
521 elementTypes.push_back(GetNonConstantType(eType));
522 }
523
524 if (elementTypes.empty()) {
525 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
526 return Allocator()->New<ETSArrayType>(GlobalETSObjectType());
527 }
528 auto const isNumeric = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); };
529 auto const isChar = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::CHAR); };
530 auto const elementType =
531 std::all_of(elementTypes.begin(), elementTypes.end(), isNumeric)
532 ? std::all_of(elementTypes.begin(), elementTypes.end(), isChar) ? GlobalCharType() : GlobalDoubleType()
533 : CreateETSUnionType(std::move(elementTypes));
534
535 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
536 return Allocator()->New<ETSArrayType>(elementType);
537 }
538
InferAliasLambdaType(ir::TypeNode * localTypeAnnotation,ir::ArrowFunctionExpression * init)539 void ETSChecker::InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::ArrowFunctionExpression *init)
540 {
541 ASSERT(localTypeAnnotation != nullptr);
542
543 if (localTypeAnnotation->IsETSTypeReference()) {
544 bool isAnnotationTypeAlias = true;
545 while (localTypeAnnotation->IsETSTypeReference() && isAnnotationTypeAlias) {
546 auto *nodeVar = localTypeAnnotation->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Variable();
547 if (nodeVar == nullptr) {
548 break;
549 }
550
551 auto *node = nodeVar->Declaration()->Node();
552
553 isAnnotationTypeAlias = node->IsTSTypeAliasDeclaration();
554 if (isAnnotationTypeAlias) {
555 localTypeAnnotation = node->AsTSTypeAliasDeclaration()->TypeAnnotation();
556 }
557 }
558 }
559
560 if (localTypeAnnotation->IsETSFunctionType()) {
561 auto *const arrowFuncExpr = init;
562 ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
563 if (lambda->Params().size() == localTypeAnnotation->AsETSFunctionType()->Params().size() &&
564 NeedTypeInference(lambda)) {
565 InferTypesForLambda(lambda, localTypeAnnotation->AsETSFunctionType());
566 }
567 }
568 }
569
FixOptionalVariableType(varbinder::Variable * const bindingVar,ir::ModifierFlags flags,ir::Expression * init)570 checker::Type *ETSChecker::FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags,
571 ir::Expression *init)
572 {
573 if ((flags & ir::ModifierFlags::OPTIONAL) != 0) {
574 if (init != nullptr && bindingVar->TsType()->IsETSPrimitiveType()) {
575 init->SetBoxingUnboxingFlags(GetBoxingFlag(bindingVar->TsType()));
576 }
577 bindingVar->SetTsType(CreateETSUnionType({GlobalETSUndefinedType(), bindingVar->TsType()}));
578 }
579 return bindingVar->TsType();
580 }
581
PreferredObjectTypeFromAnnotation(checker::Type * annotationType)582 checker::Type *PreferredObjectTypeFromAnnotation(checker::Type *annotationType)
583 {
584 if (!annotationType->IsETSUnionType()) {
585 return annotationType;
586 }
587
588 checker::Type *resolvedType = nullptr;
589 for (auto constituentType : annotationType->AsETSUnionType()->ConstituentTypes()) {
590 if (constituentType->IsETSObjectType()) {
591 if (resolvedType != nullptr) {
592 return nullptr;
593 }
594 resolvedType = constituentType;
595 }
596 }
597
598 return resolvedType;
599 }
600
CheckInit(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,checker::Type * annotationType,varbinder::Variable * const bindingVar)601 bool ETSChecker::CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init,
602 checker::Type *annotationType, varbinder::Variable *const bindingVar)
603 {
604 if (typeAnnotation == nullptr) {
605 if (init->IsArrayExpression()) {
606 annotationType = CheckArrayElements(init->AsArrayExpression());
607 bindingVar->SetTsType(annotationType);
608 }
609
610 if (init->IsObjectExpression()) {
611 LogTypeError(
612 {"Cannot infer type for ", ident->Name(), " because class composite needs an explicit target type"},
613 ident->Start());
614 return false;
615 }
616 }
617
618 if (init->IsMemberExpression() && init->AsMemberExpression()->Object()->IsObjectExpression()) {
619 LogTypeError({"Class composite must be constructed separately before referring their members."},
620 ident->Start());
621 }
622
623 if (annotationType != nullptr && annotationType->HasTypeFlag(TypeFlag::TYPE_ERROR)) {
624 return false;
625 }
626
627 if ((init->IsMemberExpression()) && (annotationType != nullptr)) {
628 SetArrayPreferredTypeForNestedMemberExpressions(init->AsMemberExpression(), annotationType);
629 }
630
631 if (init->IsArrayExpression() && (annotationType != nullptr) && !annotationType->IsETSDynamicType()) {
632 if (annotationType->IsETSTupleType()) {
633 if (!ValidateTupleMinElementSize(init->AsArrayExpression(), annotationType->AsETSTupleType())) {
634 return false;
635 }
636 }
637
638 init->AsArrayExpression()->SetPreferredType(annotationType);
639 }
640
641 if (init->IsObjectExpression() && annotationType != nullptr) {
642 init->AsObjectExpression()->SetPreferredType(PreferredObjectTypeFromAnnotation(annotationType));
643 }
644
645 if (typeAnnotation != nullptr && init->IsArrowFunctionExpression()) {
646 InferAliasLambdaType(typeAnnotation, init->AsArrowFunctionExpression());
647 }
648
649 return true;
650 }
CheckEnumType(ir::Expression * init,checker::Type * initType,const util::StringView & varName)651 void ETSChecker::CheckEnumType(ir::Expression *init, checker::Type *initType, const util::StringView &varName)
652 {
653 if (initType->IsETSObjectType() && initType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::ENUM) &&
654 !init->IsMemberExpression()) {
655 LogTypeError({"Cannot assign type '", initType->AsETSObjectType()->Name(), "' for variable ", varName, "."},
656 init->Start());
657 }
658 }
659
NeedWideningBasedOnInitializerHeuristics(ir::Expression * e)660 static bool NeedWideningBasedOnInitializerHeuristics(ir::Expression *e)
661 {
662 // NOTE: need to be done by smart casts. Return true if we need to infer wider type.
663 if (e->IsUnaryExpression()) {
664 return NeedWideningBasedOnInitializerHeuristics(e->AsUnaryExpression()->Argument());
665 }
666 const bool isConstInit =
667 e->IsIdentifier() && e->Variable() != nullptr && e->Variable()->Declaration()->IsConstDecl();
668
669 return e->IsConditionalExpression() || e->IsLiteral() || isConstInit;
670 }
671
672 // Isolated until 'constant' types are tracked in some cases
ShouldPreserveConstantTypeInVariableDeclaration(Type * annotation,Type * init)673 static bool ShouldPreserveConstantTypeInVariableDeclaration(Type *annotation, Type *init)
674 {
675 auto const isNumericWithConstTracking = [](Type *type) {
676 return type->HasTypeFlag(TypeFlag::ETS_NUMERIC) || type->IsCharType();
677 };
678
679 return ((isNumericWithConstTracking(init) && isNumericWithConstTracking(annotation)) ||
680 (init->IsETSStringType() && annotation->IsETSStringType()));
681 }
682
CheckVariableDeclaration(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,ir::ModifierFlags const flags)683 checker::Type *ETSChecker::CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *typeAnnotation,
684 ir::Expression *init, ir::ModifierFlags const flags)
685 {
686 const util::StringView &varName = ident->Name();
687 ASSERT(ident->Variable());
688 varbinder::Variable *const bindingVar = ident->Variable();
689 checker::Type *annotationType = nullptr;
690
691 const bool isConst = (flags & ir::ModifierFlags::CONST) != 0;
692 const bool isReadonly = (flags & ir::ModifierFlags::READONLY) != 0;
693 const bool isStatic = (flags & ir::ModifierFlags::STATIC) != 0;
694 // Note(lujiahui): It should be checked if the readonly function parameter and readonly number[] parameters
695 // are assigned with CONSTANT, which would not be correct. (After feature supported)
696 const bool omitConstInit = isConst || (isReadonly && isStatic);
697
698 if (typeAnnotation != nullptr) {
699 annotationType = typeAnnotation->GetType(this);
700 bindingVar->SetTsType(annotationType);
701 }
702
703 if (init == nullptr) {
704 return FixOptionalVariableType(bindingVar, flags, init);
705 }
706
707 if (!CheckInit(ident, typeAnnotation, init, annotationType, bindingVar)) {
708 return GlobalTypeError();
709 }
710
711 checker::Type *initType = init->Check(this);
712
713 if (initType == nullptr) {
714 LogTypeError("Cannot get the expression type", init->Start());
715 return GlobalTypeError();
716 }
717
718 if (typeAnnotation == nullptr && initType->IsETSFunctionType()) {
719 annotationType =
720 initType->AsETSFunctionType()->FunctionalInterface() == nullptr
721 ? FunctionTypeToFunctionalInterfaceType(initType->AsETSFunctionType()->CallSignatures().front())
722 : initType->AsETSFunctionType()->FunctionalInterface();
723 bindingVar->SetTsType(annotationType);
724 }
725
726 if (annotationType != nullptr) {
727 CheckAnnotationTypeForVariableDeclaration(annotationType, annotationType->IsETSUnionType(), init, initType);
728
729 if (omitConstInit && ShouldPreserveConstantTypeInVariableDeclaration(annotationType, initType)) {
730 bindingVar->SetTsType(init->TsType());
731 }
732 return FixOptionalVariableType(bindingVar, flags, init);
733 }
734
735 CheckEnumType(init, initType, varName);
736
737 // NOTE: need to be done by smart casts
738 auto needWidening = !omitConstInit && typeAnnotation == nullptr && NeedWideningBasedOnInitializerHeuristics(init);
739 bindingVar->SetTsType(needWidening ? GetNonConstantType(initType) : initType);
740
741 return FixOptionalVariableType(bindingVar, flags, init);
742 }
743
CheckAnnotationTypeForVariableDeclaration(checker::Type * annotationType,bool isUnionFunction,ir::Expression * init,checker::Type * initType)744 void ETSChecker::CheckAnnotationTypeForVariableDeclaration(checker::Type *annotationType, bool isUnionFunction,
745 ir::Expression *init, checker::Type *initType)
746 {
747 Type *sourceType = TryGettingFunctionTypeFromInvokeFunction(initType);
748
749 if (!isUnionFunction && annotationType->IsETSUnionType()) {
750 for (auto it : annotationType->AsETSUnionType()->ConstituentTypes()) {
751 if (it->IsETSFunctionType() ||
752 (it->IsETSObjectType() && it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
753 isUnionFunction = true;
754 break;
755 }
756 }
757 }
758
759 if (!AssignmentContext(Relation(), init, initType, annotationType, init->Start(), {}, TypeRelationFlag::NO_THROW)
760 // CC-OFFNXT(G.FMT.02) project code style
761 .IsAssignable()) {
762 Type *targetType = isUnionFunction ? annotationType : TryGettingFunctionTypeFromInvokeFunction(annotationType);
763 LogTypeError({"Type '", sourceType, "' cannot be assigned to type '", targetType, "'"}, init->Start());
764 }
765 }
766
767 //==============================================================================//
768 // Smart cast support
769 //==============================================================================//
770
ResolveSmartType(checker::Type * sourceType,checker::Type * targetType)771 checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker::Type *targetType)
772 {
773 // For left-hand variable of primitive type leave it as is.
774 if (targetType->IsETSPrimitiveType()) {
775 return targetType;
776 }
777
778 // For left-hand variable of tuple type leave it as is.
779 if (targetType->IsETSTupleType()) {
780 return targetType;
781 }
782
783 // For left-hand variable of builtin type leave it as is.
784 if (targetType->IsETSObjectType() && targetType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) {
785 return targetType;
786 }
787
788 // For the Function source or target types leave the target type as is
789 // until we will be able to create the functional interface type from the source.
790 if (targetType->HasTypeFlag(TypeFlag::FUNCTION) || sourceType->HasTypeFlag(TypeFlag::FUNCTION)) {
791 return targetType;
792 }
793
794 // Nothing to do with identical types:
795 auto *nonConstSourceType = GetNonConstantType(sourceType);
796 auto *nonConstTargetType = GetNonConstantType(targetType);
797
798 if (Relation()->IsIdenticalTo(nonConstSourceType, nonConstTargetType) ||
799 Relation()->IsIdenticalTo(GlobalBuiltinJSValueType(), nonConstTargetType)) {
800 return targetType;
801 }
802
803 // For type parameter, null or undefined source type return it as is.
804 if (sourceType->IsETSTypeParameter() || sourceType->DefinitelyETSNullish()) {
805 return sourceType;
806 }
807
808 // In case of Union left-hand type we have to select the proper type from the Union
809 // Because now we have logging of errors we have to continue analyze incorrect program, for
810 // this case we change typeError to source type.
811 if (targetType->IsETSUnionType()) {
812 auto component = targetType->AsETSUnionType()->GetAssignableType(this, sourceType);
813 return component->IsTypeError() ? MaybeBoxType(sourceType) : component;
814 }
815
816 // If source is reference type, set it as the current and use it for identifier smart cast
817 if (sourceType->IsETSReferenceType()) {
818 return sourceType;
819 }
820
821 // For right-hand variable of primitive type apply boxing conversion (case: 'let x: Object = 5', then x => Int).
822 if (sourceType->IsETSPrimitiveType() && !sourceType->IsETSVoidType() && targetType->IsETSObjectType()) {
823 return MaybeBoxInRelation(sourceType);
824 }
825
826 // NOTE - it seems that all the other possible cases are assignments like:
827 // 'Object = ObjectLiteral' or smth similar ???
828 // thus for such cases also leave the target type as is.
829 // Possible errors in tests should clarify this hypothesis sooner or later :)
830 return targetType;
831 }
832
833 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestNullishCondition(Type * testedType,Type * actualType,bool const strict)834 std::pair<Type *, Type *> ETSChecker::CheckTestNullishCondition(Type *testedType, Type *actualType, bool const strict)
835 {
836 if (!strict) {
837 return RemoveNullishTypes(actualType);
838 }
839
840 if (testedType->IsETSNullType()) {
841 return {GlobalETSNullType(), RemoveNullType(actualType)};
842 }
843
844 if (testedType->IsETSUndefinedType()) {
845 return {GlobalETSUndefinedType(), RemoveUndefinedType(actualType)};
846 }
847
848 return {GlobalETSNullishType(), GetNonNullishType(actualType)};
849 }
850
851 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestObjectCondition(ETSArrayType * testedType,Type * actualType)852 std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSArrayType *testedType, Type *actualType)
853 {
854 if (actualType->IsETSUnionType()) {
855 return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType);
856 }
857
858 // Both testing and actual (smart) types are arrays. Set types according to their relation.
859 // NOTE: probably the rules of type extraction should be modified later on!
860 if (actualType->IsETSArrayType()) {
861 auto *const arrayType = actualType->AsETSArrayType();
862
863 if (Relation()->IsIdenticalTo(arrayType, testedType) ||
864 arrayType->AssemblerName() == testedType->AssemblerName()) {
865 return {testedType, GetGlobalTypesHolder()->GlobalNeverType()};
866 }
867
868 if (Relation()->IsSupertypeOf(arrayType, testedType)) {
869 return {testedType, actualType};
870 }
871
872 if (Relation()->IsSupertypeOf(testedType, arrayType)) {
873 return {testedType, actualType};
874 }
875 } else if (actualType->IsETSObjectType() && actualType->AsETSObjectType()->IsGlobalETSObjectType()) {
876 return {testedType, actualType};
877 }
878
879 return {GetGlobalTypesHolder()->GlobalNeverType(), actualType};
880 }
881
882 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestObjectCondition(ETSObjectType * testedType,Type * actualType,bool const strict)883 std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType,
884 bool const strict)
885 {
886 if (actualType->IsETSUnionType()) {
887 return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType);
888 }
889
890 // Both testing and actual (smart) types are objects. Set types according to their relation.
891 // NOTE: probably the rules of type extraction should be modified later on!
892 if (actualType->IsETSObjectType()) {
893 auto *const objectType = actualType->AsETSObjectType();
894
895 if (Relation()->IsIdenticalTo(objectType, testedType) ||
896 objectType->AssemblerName() == testedType->AssemblerName()) {
897 return {testedType, strict ? GetGlobalTypesHolder()->GlobalNeverType() : actualType};
898 }
899
900 if (Relation()->IsSupertypeOf(objectType, testedType)) {
901 return {testedType, actualType};
902 }
903
904 if (Relation()->IsSupertypeOf(testedType, objectType)) {
905 return {testedType, actualType};
906 }
907
908 return {GetGlobalTypesHolder()->GlobalNeverType(), actualType};
909 }
910
911 // NOTE: other cases (for example with functional types) will be implemented later on
912 return {testedType, actualType};
913 }
914
915 static constexpr std::size_t const VARIABLE_POSITION = 0UL;
916 static constexpr std::size_t const CONSEQUENT_TYPE_POSITION = 1UL;
917 static constexpr std::size_t const ALTERNATE_TYPE_POSITION = 2UL;
918
CheckTestSmartCastCondition(lexer::TokenType operatorType)919 void CheckerContext::CheckTestSmartCastCondition(lexer::TokenType operatorType)
920 {
921 if (operatorType != lexer::TokenType::EOS && operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_AND &&
922 operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
923 return;
924 }
925
926 auto types = ResolveSmartCastTypes();
927
928 if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
929 if (types.has_value()) {
930 auto const &variable = std::get<VARIABLE_POSITION>(*types);
931 // NOTE: now we support only cases like 'if (x != null && y == null)' but don't support different type
932 // checks for a single variable (like 'if (x != null && x instanceof string)'), because it seems that
933 // it doesn't make much sense.
934 // Can be implemented later on if the need arises.
935 if (auto [_, inserted] =
936 testSmartCasts_.emplace(variable, std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(*types),
937 std::get<ALTERNATE_TYPE_POSITION>(*types)));
938 !inserted) {
939 testSmartCasts_[variable] = {nullptr, nullptr};
940 }
941 }
942 // Clear alternate types, because now they become indefinite
943 for (auto &smartCast : testSmartCasts_) {
944 smartCast.second.second = nullptr;
945 }
946 } else if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
947 if (bool const cleanConsequent = types.has_value() ? CheckTestOrSmartCastCondition(*types) : true;
948 cleanConsequent) {
949 // Clear consequent types, because now they become indefinite
950 for (auto &smartCast : testSmartCasts_) {
951 smartCast.second.first = nullptr;
952 }
953 }
954 } else if (types.has_value()) {
955 testSmartCasts_.emplace(
956 std::get<VARIABLE_POSITION>(*types),
957 std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(*types), std::get<ALTERNATE_TYPE_POSITION>(*types)));
958 }
959
960 testCondition_ = {};
961 operatorType_ = operatorType;
962 }
963
ResolveSmartCastTypes()964 std::optional<SmartCastTuple> CheckerContext::ResolveSmartCastTypes()
965 {
966 if (testCondition_.variable == nullptr) {
967 return std::nullopt;
968 }
969
970 // Exclude processing of global variables and those captured in lambdas and modified there
971 auto const *const variableScope = testCondition_.variable->GetScope();
972 auto const topLevelVariable =
973 variableScope != nullptr ? variableScope->IsGlobalScope() ||
974 (variableScope->Parent() != nullptr && variableScope->Parent()->IsGlobalScope())
975 : false;
976 if (topLevelVariable) {
977 return std::nullopt;
978 }
979
980 ASSERT(testCondition_.testedType != nullptr);
981 // NOTE: functional types are not supported now
982 if (!testCondition_.testedType->IsETSReferenceType() ||
983 testCondition_.testedType->HasTypeFlag(TypeFlag::FUNCTION)) {
984 return std::nullopt;
985 }
986
987 auto *smartType = GetSmartCast(testCondition_.variable);
988 if (smartType == nullptr) {
989 smartType = testCondition_.variable->TsType();
990 }
991
992 auto *const checker = parent_->AsETSChecker();
993 Type *consequentType = nullptr;
994 Type *alternateType = nullptr;
995
996 if (testCondition_.testedType->DefinitelyETSNullish()) {
997 // In case of testing for 'null' and/or 'undefined' remove corresponding null-like types.
998 std::tie(consequentType, alternateType) =
999 checker->CheckTestNullishCondition(testCondition_.testedType, smartType, testCondition_.strict);
1000 } else {
1001 if (testCondition_.testedType->IsETSObjectType()) {
1002 auto *const testedType = testCondition_.testedType->AsETSObjectType();
1003 std::tie(consequentType, alternateType) =
1004 checker->CheckTestObjectCondition(testedType, smartType, testCondition_.strict);
1005 } else if (testCondition_.testedType->IsETSArrayType()) {
1006 auto *const testedType = testCondition_.testedType->AsETSArrayType();
1007 std::tie(consequentType, alternateType) = checker->CheckTestObjectCondition(testedType, smartType);
1008 } else if (testCondition_.testedType->IsETSUnionType()) {
1009 // NOTE: now we don't support 'instanceof' operation for union types?
1010 UNREACHABLE();
1011 } else {
1012 // NOTE: it seems that no more cases are possible here! :)
1013 UNREACHABLE();
1014 }
1015 }
1016
1017 return !testCondition_.negate
1018 ? std::make_optional(std::make_tuple(testCondition_.variable, consequentType, alternateType))
1019 : std::make_optional(std::make_tuple(testCondition_.variable, alternateType, consequentType));
1020 }
CheckVoidAnnotation(const ir::ETSPrimitiveType * typeAnnotation)1021 bool ETSChecker::CheckVoidAnnotation(const ir::ETSPrimitiveType *typeAnnotation)
1022 {
1023 // Void annotation is valid only when used as 'return type' , 'type parameter instantiation', 'default type'.
1024 if (typeAnnotation->GetPrimitiveType() != ir::PrimitiveType::VOID) {
1025 return true;
1026 }
1027
1028 auto parent = typeAnnotation->Parent();
1029 if (parent->IsScriptFunction() && parent->AsScriptFunction()->ReturnTypeAnnotation() == typeAnnotation) {
1030 return true;
1031 }
1032 if (parent->IsETSFunctionType() && parent->AsETSFunctionType()->ReturnType() == typeAnnotation) {
1033 return true;
1034 }
1035 if (parent->IsTSTypeParameterInstantiation() || parent->IsTSTypeParameter()) {
1036 return true;
1037 }
1038 LogTypeError({"'void' used as type annotation."}, typeAnnotation->Start());
1039 return false;
1040 }
ApplySmartCast(varbinder::Variable const * const variable,checker::Type * const smartType)1041 void ETSChecker::ApplySmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept
1042 {
1043 ASSERT(variable != nullptr);
1044 if (smartType != nullptr) {
1045 auto *variableType = variable->TsType();
1046
1047 if (Relation()->IsIdenticalTo(variableType, smartType)) {
1048 Context().RemoveSmartCast(variable);
1049 } else {
1050 Context().SetSmartCast(variable, smartType);
1051 }
1052 }
1053 }
1054
CheckTestOrSmartCastCondition(SmartCastTuple const & types)1055 bool CheckerContext::CheckTestOrSmartCastCondition(SmartCastTuple const &types)
1056 {
1057 auto *const &variable = std::get<VARIABLE_POSITION>(types);
1058 auto *const &consequentTypeNew = std::get<CONSEQUENT_TYPE_POSITION>(types);
1059 auto *const &alternateTypeNew = std::get<ALTERNATE_TYPE_POSITION>(types);
1060
1061 if (auto const it = testSmartCasts_.find(variable); it != testSmartCasts_.end()) {
1062 auto *const consequentTypeOld = it->second.first;
1063 if (consequentTypeOld == nullptr) {
1064 return true;
1065 }
1066
1067 if (consequentTypeNew != nullptr && !parent_->Relation()->IsIdenticalTo(consequentTypeOld, consequentTypeNew)) {
1068 it->second.first = parent_->AsETSChecker()->CreateETSUnionType({consequentTypeOld, consequentTypeNew});
1069 }
1070
1071 if (auto *const alternateTypeOld = it->second.second; alternateTypeOld != nullptr) {
1072 if (alternateTypeNew != nullptr &&
1073 !parent_->Relation()->IsIdenticalTo(alternateTypeOld, alternateTypeNew)) {
1074 it->second.second = parent_->AsETSChecker()->CreateETSUnionType({alternateTypeOld, alternateTypeNew});
1075 }
1076 } else {
1077 it->second.second = alternateTypeNew;
1078 }
1079
1080 return false;
1081 }
1082
1083 // NOTE: now we support only cases like 'if (x != null || y != null)' or 'if (x instanceof A || x instanceof B)'
1084 // although it seems that the resulting variable type in the second case isn't used in subsequent code directly.
1085 // More complex conditions can be implemented later on if the need arises.
1086 testSmartCasts_.emplace(variable, std::make_pair(consequentTypeNew, alternateTypeNew));
1087 return true;
1088 }
1089
1090 //==============================================================================//
1091
SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression * expr,Type * annotationType)1092 void ETSChecker::SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression *expr, Type *annotationType)
1093 {
1094 if ((expr == nullptr) || (annotationType == nullptr)) {
1095 return;
1096 }
1097
1098 if (expr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) {
1099 return;
1100 }
1101
1102 // Expand all member expressions
1103 Type *elementType = annotationType;
1104 ir::Expression *object = expr->Object();
1105 while ((object != nullptr) && (object->IsMemberExpression())) {
1106 ir::MemberExpression *memberExpr = object->AsMemberExpression();
1107 if (memberExpr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) {
1108 return;
1109 }
1110
1111 object = memberExpr->Object();
1112 elementType = CreateETSArrayType(elementType);
1113 }
1114
1115 // Set explicit target type for array
1116 if ((object != nullptr) && (object->IsArrayExpression())) {
1117 ir::ArrayExpression *array = object->AsArrayExpression();
1118 array->SetPreferredType(CreateETSArrayType(elementType));
1119 }
1120 }
1121
CollectAliasParametersForBoxing(Type * expandedAliasType,std::set<Type * > & parametersNeedToBeBoxed,bool needToBeBoxed)1122 static void CollectAliasParametersForBoxing(Type *expandedAliasType, std::set<Type *> ¶metersNeedToBeBoxed,
1123 bool needToBeBoxed)
1124 {
1125 if (expandedAliasType->IsETSTypeParameter() && needToBeBoxed) {
1126 parametersNeedToBeBoxed.insert(expandedAliasType);
1127 } else if (expandedAliasType->IsETSObjectType()) {
1128 auto objectType = expandedAliasType->AsETSObjectType();
1129 needToBeBoxed =
1130 objectType->GetDeclNode()->IsClassDefinition() || objectType->GetDeclNode()->IsTSInterfaceDeclaration();
1131 for (const auto typeArgument : objectType->TypeArguments()) {
1132 CollectAliasParametersForBoxing(typeArgument, parametersNeedToBeBoxed, needToBeBoxed);
1133 }
1134 } else if (expandedAliasType->IsETSTupleType()) {
1135 auto tupleType = expandedAliasType->AsETSTupleType();
1136 needToBeBoxed = false;
1137 for (auto type : tupleType->GetTupleTypesList()) {
1138 CollectAliasParametersForBoxing(type, parametersNeedToBeBoxed, needToBeBoxed);
1139 }
1140 } else if (expandedAliasType->IsETSArrayType()) {
1141 auto arrayType = expandedAliasType->AsETSArrayType();
1142 needToBeBoxed = false;
1143 auto elementType = arrayType->ElementType();
1144 CollectAliasParametersForBoxing(elementType, parametersNeedToBeBoxed, needToBeBoxed);
1145 } else if (expandedAliasType->IsETSUnionType()) {
1146 auto unionType = expandedAliasType->AsETSUnionType();
1147 needToBeBoxed = false;
1148 for (auto type : unionType->ConstituentTypes()) {
1149 CollectAliasParametersForBoxing(type, parametersNeedToBeBoxed, needToBeBoxed);
1150 }
1151 }
1152 }
1153
HandleTypeAlias(ir::Expression * const name,const ir::TSTypeParameterInstantiation * const typeParams)1154 Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypeParameterInstantiation *const typeParams)
1155 {
1156 ASSERT(name->IsIdentifier() && name->AsIdentifier()->Variable() &&
1157 name->AsIdentifier()->Variable()->Declaration()->IsTypeAliasDecl());
1158
1159 auto *const typeAliasNode =
1160 name->AsIdentifier()->Variable()->Declaration()->AsTypeAliasDecl()->Node()->AsTSTypeAliasDeclaration();
1161
1162 // NOTE (mmartin): modify for default params
1163 if ((typeParams == nullptr) != (typeAliasNode->TypeParams() == nullptr)) {
1164 if (typeParams == nullptr) {
1165 LogTypeError("Type alias declaration is generic, but no type parameters were provided", name->Start());
1166 return GlobalTypeError();
1167 }
1168
1169 LogTypeError("Type alias declaration is not generic, but type parameters were provided", typeParams->Start());
1170 return GlobalTypeError();
1171 }
1172
1173 if (typeParams == nullptr) {
1174 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1175 return GetReferencedTypeBase(name);
1176 }
1177
1178 for (auto *const origTypeParam : typeParams->Params()) {
1179 origTypeParam->Check(this);
1180 }
1181
1182 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1183 Type *const aliasType = GetReferencedTypeBase(name);
1184 auto *aliasSub = NewSubstitution();
1185 if (typeAliasNode->TypeParams()->Params().size() != typeParams->Params().size()) {
1186 LogTypeError("Wrong number of type parameters for generic type alias", typeParams->Start());
1187 return GlobalTypeError();
1188 }
1189
1190 std::set<Type *> parametersNeedToBeBoxed;
1191 auto expandedAliasType = aliasType->Substitute(Relation(), aliasSub);
1192 CollectAliasParametersForBoxing(expandedAliasType, parametersNeedToBeBoxed, false);
1193
1194 for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) {
1195 auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name();
1196 auto *typeAliasType = typeAliasTypeName->Variable()->TsType();
1197 if (!typeAliasType->IsETSTypeParameter()) {
1198 continue;
1199 }
1200 auto paramType = typeParams->Params().at(idx)->TsType();
1201 if (parametersNeedToBeBoxed.find(typeAliasType) != parametersNeedToBeBoxed.end()) {
1202 auto boxedType = MaybeBoxInRelation(typeParams->Params().at(idx)->GetType(this));
1203 if (boxedType != nullptr) {
1204 paramType = boxedType;
1205 }
1206 }
1207 aliasSub->insert({typeAliasType->AsETSTypeParameter(), paramType});
1208 }
1209
1210 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1211 ValidateGenericTypeAliasForClonedNode(typeAliasNode->AsTSTypeAliasDeclaration(), typeParams);
1212
1213 return aliasType->Substitute(Relation(), aliasSub);
1214 }
1215
GetNameForSynteticObjectType(const util::StringView & source)1216 std::vector<util::StringView> ETSChecker::GetNameForSynteticObjectType(const util::StringView &source)
1217 {
1218 const std::string str = source.Mutf8();
1219 std::istringstream ss {str};
1220 const char delimiter = '.';
1221 std::string token;
1222
1223 std::vector<util::StringView> syntheticName {};
1224
1225 while (std::getline(ss, token, delimiter)) {
1226 if (!token.empty()) {
1227 util::UString sV(token, Allocator());
1228 syntheticName.emplace_back(sV.View());
1229 }
1230 }
1231
1232 return syntheticName;
1233 }
1234
FindSpecifierForModuleObject(ir::ETSImportDeclaration * importDecl,util::StringView const & name)1235 std::pair<bool, util::StringView> FindSpecifierForModuleObject(ir::ETSImportDeclaration *importDecl,
1236 util::StringView const &name)
1237 {
1238 if (importDecl == nullptr) {
1239 return std::make_pair(true, util::StringView());
1240 }
1241
1242 for (auto item : importDecl->Specifiers()) {
1243 if (item->IsImportSpecifier() && item->AsImportSpecifier()->Imported()->Name().Is(name.Mutf8())) {
1244 if (!item->AsImportSpecifier()->Imported()->Name().Is(item->AsImportSpecifier()->Local()->Name().Mutf8())) {
1245 return std::make_pair(true, item->AsImportSpecifier()->Local()->Name());
1246 }
1247 return std::make_pair(true, util::StringView());
1248 }
1249 }
1250 return std::make_pair(false, util::StringView());
1251 }
1252
1253 template <checker::PropertyType TYPE>
BindingsModuleObjectAddProperty(checker::ETSObjectType * moduleObjType,ir::ETSImportDeclaration * importDecl,const varbinder::Scope::VariableMap & bindings)1254 void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleObjType,
1255 ir::ETSImportDeclaration *importDecl,
1256 const varbinder::Scope::VariableMap &bindings)
1257 {
1258 for (auto [_, var] : bindings) {
1259 (void)_;
1260 auto [found, aliasedName] = FindSpecifierForModuleObject(importDecl, var->AsLocalVariable()->Name());
1261 if ((var->AsLocalVariable()->Declaration()->Node()->IsExported() ||
1262 var->AsLocalVariable()->Declaration()->Node()->IsExportedType()) &&
1263 found) {
1264 if (!aliasedName.Empty()) {
1265 moduleObjType->AddReExportAlias(var->Declaration()->Name(), aliasedName);
1266 }
1267 moduleObjType->AddProperty<TYPE>(var->AsLocalVariable(),
1268 FindPropNameForNamespaceImport(var->AsLocalVariable()->Name()));
1269 }
1270 }
1271 }
1272
FindPropNameForNamespaceImport(const util::StringView & originalName)1273 util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringView &originalName)
1274 {
1275 if (auto relatedMapItem =
1276 VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap().find(Program()->SourceFilePath());
1277 relatedMapItem != VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap().end()) {
1278 if (auto result = std::find_if(relatedMapItem->second.begin(), relatedMapItem->second.end(),
1279 [originalName](const auto &item) { return item.second == originalName; });
1280 result != relatedMapItem->second.end()) {
1281 return result->first;
1282 }
1283 }
1284
1285 return originalName;
1286 }
1287
1288 // Helps to prevent searching for the imported file among external sources if it is the entry program
SelectEntryOrExternalProgram(varbinder::ETSBinder * etsBinder,const util::StringView & importPath)1289 static parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder,
1290 const util::StringView &importPath)
1291 {
1292 if (importPath.Is(etsBinder->GetGlobalRecordTable()->Program()->AbsoluteName().Mutf8())) {
1293 return etsBinder->GetGlobalRecordTable()->Program();
1294 }
1295
1296 auto programList = etsBinder->GetProgramList(importPath);
1297 ASSERT(!programList.empty());
1298 return programList.front();
1299 }
1300
SetPropertiesForModuleObject(checker::ETSObjectType * moduleObjType,const util::StringView & importPath,ir::ETSImportDeclaration * importDecl)1301 void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjType, const util::StringView &importPath,
1302 ir::ETSImportDeclaration *importDecl)
1303 {
1304 parser::Program *program =
1305 SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath);
1306 // Check imported properties before assigning them to module object
1307 if (!program->IsASTChecked()) {
1308 // NOTE: helps to avoid endless loop in case of recursive imports that uses all bindings
1309 program->MarkASTAsChecked();
1310 program->Ast()->Check(this);
1311 }
1312
1313 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_FIELD>(
1314 moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings());
1315
1316 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_METHOD>(
1317 moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings());
1318
1319 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1320 moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings());
1321
1322 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1323 moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings());
1324
1325 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1326 moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings());
1327 }
1328
SetrModuleObjectTsType(ir::Identifier * local,checker::ETSObjectType * moduleObjType)1329 void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType)
1330 {
1331 auto *etsBinder = static_cast<varbinder::ETSBinder *>(VarBinder());
1332
1333 for (auto [bindingName, var] : etsBinder->TopScope()->Bindings()) {
1334 if (bindingName.Is(local->Name().Mutf8())) {
1335 var->SetTsType(moduleObjType);
1336 }
1337 }
1338 }
1339
GetReferencedTypeFromBase(Type * baseType,ir::Expression * name)1340 Type *ETSChecker::GetReferencedTypeFromBase([[maybe_unused]] Type *baseType, [[maybe_unused]] ir::Expression *name)
1341 {
1342 return TypeError(name, "Invalid type reference.", name->Start());
1343 }
1344
GetReferencedTypeBase(ir::Expression * name)1345 Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name)
1346 {
1347 if (name->IsTSQualifiedName()) {
1348 return name->Check(this);
1349 }
1350
1351 ASSERT(name->IsIdentifier() && name->AsIdentifier()->Variable() != nullptr);
1352
1353 // NOTE: kbaladurin. forbid usage imported entities as types without declarations
1354 auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(name->AsIdentifier()->Variable());
1355 if (importData != nullptr && importData->import->IsPureDynamic()) {
1356 name->SetTsType(GlobalBuiltinDynamicType(importData->import->Language()));
1357 return name->TsType();
1358 }
1359
1360 auto *refVar = name->AsIdentifier()->Variable()->AsLocalVariable();
1361
1362 checker::Type *tsType = nullptr;
1363 switch (refVar->Declaration()->Node()->Type()) {
1364 case ir::AstNodeType::TS_INTERFACE_DECLARATION: {
1365 tsType = GetTypeFromInterfaceReference(refVar);
1366 break;
1367 }
1368 case ir::AstNodeType::CLASS_DECLARATION:
1369 case ir::AstNodeType::STRUCT_DECLARATION:
1370 case ir::AstNodeType::CLASS_DEFINITION: {
1371 tsType = GetTypeFromClassReference(refVar);
1372 break;
1373 }
1374 case ir::AstNodeType::TS_ENUM_DECLARATION: {
1375 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1376 tsType = GetTypeFromEnumReference(refVar);
1377 break;
1378 }
1379 case ir::AstNodeType::TS_TYPE_PARAMETER: {
1380 tsType = GetTypeFromTypeParameterReference(refVar, name->Start());
1381 break;
1382 }
1383 case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION: {
1384 tsType = GetTypeFromTypeAliasReference(refVar);
1385 break;
1386 }
1387 case ir::AstNodeType::ANNOTATION_DECLARATION: {
1388 LogTypeError("Annotations cannot be used as a type.", name->Start());
1389 tsType = GlobalTypeError();
1390 break;
1391 }
1392 default: {
1393 UNREACHABLE();
1394 }
1395 }
1396 name->SetTsType(tsType);
1397 return tsType;
1398 }
1399
ConcatConstantString(util::UString & target,Type * type)1400 void ETSChecker::ConcatConstantString(util::UString &target, Type *type)
1401 {
1402 switch (ETSType(type)) {
1403 case TypeFlag::ETS_OBJECT: {
1404 ASSERT(type->IsETSStringType());
1405 target.Append(type->AsETSStringType()->GetValue());
1406 break;
1407 }
1408 case TypeFlag::ETS_BOOLEAN: {
1409 target.Append(type->AsETSBooleanType()->GetValue() ? "true" : "false");
1410 break;
1411 }
1412 case TypeFlag::BYTE: {
1413 target.Append(std::to_string(type->AsByteType()->GetValue()));
1414 break;
1415 }
1416 case TypeFlag::CHAR: {
1417 std::string s(1, type->AsCharType()->GetValue());
1418 target.Append(s);
1419 break;
1420 }
1421 case TypeFlag::SHORT: {
1422 target.Append(std::to_string(type->AsShortType()->GetValue()));
1423 break;
1424 }
1425 case TypeFlag::INT: {
1426 target.Append(std::to_string(type->AsIntType()->GetValue()));
1427 break;
1428 }
1429 case TypeFlag::LONG: {
1430 target.Append(std::to_string(type->AsLongType()->GetValue()));
1431 break;
1432 }
1433 case TypeFlag::FLOAT: {
1434 target.Append(std::to_string(type->AsFloatType()->GetValue()));
1435 break;
1436 }
1437 case TypeFlag::DOUBLE: {
1438 target.Append(std::to_string(type->AsDoubleType()->GetValue()));
1439 break;
1440 }
1441 default: {
1442 UNREACHABLE();
1443 }
1444 }
1445 }
1446
HandleStringConcatenation(Type * leftType,Type * rightType)1447 Type *ETSChecker::HandleStringConcatenation(Type *leftType, Type *rightType)
1448 {
1449 ASSERT(leftType->IsETSStringType() || rightType->IsETSStringType());
1450
1451 if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT) ||
1452 leftType->IsETSBigIntType() || rightType->IsETSBigIntType()) {
1453 return GlobalETSStringLiteralType();
1454 }
1455
1456 util::UString concatenated(Allocator());
1457 ConcatConstantString(concatenated, leftType);
1458 ConcatConstantString(concatenated, rightType);
1459
1460 return CreateETSStringLiteralType(concatenated.View());
1461 }
1462
FindFunctionInVectorGivenByName(util::StringView name,ArenaVector<checker::ETSFunctionType * > & list)1463 checker::ETSFunctionType *ETSChecker::FindFunctionInVectorGivenByName(util::StringView name,
1464 ArenaVector<checker::ETSFunctionType *> &list)
1465 {
1466 for (auto *it : list) {
1467 if (it->Name() == name) {
1468 return it;
1469 }
1470 }
1471
1472 return nullptr;
1473 }
1474
IsFunctionContainsSignature(checker::ETSFunctionType * funcType,Signature * signature)1475 bool ETSChecker::IsFunctionContainsSignature(checker::ETSFunctionType *funcType, Signature *signature)
1476 {
1477 for (auto *it : funcType->CallSignatures()) {
1478 Relation()->IsCompatibleTo(it, signature);
1479 if (Relation()->IsTrue()) {
1480 return true;
1481 }
1482 }
1483
1484 return false;
1485 }
1486
CheckFunctionContainsClashingSignature(const checker::ETSFunctionType * funcType,Signature * signature)1487 bool ETSChecker::CheckFunctionContainsClashingSignature(const checker::ETSFunctionType *funcType, Signature *signature)
1488 {
1489 for (auto *it : funcType->CallSignatures()) {
1490 SavedTypeRelationFlagsContext strfCtx(Relation(), TypeRelationFlag::NONE);
1491 Relation()->IsCompatibleTo(it, signature);
1492 if (Relation()->IsTrue() && it->Function()->Id()->Name() == signature->Function()->Id()->Name()) {
1493 std::stringstream ss;
1494 it->ToString(ss, nullptr, true);
1495 auto sigStr1 = ss.str();
1496 ss.str(std::string {}); // Clear buffer
1497 signature->ToString(ss, nullptr, true);
1498 auto sigStr2 = ss.str();
1499 LogTypeError({"Function '", it->Function()->Id()->Name(), sigStr1.c_str(),
1500 "' is redeclared with different signature '", signature->Function()->Id()->Name(),
1501 sigStr2.c_str(), "'"},
1502 signature->Function()->ReturnTypeAnnotation()->Start());
1503 return false;
1504 }
1505 }
1506 return true;
1507 }
1508
MergeSignatures(checker::ETSFunctionType * target,checker::ETSFunctionType * source)1509 void ETSChecker::MergeSignatures(checker::ETSFunctionType *target, checker::ETSFunctionType *source)
1510 {
1511 for (auto *s : source->CallSignatures()) {
1512 if (IsFunctionContainsSignature(target, s)) {
1513 continue;
1514 }
1515
1516 if (!CheckFunctionContainsClashingSignature(target, s)) {
1517 continue;
1518 }
1519 target->AddCallSignature(s);
1520 }
1521 }
1522
MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType * > & merged,ArenaVector<checker::ETSFunctionType * > & current)1523 void ETSChecker::MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType *> &merged,
1524 ArenaVector<checker::ETSFunctionType *> ¤t)
1525 {
1526 for (auto *curr : current) {
1527 auto name = curr->Name();
1528 auto *found = FindFunctionInVectorGivenByName(name, merged);
1529 if (found != nullptr) {
1530 MergeSignatures(found, curr);
1531 continue;
1532 }
1533
1534 merged.push_back(curr);
1535 }
1536 }
1537
FindAncestorGivenByType(ir::AstNode * node,ir::AstNodeType type,const ir::AstNode * endNode)1538 ir::AstNode *ETSChecker::FindAncestorGivenByType(ir::AstNode *node, ir::AstNodeType type, const ir::AstNode *endNode)
1539 {
1540 auto *iter = node->Parent();
1541
1542 while (iter != endNode) {
1543 if (iter->Type() == type) {
1544 return iter;
1545 }
1546
1547 iter = iter->Parent();
1548 }
1549
1550 return nullptr;
1551 }
1552
GetContainingObjectNameFromSignature(Signature * signature)1553 util::StringView ETSChecker::GetContainingObjectNameFromSignature(Signature *signature)
1554 {
1555 ASSERT(signature->Function());
1556 auto *iter = signature->Function()->Parent();
1557
1558 while (iter != nullptr) {
1559 if (iter->IsClassDefinition()) {
1560 return iter->AsClassDefinition()->Ident()->Name();
1561 }
1562
1563 if (iter->IsTSInterfaceDeclaration()) {
1564 return iter->AsTSInterfaceDeclaration()->Id()->Name();
1565 }
1566
1567 iter = iter->Parent();
1568 }
1569
1570 UNREACHABLE();
1571 return {""};
1572 }
1573
FindJumpTarget(ir::AstNode * node)1574 std::optional<const ir::AstNode *> ETSChecker::FindJumpTarget(ir::AstNode *node)
1575 {
1576 ASSERT(node->IsBreakStatement() || node->IsContinueStatement());
1577
1578 bool const isContinue = node->IsContinueStatement();
1579
1580 // Look for label
1581 auto label = isContinue ? node->AsContinueStatement()->Ident() : node->AsBreakStatement()->Ident();
1582 if (label != nullptr) {
1583 auto var = label->Variable();
1584 if (var != nullptr && var->Declaration()->IsLabelDecl()) {
1585 return var->Declaration()->Node();
1586 }
1587
1588 // Failed to resolve variable for label
1589 LogUnresolvedReferenceError(label);
1590 return {};
1591 }
1592
1593 // No label, find the nearest loop or switch statement
1594 const auto *iter = node->Parent();
1595 while (iter != nullptr) {
1596 switch (iter->Type()) {
1597 case ir::AstNodeType::DO_WHILE_STATEMENT:
1598 case ir::AstNodeType::WHILE_STATEMENT:
1599 case ir::AstNodeType::FOR_UPDATE_STATEMENT:
1600 case ir::AstNodeType::FOR_OF_STATEMENT:
1601 case ir::AstNodeType::SWITCH_STATEMENT: {
1602 return iter;
1603 }
1604 default: {
1605 break;
1606 }
1607 }
1608
1609 iter = iter->Parent();
1610 }
1611
1612 LogTypeError({"Control flow redirection statement can not be used out of loop or switch statement."},
1613 node->Start());
1614 return nullptr;
1615 }
1616
GetAccessFlagFromNode(const ir::AstNode * node)1617 varbinder::VariableFlags ETSChecker::GetAccessFlagFromNode(const ir::AstNode *node)
1618 {
1619 if (node->IsPrivate()) {
1620 return varbinder::VariableFlags::PRIVATE;
1621 }
1622
1623 if (node->IsProtected()) {
1624 return varbinder::VariableFlags::PROTECTED;
1625 }
1626
1627 return varbinder::VariableFlags::PUBLIC;
1628 }
1629
CheckSwitchDiscriminant(ir::Expression * discriminant)1630 Type *ETSChecker::CheckSwitchDiscriminant(ir::Expression *discriminant)
1631 {
1632 discriminant->Check(this);
1633 auto *discriminantType = GetNonConstantType(MaybeUnboxExpression(discriminant));
1634 if (!discriminantType->HasTypeFlag(TypeFlag::VALID_SWITCH_TYPE)) {
1635 if (!(discriminantType->IsETSObjectType() &&
1636 discriminantType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_STRING |
1637 ETSObjectFlags::STRING | ETSObjectFlags::ENUM))) {
1638 LogTypeError({"Incompatible types. Found: ", discriminantType,
1639 ", required: char , byte , short , int, long , Char , Byte , Short , Int, Long , String "
1640 "or an enum type"},
1641 discriminant->Start());
1642 }
1643 }
1644
1645 return discriminantType;
1646 }
1647
AddBoxingUnboxingFlagsToNode(ir::AstNode * node,Type * boxingUnboxingType)1648 void ETSChecker::AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxingUnboxingType)
1649 {
1650 if (boxingUnboxingType->IsETSObjectType()) {
1651 node->AddBoxingUnboxingFlags(GetBoxingFlag(boxingUnboxingType));
1652 } else if (!boxingUnboxingType->IsETSUnionType()) {
1653 node->AddBoxingUnboxingFlags(GetUnboxingFlag(boxingUnboxingType));
1654 }
1655 }
1656
MaybeBoxExpression(ir::Expression * expr)1657 Type *ETSChecker::MaybeBoxExpression(ir::Expression *expr)
1658 {
1659 auto *promoted = MaybeBoxType(expr->TsType());
1660 if (promoted != expr->TsType()) {
1661 expr->AddBoxingUnboxingFlags(GetBoxingFlag(promoted));
1662 }
1663 return promoted;
1664 }
1665
MaybeUnboxExpression(ir::Expression * expr)1666 Type *ETSChecker::MaybeUnboxExpression(ir::Expression *expr)
1667 {
1668 auto *primitive = MaybeUnboxType(expr->TsType());
1669 if (primitive != expr->TsType()) {
1670 expr->AddBoxingUnboxingFlags(GetUnboxingFlag(primitive));
1671 }
1672 return primitive;
1673 }
1674
CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement * > const & cases)1675 void ETSChecker::CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement *> const &cases)
1676 {
1677 CheckItemCasesConstant(cases);
1678 CheckItemCasesDuplicate(cases);
1679 }
1680
GetStringFromIdentifierValue(checker::Type * caseType) const1681 std::string ETSChecker::GetStringFromIdentifierValue(checker::Type *caseType) const
1682 {
1683 const auto identifierTypeKind = ETSChecker::TypeKind(caseType);
1684 switch (identifierTypeKind) {
1685 case TypeFlag::BYTE: {
1686 return std::to_string(caseType->AsByteType()->GetValue());
1687 }
1688 case TypeFlag::SHORT: {
1689 return std::to_string(caseType->AsShortType()->GetValue());
1690 }
1691 case TypeFlag::CHAR: {
1692 return std::to_string(caseType->AsCharType()->GetValue());
1693 }
1694 case TypeFlag::INT: {
1695 return std::to_string(caseType->AsIntType()->GetValue());
1696 }
1697 case TypeFlag::LONG: {
1698 return std::to_string(caseType->AsLongType()->GetValue());
1699 }
1700 case TypeFlag::ETS_OBJECT: {
1701 VarBinder()->ThrowError(caseType->AsETSObjectType()->Variable()->Declaration()->Node()->Start(),
1702 "not implemented");
1703 }
1704 default: {
1705 UNREACHABLE();
1706 }
1707 }
1708 }
1709
IsConstantMemberOrIdentifierExpression(ir::Expression * expression)1710 bool IsConstantMemberOrIdentifierExpression(ir::Expression *expression)
1711 {
1712 if (expression->IsMemberExpression()) {
1713 auto *var = expression->AsMemberExpression()->PropVar();
1714 return var->Declaration()->IsConstDecl() ||
1715 (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC));
1716 }
1717
1718 if (expression->IsIdentifier()) {
1719 auto *var = expression->AsIdentifier()->Variable();
1720 return var->Declaration()->IsConstDecl() ||
1721 (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC));
1722 }
1723
1724 return false;
1725 }
1726
CheckItemCasesConstant(ArenaVector<ir::SwitchCaseStatement * > const & cases)1727 void ETSChecker::CheckItemCasesConstant(ArenaVector<ir::SwitchCaseStatement *> const &cases)
1728 {
1729 for (auto &it : cases) {
1730 auto *caseTest = it->Test();
1731 if (caseTest == nullptr) {
1732 continue;
1733 }
1734 auto *caseType = caseTest->TsType();
1735 if (caseTest->TsType()->IsETSEnumType()) {
1736 continue;
1737 }
1738
1739 if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) {
1740 if (!IsConstantMemberOrIdentifierExpression(caseTest)) {
1741 LogTypeError("Constant expression required", it->Start());
1742 continue;
1743 }
1744
1745 if (!caseType->HasTypeFlag(checker::TypeFlag::VALID_SWITCH_TYPE)) {
1746 LogTypeError("Unexpected type " + caseType->ToString(), it->Start());
1747 continue;
1748 }
1749 continue;
1750 }
1751 }
1752 }
1753
CheckItemEnumType(ir::Expression const * const caseTest,ETSChecker * checker,ETSIntEnumType const * const type,bool & isDup)1754 void CheckItemEnumType(ir::Expression const *const caseTest, ETSChecker *checker, ETSIntEnumType const *const type,
1755 bool &isDup)
1756 {
1757 if (caseTest->TsType()->AsETSIntEnumType()->IsSameEnumLiteralType(type)) {
1758 isDup = true;
1759 checker->LogTypeError("Case duplicate", caseTest->Start());
1760 }
1761 }
1762
CheckItemStringEnumType(ir::Expression const * const caseTest,ETSChecker * checker,ETSStringEnumType const * const type,bool & isDup)1763 void CheckItemStringEnumType(ir::Expression const *const caseTest, ETSChecker *checker,
1764 ETSStringEnumType const *const type, bool &isDup)
1765 {
1766 if (caseTest->TsType()->AsETSStringEnumType()->IsSameEnumLiteralType(type)) {
1767 isDup = true;
1768 checker->LogTypeError("Case duplicate", caseTest->Start());
1769 }
1770 }
1771
CheckItemCasesDuplicate(ArenaVector<ir::SwitchCaseStatement * > const & cases)1772 void ETSChecker::CheckItemCasesDuplicate(ArenaVector<ir::SwitchCaseStatement *> const &cases)
1773 {
1774 for (size_t caseNum = 0; caseNum < cases.size(); caseNum++) {
1775 bool isItemDuplicate = false;
1776 for (size_t compareCase = caseNum + 1; compareCase < cases.size(); compareCase++) {
1777 auto *caseTest = cases.at(caseNum)->Test();
1778 auto *compareCaseTest = cases.at(compareCase)->Test();
1779
1780 if (caseTest == nullptr || compareCaseTest == nullptr) {
1781 continue;
1782 }
1783
1784 if (caseTest->TsType()->IsETSIntEnumType()) {
1785 CheckItemEnumType(caseTest, this, compareCaseTest->TsType()->AsETSIntEnumType(), isItemDuplicate);
1786 continue;
1787 }
1788
1789 if (caseTest->TsType()->IsETSStringEnumType()) {
1790 CheckItemStringEnumType(caseTest, this, compareCaseTest->TsType()->AsETSStringEnumType(),
1791 isItemDuplicate);
1792 continue;
1793 }
1794
1795 if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) {
1796 CheckIdentifierSwitchCase(caseTest, compareCaseTest, cases.at(caseNum)->Start());
1797 continue;
1798 }
1799
1800 if (compareCaseTest->IsIdentifier() || compareCaseTest->IsMemberExpression()) {
1801 CheckIdentifierSwitchCase(compareCaseTest, caseTest, cases.at(compareCase)->Start());
1802 continue;
1803 }
1804
1805 if (caseTest->IsLiteral() && compareCaseTest->IsLiteral() &&
1806 GetStringFromLiteral(caseTest) != GetStringFromLiteral(compareCaseTest)) {
1807 continue;
1808 }
1809
1810 if (!(IsConstantExpression(caseTest, caseTest->TsType()) || caseTest->IsLiteral()) ||
1811 !(IsConstantExpression(compareCaseTest, compareCaseTest->TsType()) || compareCaseTest->IsLiteral())) {
1812 continue;
1813 }
1814
1815 if (!isItemDuplicate) {
1816 isItemDuplicate = true;
1817 LogTypeError("Case duplicate", cases.at(compareCase)->Start());
1818 }
1819 }
1820 }
1821 }
1822
CompareIdentifiersValuesAreDifferent(ir::Expression * compareValue,const std::string & caseValue)1823 bool ETSChecker::CompareIdentifiersValuesAreDifferent(ir::Expression *compareValue, const std::string &caseValue)
1824 {
1825 if (IsConstantMemberOrIdentifierExpression(compareValue)) {
1826 checker::Type *compareCaseType = compareValue->TsType();
1827
1828 const auto compareCaseValue = GetStringFromIdentifierValue(compareCaseType);
1829 return caseValue != compareCaseValue;
1830 }
1831
1832 return caseValue != GetStringFromLiteral(compareValue);
1833 }
1834
CheckIdentifierSwitchCase(ir::Expression * currentCase,ir::Expression * compareCase,const lexer::SourcePosition & pos)1835 void ETSChecker::CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase,
1836 const lexer::SourcePosition &pos)
1837 {
1838 currentCase->Check(this);
1839
1840 if (!IsConstantMemberOrIdentifierExpression(currentCase)) {
1841 return;
1842 }
1843
1844 checker::Type *caseType = currentCase->TsType();
1845
1846 if (!caseType->HasTypeFlag(checker::TypeFlag::VALID_SWITCH_TYPE)) {
1847 return;
1848 }
1849
1850 if (!CompareIdentifiersValuesAreDifferent(compareCase, GetStringFromIdentifierValue(caseType))) {
1851 LogTypeError("Variable has same value with another switch case", pos);
1852 return;
1853 }
1854 }
1855
GetStringFromLiteral(ir::Expression * caseTest) const1856 std::string ETSChecker::GetStringFromLiteral(ir::Expression *caseTest) const
1857 {
1858 switch (caseTest->Type()) {
1859 case ir::AstNodeType::CHAR_LITERAL: {
1860 return std::to_string(caseTest->AsCharLiteral()->Char());
1861 }
1862 case ir::AstNodeType::STRING_LITERAL:
1863 case ir::AstNodeType::NUMBER_LITERAL: {
1864 return util::Helpers::LiteralToPropName(caseTest).Mutf8();
1865 }
1866 default:
1867 UNREACHABLE();
1868 }
1869 }
1870
IsSameDeclarationType(varbinder::LocalVariable * target,varbinder::LocalVariable * compare)1871 bool ETSChecker::IsSameDeclarationType(varbinder::LocalVariable *target, varbinder::LocalVariable *compare)
1872 {
1873 return target->Declaration()->Type() == compare->Declaration()->Type();
1874 }
1875
CheckRethrowingParams(const ir::AstNode * ancestorFunction,const ir::AstNode * node)1876 bool ETSChecker::CheckRethrowingParams(const ir::AstNode *ancestorFunction, const ir::AstNode *node)
1877 {
1878 for (const auto param : ancestorFunction->AsScriptFunction()->Signature()->Function()->Params()) {
1879 if (node->AsCallExpression()->Callee()->AsIdentifier()->Name().Is(
1880 // CC-OFFNXT(G.FMT.06-CPP) project code style
1881 param->AsETSParameterExpression()->Ident()->Name().Mutf8())) {
1882 return true;
1883 }
1884 }
1885 return false;
1886 }
1887
CheckThrowingStatements(ir::AstNode * node)1888 void ETSChecker::CheckThrowingStatements(ir::AstNode *node)
1889 {
1890 ir::AstNode *ancestorFunction = FindAncestorGivenByType(node, ir::AstNodeType::SCRIPT_FUNCTION);
1891
1892 if (ancestorFunction == nullptr) {
1893 LogTypeError(
1894 "This statement can cause an exception, therefore it must be enclosed in a try statement with a default "
1895 "catch clause",
1896 node->Start());
1897 return;
1898 }
1899
1900 if (ancestorFunction->AsScriptFunction()->IsThrowing() ||
1901 (ancestorFunction->AsScriptFunction()->IsRethrowing() &&
1902 (!node->IsThrowStatement() && CheckRethrowingParams(ancestorFunction, node)))) {
1903 return;
1904 }
1905
1906 if (!CheckThrowingPlacement(node, ancestorFunction)) {
1907 if (ancestorFunction->AsScriptFunction()->IsRethrowing() && !node->IsThrowStatement()) {
1908 LogTypeError(
1909 "This statement can cause an exception, re-throwing functions can throw exception only by their "
1910 "parameters.",
1911 node->Start());
1912 return;
1913 }
1914
1915 if (auto interfaces =
1916 ancestorFunction->AsScriptFunction()->Signature()->Owner()->AsETSObjectType()->Interfaces();
1917 !(!interfaces.empty() &&
1918 interfaces[0]->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_INTERFACE))) {
1919 LogTypeError(
1920 "This statement can cause an exception, therefore it must be enclosed in a try statement with a "
1921 "default "
1922 "catch clause",
1923 node->Start());
1924 return;
1925 }
1926 }
1927 }
1928
CheckThrowingPlacement(ir::AstNode * node,const ir::AstNode * ancestorFunction)1929 bool ETSChecker::CheckThrowingPlacement(ir::AstNode *node, const ir::AstNode *ancestorFunction)
1930 {
1931 ir::AstNode *startPoint = node;
1932 ir::AstNode *enclosingCatchClause = nullptr;
1933 ir::BlockStatement *enclosingFinallyBlock = nullptr;
1934 ir::AstNode *p = startPoint->Parent();
1935
1936 bool isHandled = false;
1937 const auto predicateFunc = [&enclosingCatchClause](ir::CatchClause *clause) {
1938 return clause == enclosingCatchClause;
1939 };
1940
1941 do {
1942 if (p->IsTryStatement() && p->AsTryStatement()->HasDefaultCatchClause()) {
1943 enclosingCatchClause = FindAncestorGivenByType(startPoint, ir::AstNodeType::CATCH_CLAUSE, p);
1944 enclosingFinallyBlock = FindFinalizerOfTryStatement(startPoint, p);
1945 const auto catches = p->AsTryStatement()->CatchClauses();
1946 if (std::any_of(catches.begin(), catches.end(), predicateFunc)) {
1947 startPoint = enclosingCatchClause;
1948 } else if (enclosingFinallyBlock != nullptr &&
1949 enclosingFinallyBlock == p->AsTryStatement()->FinallyBlock()) {
1950 startPoint = enclosingFinallyBlock;
1951 } else {
1952 isHandled = true;
1953 break;
1954 }
1955 }
1956
1957 p = p->Parent();
1958 } while (p != ancestorFunction);
1959
1960 return isHandled;
1961 }
1962
FindFinalizerOfTryStatement(ir::AstNode * startFrom,const ir::AstNode * p)1963 ir::BlockStatement *ETSChecker::FindFinalizerOfTryStatement(ir::AstNode *startFrom, const ir::AstNode *p)
1964 {
1965 auto *iter = startFrom->Parent();
1966
1967 do {
1968 if (iter->IsBlockStatement()) {
1969 ir::BlockStatement *finallyBlock = iter->AsBlockStatement();
1970
1971 if (finallyBlock == p->AsTryStatement()->FinallyBlock()) {
1972 return finallyBlock;
1973 }
1974 }
1975
1976 iter = iter->Parent();
1977 } while (iter != p);
1978
1979 return nullptr;
1980 }
1981
CheckRethrowingFunction(ir::ScriptFunction * func)1982 void ETSChecker::CheckRethrowingFunction(ir::ScriptFunction *func)
1983 {
1984 if (func->Signature()->Owner()->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL_INTERFACE)) {
1985 return;
1986 }
1987
1988 bool foundThrowingParam = false;
1989
1990 // It doesn't support lambdas yet.
1991 for (auto item : func->Params()) {
1992 auto const *type = item->AsETSParameterExpression()->Ident()->TypeAnnotation();
1993
1994 if (type->IsETSTypeReference()) {
1995 auto *typeDecl = type->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Variable()->Declaration();
1996 if (typeDecl->IsTypeAliasDecl()) {
1997 type = typeDecl->Node()->AsTSTypeAliasDeclaration()->TypeAnnotation();
1998 }
1999 }
2000
2001 if (type->IsETSFunctionType() && type->AsETSFunctionType()->IsThrowing()) {
2002 foundThrowingParam = true;
2003 break;
2004 }
2005 }
2006
2007 if (!foundThrowingParam) {
2008 LogTypeError("A rethrowing function must have a throwing function parameter", func->Start());
2009 }
2010 }
2011
GetRelevantArgumentedTypeFromChild(ETSObjectType * const child,ETSObjectType * const target)2012 ETSObjectType *ETSChecker::GetRelevantArgumentedTypeFromChild(ETSObjectType *const child, ETSObjectType *const target)
2013 {
2014 if (child->GetDeclNode() == target->GetDeclNode()) {
2015 auto *relevantType = CreateNewETSObjectType(child->Name(), child->GetDeclNode(), child->ObjectFlags());
2016
2017 ArenaVector<Type *> params = child->TypeArguments();
2018
2019 relevantType->SetTypeArguments(std::move(params));
2020 relevantType->SetEnclosingType(child->EnclosingType());
2021 relevantType->SetSuperType(child->SuperType());
2022
2023 return relevantType;
2024 }
2025
2026 ASSERT(child->SuperType() != nullptr);
2027
2028 return GetRelevantArgumentedTypeFromChild(child->SuperType(), target);
2029 }
2030
EmplaceSubstituted(Substitution * substitution,ETSTypeParameter * tparam,Type * typeArg)2031 void ETSChecker::EmplaceSubstituted(Substitution *substitution, ETSTypeParameter *tparam, Type *typeArg)
2032 {
2033 // *only* reference type may be substituted, no exceptions
2034 ASSERT(typeArg->IsETSReferenceType());
2035 substitution->emplace(tparam, typeArg);
2036 }
2037
GetHashFromTypeArguments(const ArenaVector<Type * > & typeArgTypes)2038 util::StringView ETSChecker::GetHashFromTypeArguments(const ArenaVector<Type *> &typeArgTypes)
2039 {
2040 std::stringstream ss;
2041
2042 for (auto *it : typeArgTypes) {
2043 it->ToString(ss, true);
2044 ss << compiler::Signatures::MANGLE_SEPARATOR;
2045
2046 // In case of ETSTypeParameters storing the name might not be sufficient as there can
2047 // be multiple different type parameters with the same name. For those we test identity based
2048 // on their memory address equality, so we store them in the hash to keep it unique.
2049 // To make it consistent we store it for every type.
2050 // NOTE (mmartin): change bare address to something more appropriate unique representation
2051 ss << it << compiler::Signatures::MANGLE_SEPARATOR;
2052 }
2053
2054 return util::UString(ss.str(), Allocator()).View();
2055 }
2056
GetHashFromSubstitution(const Substitution * substitution)2057 util::StringView ETSChecker::GetHashFromSubstitution(const Substitution *substitution)
2058 {
2059 std::vector<std::string> fields;
2060 for (auto [k, v] : *substitution) {
2061 std::stringstream ss;
2062 k->ToString(ss, true);
2063 ss << ":";
2064 v->ToString(ss, true);
2065 // NOTE (mmartin): change bare address to something more appropriate unique representation
2066 ss << ":" << k << ":" << v;
2067 fields.push_back(ss.str());
2068 }
2069 std::sort(fields.begin(), fields.end());
2070
2071 std::stringstream ss;
2072 for (auto &fstr : fields) {
2073 ss << fstr;
2074 ss << ";";
2075 }
2076 return util::UString(ss.str(), Allocator()).View();
2077 }
2078
GetHashFromFunctionType(ir::ETSFunctionType * type)2079 util::StringView ETSChecker::GetHashFromFunctionType(ir::ETSFunctionType *type)
2080 {
2081 std::stringstream ss;
2082 for (auto *p : type->Params()) {
2083 auto *const param = p->AsETSParameterExpression();
2084 param->TypeAnnotation()->GetType(this)->ToString(ss, true);
2085 ss << ";";
2086 }
2087
2088 type->ReturnType()->GetType(this)->ToString(ss, true);
2089 ss << ";";
2090
2091 if (type->IsThrowing()) {
2092 ss << "throws;";
2093 }
2094
2095 if (type->IsRethrowing()) {
2096 ss << "rethrows;";
2097 }
2098
2099 return util::UString(ss.str(), Allocator()).View();
2100 }
2101
GetOriginalBaseType(Type * const object)2102 ETSObjectType *ETSChecker::GetOriginalBaseType(Type *const object)
2103 {
2104 if (object == nullptr || !object->IsETSObjectType()) {
2105 return nullptr;
2106 }
2107
2108 return object->AsETSObjectType()->GetOriginalBaseType();
2109 }
2110
CheckValidGenericTypeParameter(Type * const argType,const lexer::SourcePosition & pos)2111 void ETSChecker::CheckValidGenericTypeParameter(Type *const argType, const lexer::SourcePosition &pos)
2112 {
2113 std::stringstream ss;
2114 argType->ToString(ss);
2115 LogTypeError("Type '" + ss.str() + "' is not valid for generic type arguments", pos);
2116 }
2117
CheckNumberOfTypeArguments(ETSObjectType * const type,ir::TSTypeParameterInstantiation * const typeArgs,const lexer::SourcePosition & pos)2118 bool ETSChecker::CheckNumberOfTypeArguments(ETSObjectType *const type, ir::TSTypeParameterInstantiation *const typeArgs,
2119 const lexer::SourcePosition &pos)
2120 {
2121 auto const &typeParams = type->TypeArguments();
2122 if (typeParams.empty()) {
2123 if (typeArgs != nullptr) {
2124 LogTypeError({"Type '", type, "' is not generic."}, pos);
2125 return false;
2126 }
2127 return true;
2128 }
2129
2130 size_t minimumTypeArgs = std::count_if(typeParams.begin(), typeParams.end(), [](Type *param) {
2131 return param->AsETSTypeParameter()->GetDefaultType() == nullptr;
2132 });
2133 if (typeArgs == nullptr && minimumTypeArgs > 0) {
2134 LogTypeError({"Type '", type, "' is generic but type argument were not provided."}, pos);
2135 return false;
2136 }
2137
2138 if (typeArgs != nullptr &&
2139 ((minimumTypeArgs > typeArgs->Params().size()) || (typeParams.size() < typeArgs->Params().size()))) {
2140 LogTypeError({"Type '", type, "' has ", minimumTypeArgs, " number of type parameters, but ",
2141 typeArgs->Params().size(), " type arguments were provided."},
2142 pos);
2143 return false;
2144 }
2145 return true;
2146 }
2147
NeedTypeInference(const ir::ScriptFunction * lambda)2148 bool ETSChecker::NeedTypeInference(const ir::ScriptFunction *lambda)
2149 {
2150 if (lambda->ReturnTypeAnnotation() == nullptr) {
2151 return true;
2152 }
2153 for (auto *const param : lambda->Params()) {
2154 const auto *const lambdaParam = param->AsETSParameterExpression()->Ident();
2155 if (lambdaParam->TypeAnnotation() == nullptr) {
2156 return true;
2157 }
2158 }
2159 return false;
2160 }
2161
FindTypeInferenceArguments(const ArenaVector<ir::Expression * > & arguments)2162 std::vector<bool> ETSChecker::FindTypeInferenceArguments(const ArenaVector<ir::Expression *> &arguments)
2163 {
2164 std::vector<bool> argTypeInferenceRequired(arguments.size());
2165 size_t index = 0;
2166 for (ir::Expression *arg : arguments) {
2167 if (arg->IsArrowFunctionExpression()) {
2168 ir::ScriptFunction *const lambda = arg->AsArrowFunctionExpression()->Function();
2169 if (NeedTypeInference(lambda)) {
2170 argTypeInferenceRequired[index] = true;
2171 }
2172 }
2173 ++index;
2174 }
2175 return argTypeInferenceRequired;
2176 }
2177
CheckLambdaAssignableUnion(ir::AstNode * typeAnn,ir::ScriptFunction * lambda)2178 bool ETSChecker::CheckLambdaAssignableUnion(ir::AstNode *typeAnn, ir::ScriptFunction *lambda)
2179 {
2180 for (auto *type : typeAnn->AsETSUnionType()->Types()) {
2181 if (type->IsETSFunctionType()) {
2182 return lambda->Params().size() == type->AsETSFunctionType()->Params().size();
2183 }
2184 }
2185
2186 return false;
2187 }
2188
InferTypesForLambda(ir::ScriptFunction * lambda,ir::ETSFunctionType * calleeType,Signature * maybeSubstitutedFunctionSig)2189 void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, ir::ETSFunctionType *calleeType,
2190 Signature *maybeSubstitutedFunctionSig)
2191 {
2192 for (size_t i = 0; i < calleeType->Params().size(); ++i) {
2193 const auto *const calleeParam = calleeType->Params()[i]->AsETSParameterExpression()->Ident();
2194 auto *const lambdaParam = lambda->Params()[i]->AsETSParameterExpression()->Ident();
2195 if (lambdaParam->TypeAnnotation() == nullptr) {
2196 auto *const typeAnnotation = calleeParam->TypeAnnotation()->Clone(Allocator(), lambdaParam);
2197 if (maybeSubstitutedFunctionSig != nullptr) {
2198 ASSERT(maybeSubstitutedFunctionSig->Params().size() == calleeType->Params().size());
2199 typeAnnotation->SetTsType(maybeSubstitutedFunctionSig->Params()[i]->TsType());
2200 }
2201 lambdaParam->SetTsTypeAnnotation(typeAnnotation);
2202 typeAnnotation->SetParent(lambdaParam);
2203 }
2204 }
2205 if (lambda->ReturnTypeAnnotation() == nullptr) {
2206 auto *const returnTypeAnnotation = calleeType->ReturnType()->Clone(Allocator(), lambda);
2207 if (maybeSubstitutedFunctionSig != nullptr) {
2208 returnTypeAnnotation->SetTsType(maybeSubstitutedFunctionSig->ReturnType());
2209 }
2210
2211 // Return type can be ETSFunctionType
2212 // Run varbinder to set scopes for cloned node
2213 compiler::InitScopesPhaseETS::RunExternalNode(returnTypeAnnotation, VarBinder());
2214 lambda->SetReturnTypeAnnotation(returnTypeAnnotation);
2215 }
2216 }
2217
ModifyPreferredType(ir::ArrayExpression * const arrayExpr,Type * const newPreferredType)2218 void ETSChecker::ModifyPreferredType(ir::ArrayExpression *const arrayExpr, Type *const newPreferredType)
2219 {
2220 // After modifying the preferred type of an array expression, it needs to be rechecked at the call site
2221 arrayExpr->SetPreferredType(newPreferredType);
2222 arrayExpr->SetTsType(nullptr);
2223
2224 for (auto *const element : arrayExpr->Elements()) {
2225 if (element->IsArrayExpression()) {
2226 ModifyPreferredType(element->AsArrayExpression(), nullptr);
2227 }
2228 }
2229 }
2230
IsInLocalClass(const ir::AstNode * node) const2231 bool ETSChecker::IsInLocalClass(const ir::AstNode *node) const
2232 {
2233 while (node != nullptr) {
2234 if (node->Type() == ir::AstNodeType::CLASS_DEFINITION) {
2235 return node->AsClassDefinition()->IsLocal();
2236 }
2237 node = node->Parent();
2238 }
2239
2240 return false;
2241 }
2242
GenerateImplicitInstantiateArg(varbinder::LocalVariable * instantiateMethod,const std::string & className)2243 ir::Expression *ETSChecker::GenerateImplicitInstantiateArg(varbinder::LocalVariable *instantiateMethod,
2244 const std::string &className)
2245 {
2246 auto callSignatures = instantiateMethod->TsType()->AsETSFunctionType()->CallSignatures();
2247 ASSERT(!callSignatures.empty());
2248 auto methodOwner = std::string(callSignatures[0]->Owner()->Name());
2249 std::string implicitInstantiateArgument = "()=>{return new " + className + "()";
2250 if (methodOwner != className) {
2251 implicitInstantiateArgument.append(" as " + methodOwner);
2252 }
2253 implicitInstantiateArgument.append("}");
2254
2255 parser::Program program(Allocator(), VarBinder());
2256 es2panda::CompilerOptions options;
2257 auto parser = parser::ETSParser(&program, options, parser::ParserStatus::NO_OPTS);
2258 auto *argExpr = parser.CreateExpression(implicitInstantiateArgument);
2259 compiler::InitScopesPhaseETS::RunExternalNode(argExpr, &program);
2260
2261 return argExpr;
2262 }
2263
ClassPropToImplementationProp(ir::ClassProperty * classProp,varbinder::ClassScope * scope)2264 ir::ClassProperty *ETSChecker::ClassPropToImplementationProp(ir::ClassProperty *classProp, varbinder::ClassScope *scope)
2265 {
2266 classProp->Key()->AsIdentifier()->SetName(
2267 util::UString(std::string(compiler::Signatures::PROPERTY) + classProp->Key()->AsIdentifier()->Name().Mutf8(),
2268 Allocator())
2269 .View());
2270 classProp->AddModifier(ir::ModifierFlags::PRIVATE);
2271
2272 auto *fieldDecl = Allocator()->New<varbinder::LetDecl>(classProp->Key()->AsIdentifier()->Name());
2273 fieldDecl->BindNode(classProp);
2274
2275 auto fieldVar = scope->InstanceFieldScope()->AddDecl(Allocator(), fieldDecl, ScriptExtension::ETS);
2276 fieldVar->AddFlag(varbinder::VariableFlags::PROPERTY);
2277
2278 classProp->Key()->SetVariable(fieldVar);
2279 classProp->Key()->AsIdentifier()->SetVariable(fieldVar);
2280 fieldVar->SetTsType(classProp->TsType());
2281
2282 return classProp;
2283 }
2284
GenerateGetterSetterBody(ArenaVector<ir::Statement * > & stmts,ArenaVector<ir::Expression * > & params,ir::ClassProperty * const field,varbinder::FunctionParamScope * paramScope,bool isSetter)2285 void ETSChecker::GenerateGetterSetterBody(ArenaVector<ir::Statement *> &stmts, ArenaVector<ir::Expression *> ¶ms,
2286 ir::ClassProperty *const field, varbinder::FunctionParamScope *paramScope,
2287 bool isSetter)
2288 {
2289 auto *classDef = field->Parent()->AsClassDefinition();
2290
2291 ir::Expression *baseExpression;
2292 if ((field->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U) {
2293 baseExpression = Allocator()->New<ir::SuperExpression>();
2294 } else {
2295 baseExpression = Allocator()->New<ir::ThisExpression>();
2296 }
2297 baseExpression->SetParent(classDef);
2298 baseExpression->SetTsType(classDef->TsType());
2299
2300 auto *memberExpression =
2301 AllocNode<ir::MemberExpression>(baseExpression, field->Key()->AsIdentifier()->Clone(Allocator(), nullptr),
2302 ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
2303 memberExpression->SetTsType(field->TsType());
2304 memberExpression->SetPropVar(field->Key()->Variable()->AsLocalVariable());
2305 memberExpression->SetRange(classDef->Range());
2306 if (memberExpression->ObjType() == nullptr && classDef->TsType() != nullptr) {
2307 memberExpression->SetObjectType(classDef->TsType()->AsETSObjectType());
2308 }
2309
2310 if (!isSetter) {
2311 stmts.push_back(AllocNode<ir::ReturnStatement>(memberExpression));
2312 return;
2313 }
2314
2315 auto *paramIdent = field->Key()->AsIdentifier()->Clone(Allocator(), nullptr);
2316 if (field->TypeAnnotation() != nullptr) {
2317 auto *const typeAnnotation = field->TypeAnnotation()->Clone(Allocator(), paramIdent);
2318 paramIdent->SetTsTypeAnnotation(typeAnnotation);
2319 } else {
2320 paramIdent->SetTsType(field->TsType());
2321 }
2322
2323 auto *paramExpression = AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr);
2324 paramExpression->SetRange(paramIdent->Range());
2325 auto *const paramVar = std::get<2>(paramScope->AddParamDecl(Allocator(), paramExpression));
2326 paramExpression->SetVariable(paramVar);
2327
2328 params.push_back(paramExpression);
2329
2330 auto *assignmentExpression = AllocNode<ir::AssignmentExpression>(
2331 memberExpression, paramExpression->Clone(Allocator(), nullptr), lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
2332 assignmentExpression->SetTsType(paramVar->TsType());
2333
2334 assignmentExpression->SetRange({field->Start(), field->End()});
2335 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2336 stmts.push_back(AllocNode<ir::ExpressionStatement>(assignmentExpression));
2337 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2338 stmts.push_back(Allocator()->New<ir::ReturnStatement>(nullptr));
2339 }
2340
GenerateDefaultGetterSetter(ir::ClassProperty * const property,ir::ClassProperty * const field,varbinder::ClassScope * classScope,bool isSetter,ETSChecker * checker)2341 ir::MethodDefinition *ETSChecker::GenerateDefaultGetterSetter(ir::ClassProperty *const property,
2342 ir::ClassProperty *const field,
2343 varbinder::ClassScope *classScope, bool isSetter,
2344 ETSChecker *checker)
2345 {
2346 auto *paramScope = checker->Allocator()->New<varbinder::FunctionParamScope>(checker->Allocator(), classScope);
2347 auto *functionScope = checker->Allocator()->New<varbinder::FunctionScope>(checker->Allocator(), paramScope);
2348
2349 functionScope->BindParamScope(paramScope);
2350 paramScope->BindFunctionScope(functionScope);
2351
2352 ArenaVector<ir::Expression *> params(checker->Allocator()->Adapter());
2353 ArenaVector<ir::Statement *> stmts(checker->Allocator()->Adapter());
2354 checker->GenerateGetterSetterBody(stmts, params, field, paramScope, isSetter);
2355 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2356 auto *body = checker->AllocNode<ir::BlockStatement>(checker->Allocator(), std::move(stmts));
2357 auto funcFlags = isSetter ? ir::ScriptFunctionFlags::SETTER : ir::ScriptFunctionFlags::GETTER;
2358 auto *const returnTypeAnn = isSetter || field->TypeAnnotation() == nullptr
2359 ? nullptr
2360 : field->TypeAnnotation()->Clone(checker->Allocator(), nullptr);
2361 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2362 auto *func = checker->AllocNode<ir::ScriptFunction>(
2363 checker->Allocator(),
2364 ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), returnTypeAnn),
2365 funcFlags, ir::ModifierFlags::PUBLIC});
2366
2367 if (!isSetter) {
2368 func->AddFlag(ir::ScriptFunctionFlags::HAS_RETURN);
2369 }
2370 func->SetRange(field->Range());
2371 func->SetScope(functionScope);
2372 body->SetScope(functionScope);
2373 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2374 auto *methodIdent = property->Key()->AsIdentifier()->Clone(checker->Allocator(), nullptr);
2375 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2376 auto *funcExpr = checker->AllocNode<ir::FunctionExpression>(func);
2377 funcExpr->SetRange(func->Range());
2378 func->AddFlag(ir::ScriptFunctionFlags::METHOD);
2379 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2380 auto *method = checker->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, methodIdent, funcExpr,
2381 ir::ModifierFlags::PUBLIC, checker->Allocator(), false);
2382
2383 auto *decl = checker->Allocator()->New<varbinder::FunctionDecl>(checker->Allocator(),
2384 property->Key()->AsIdentifier()->Name(), method);
2385 auto *var = checker->Allocator()->New<varbinder::LocalVariable>(decl, varbinder::VariableFlags::VAR);
2386 var->AddFlag(varbinder::VariableFlags::METHOD);
2387
2388 methodIdent->SetVariable(var);
2389
2390 method->Id()->SetMutator();
2391 method->SetRange(field->Range());
2392 method->Function()->SetIdent(method->Id()->Clone(checker->Allocator(), nullptr));
2393 method->Function()->AddModifier(method->Modifiers());
2394 method->SetVariable(var);
2395 method->SetParent(field->Parent());
2396
2397 paramScope->BindNode(func);
2398 functionScope->BindNode(func);
2399
2400 auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(checker->VarBinder(), classScope);
2401 checker->VarBinder()->AsETSBinder()->ResolveMethodDefinition(method);
2402
2403 functionScope->BindName(classScope->Node()->AsClassDefinition()->InternalName());
2404 method->Check(checker);
2405
2406 return method;
2407 }
2408
GetImplementationClassProp(ETSChecker * checker,ir::ClassProperty * interfaceProp,ir::ClassProperty * originalProp,ETSObjectType * classType)2409 ir::ClassProperty *GetImplementationClassProp(ETSChecker *checker, ir::ClassProperty *interfaceProp,
2410 ir::ClassProperty *originalProp, ETSObjectType *classType)
2411 {
2412 bool isSuperOwner = ((originalProp->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U);
2413 if (!isSuperOwner) {
2414 auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2415 auto *const scope = checker->Scope()->AsClassScope();
2416 auto *const classProp = checker->ClassPropToImplementationProp(
2417 interfaceProp->Clone(checker->Allocator(), originalProp->Parent()), scope);
2418 classType->AddProperty<PropertyType::INSTANCE_FIELD>(classProp->Key()->Variable()->AsLocalVariable());
2419 classDef->Body().push_back(classProp);
2420 return classProp;
2421 }
2422
2423 auto *const classProp = classType
2424 ->GetProperty(interfaceProp->Key()->AsIdentifier()->Name(),
2425 PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE)
2426 ->Declaration()
2427 ->Node()
2428 ->AsClassProperty();
2429 classProp->AddModifier(ir::ModifierFlags::SUPER_OWNER);
2430 return classProp;
2431 }
2432
SetupGetterSetterFlags(ir::ClassProperty * originalProp,ETSObjectType * classType,ir::MethodDefinition * getter,ir::MethodDefinition * setter,const bool inExternal)2433 static void SetupGetterSetterFlags(ir::ClassProperty *originalProp, ETSObjectType *classType,
2434 ir::MethodDefinition *getter, ir::MethodDefinition *setter, const bool inExternal)
2435 {
2436 auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2437 for (auto &method : {getter, setter}) {
2438 if (method == nullptr) {
2439 continue;
2440 }
2441
2442 const auto mflag = method == getter ? ir::ModifierFlags::GETTER : ir::ModifierFlags::SETTER;
2443 const auto tflag = method == getter ? TypeFlag::GETTER : TypeFlag::SETTER;
2444
2445 method->TsType()->AddTypeFlag(tflag);
2446 method->Variable()->SetTsType(method->TsType());
2447 if (((originalProp->Modifiers() & mflag) != 0U)) {
2448 method->Function()->AddModifier(ir::ModifierFlags::OVERRIDE);
2449 }
2450
2451 if (inExternal) {
2452 method->Function()->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
2453 }
2454 method->SetParent(classDef);
2455 classType->AddProperty<checker::PropertyType::INSTANCE_METHOD>(method->Variable()->AsLocalVariable());
2456 }
2457 }
2458
GenerateGetterSetterPropertyAndMethod(ir::ClassProperty * originalProp,ETSObjectType * classType)2459 void ETSChecker::GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *originalProp, ETSObjectType *classType)
2460 {
2461 auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2462 auto *interfaceProp = originalProp->Clone(Allocator(), originalProp->Parent());
2463 interfaceProp->ClearModifier(ir::ModifierFlags::GETTER_SETTER);
2464
2465 auto *const scope = Scope()->AsClassScope();
2466 scope->InstanceFieldScope()->EraseBinding(interfaceProp->Key()->AsIdentifier()->Name());
2467 interfaceProp->SetRange(originalProp->Range());
2468
2469 auto *const classProp = GetImplementationClassProp(this, interfaceProp, originalProp, classType);
2470
2471 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2472 ir::MethodDefinition *getter = GenerateDefaultGetterSetter(interfaceProp, classProp, scope, false, this);
2473 classDef->Body().push_back(getter);
2474
2475 const auto &name = getter->Key()->AsIdentifier()->Name();
2476
2477 ir::MethodDefinition *setter =
2478 !classProp->IsConst()
2479 ? GenerateDefaultGetterSetter(interfaceProp, classProp, Scope()->AsClassScope(), true, this)
2480 : nullptr;
2481
2482 auto *const methodScope = scope->InstanceMethodScope();
2483 auto *const decl = Allocator()->New<varbinder::FunctionDecl>(Allocator(), name, getter);
2484 auto *var = methodScope->AddDecl(Allocator(), decl, ScriptExtension::ETS);
2485
2486 if (var == nullptr) {
2487 auto *const prevDecl = methodScope->FindDecl(name);
2488 for (const auto &method : {getter, setter}) {
2489 if (method != nullptr) {
2490 prevDecl->Node()->AsMethodDefinition()->AddOverload(method);
2491 }
2492 }
2493 var = methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
2494 }
2495 var->AddFlag(varbinder::VariableFlags::METHOD);
2496
2497 SetupGetterSetterFlags(originalProp, classType, getter, setter, HasStatus(CheckerStatus::IN_EXTERNAL));
2498
2499 if (setter != nullptr) {
2500 getter->Variable()->TsType()->AsETSFunctionType()->AddCallSignature(
2501 setter->TsType()->AsETSFunctionType()->CallSignatures()[0]);
2502 }
2503
2504 getter->Function()->Id()->SetVariable(var);
2505 if (setter != nullptr) {
2506 getter->AddOverload(setter);
2507 }
2508 }
2509
TryGettingFunctionTypeFromInvokeFunction(Type * type)2510 Type *ETSChecker::TryGettingFunctionTypeFromInvokeFunction(Type *type)
2511 {
2512 if (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) {
2513 auto const propInvoke = type->AsETSObjectType()->GetProperty(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME,
2514 PropertySearchFlags::SEARCH_INSTANCE_METHOD);
2515 ASSERT(propInvoke != nullptr);
2516
2517 return propInvoke->TsType();
2518 }
2519
2520 return type;
2521 }
2522
TryTransformingToStaticInvoke(ir::Identifier * const ident,const Type * resolvedType)2523 bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, const Type *resolvedType)
2524 {
2525 ASSERT(ident->Parent()->IsCallExpression());
2526 ASSERT(ident->Parent()->AsCallExpression()->Callee() == ident);
2527
2528 if (!resolvedType->IsETSObjectType()) {
2529 return false;
2530 }
2531
2532 auto className = ident->Name();
2533 std::string_view propertyName;
2534
2535 PropertySearchFlags searchFlag = PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::SEARCH_IN_BASE |
2536 PropertySearchFlags::SEARCH_STATIC_METHOD;
2537 auto *instantiateMethod =
2538 resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INSTANTIATE_METHOD, searchFlag);
2539 auto *invokeMethod =
2540 resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INVOKE_METHOD, searchFlag);
2541 if (instantiateMethod != nullptr) {
2542 propertyName = compiler::Signatures::STATIC_INSTANTIATE_METHOD;
2543 } else if (invokeMethod != nullptr) {
2544 propertyName = compiler::Signatures::STATIC_INVOKE_METHOD;
2545 } else {
2546 LogTypeError({"No static ", compiler::Signatures::STATIC_INVOKE_METHOD, " method and static ",
2547 compiler::Signatures::STATIC_INSTANTIATE_METHOD, " method in ", className, ". ", className,
2548 "() is not allowed."},
2549 ident->Start());
2550 return true;
2551 }
2552 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2553 auto *classId = AllocNode<ir::Identifier>(className, Allocator());
2554 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2555 auto *methodId = AllocNode<ir::Identifier>(propertyName, Allocator());
2556 if (propertyName == compiler::Signatures::STATIC_INSTANTIATE_METHOD) {
2557 methodId->SetVariable(instantiateMethod);
2558 } else if (propertyName == compiler::Signatures::STATIC_INVOKE_METHOD) {
2559 methodId->SetVariable(invokeMethod);
2560 }
2561
2562 auto *transformedCallee =
2563 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2564 AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
2565
2566 classId->SetRange(ident->Range());
2567 methodId->SetRange(ident->Range());
2568 transformedCallee->SetRange(ident->Range());
2569
2570 auto *callExpr = ident->Parent()->AsCallExpression();
2571 transformedCallee->SetParent(callExpr);
2572 callExpr->SetCallee(transformedCallee);
2573
2574 if (instantiateMethod != nullptr) {
2575 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2576 auto *argExpr = GenerateImplicitInstantiateArg(instantiateMethod, std::string(className));
2577
2578 argExpr->SetParent(callExpr);
2579 argExpr->SetRange(ident->Range());
2580
2581 VarBinder()->AsETSBinder()->HandleCustomNodes(argExpr);
2582
2583 auto &arguments = callExpr->Arguments();
2584 arguments.insert(arguments.begin(), argExpr);
2585 }
2586
2587 return true;
2588 }
2589
CreateSyntheticType(util::StringView const & syntheticName,checker::ETSObjectType * lastObjectType,ir::Identifier * id)2590 checker::ETSObjectType *ETSChecker::CreateSyntheticType(util::StringView const &syntheticName,
2591 checker::ETSObjectType *lastObjectType, ir::Identifier *id)
2592 {
2593 auto *syntheticObjType = Allocator()->New<checker::ETSObjectType>(Allocator(), syntheticName, syntheticName, id,
2594 checker::ETSObjectFlags::NO_OPTS);
2595
2596 auto *classDecl = Allocator()->New<varbinder::ClassDecl>(syntheticName);
2597 varbinder::LocalVariable *var =
2598 Allocator()->New<varbinder::LocalVariable>(classDecl, varbinder::VariableFlags::CLASS);
2599 var->SetTsType(syntheticObjType);
2600 lastObjectType->AddProperty<checker::PropertyType::STATIC_FIELD>(var);
2601 syntheticObjType->SetEnclosingType(lastObjectType);
2602 return syntheticObjType;
2603 }
2604
ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration * importDecl,checker::ETSObjectType * lastObjectType,ir::Identifier * ident)2605 void ETSChecker::ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration *importDecl,
2606 checker::ETSObjectType *lastObjectType, ir::Identifier *ident)
2607 {
2608 for (auto item : VarBinder()->AsETSBinder()->ReExportImports()) {
2609 if (!importDecl->ResolvedSource()->Str().Is(item->GetProgramPath().Mutf8())) {
2610 continue;
2611 }
2612 auto *reExportType = GetImportSpecifierObjectType(item->GetETSImportDeclarations(), ident);
2613 lastObjectType->AddReExports(reExportType);
2614 for (auto node : importDecl->Specifiers()) {
2615 if (node->IsImportSpecifier()) {
2616 auto specifier = node->AsImportSpecifier();
2617 lastObjectType->AddReExportAlias(specifier->Imported()->Name(), specifier->Local()->Name());
2618 }
2619 }
2620 }
2621 }
2622
GetImportSpecifierObjectType(ir::ETSImportDeclaration * importDecl,ir::Identifier * ident)2623 ETSObjectType *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident)
2624 {
2625 auto importPath = importDecl->ResolvedSource()->Str();
2626 parser::Program *program =
2627 SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath);
2628 std::vector<util::StringView> syntheticNames = GetNameForSynteticObjectType(program->ModuleName());
2629 ASSERT(!syntheticNames.empty());
2630 auto assemblerName = syntheticNames[0];
2631
2632 if (!program->OmitModuleName()) {
2633 assemblerName = util::UString(assemblerName.Mutf8()
2634 .append(compiler::Signatures::METHOD_SEPARATOR)
2635 .append(compiler::Signatures::ETS_GLOBAL),
2636 Allocator())
2637 .View();
2638 }
2639
2640 auto *moduleObjectType =
2641 Allocator()->New<checker::ETSObjectType>(Allocator(), syntheticNames[0], assemblerName,
2642 std::make_tuple(ident, checker::ETSObjectFlags::CLASS, Relation()));
2643
2644 auto *rootDecl = Allocator()->New<varbinder::ClassDecl>(syntheticNames[0]);
2645 varbinder::LocalVariable *rootVar =
2646 Allocator()->New<varbinder::LocalVariable>(rootDecl, varbinder::VariableFlags::NONE);
2647 rootVar->SetTsType(moduleObjectType);
2648
2649 syntheticNames.erase(syntheticNames.begin());
2650 checker::ETSObjectType *lastObjectType(moduleObjectType);
2651
2652 for (const auto &syntheticName : syntheticNames) {
2653 lastObjectType = CreateSyntheticType(syntheticName, lastObjectType, ident);
2654 }
2655
2656 ImportNamespaceObjectTypeAddReExportType(importDecl, lastObjectType, ident);
2657 SetPropertiesForModuleObject(lastObjectType, importPath,
2658 importDecl->Specifiers()[0]->IsImportNamespaceSpecifier() ? nullptr : importDecl);
2659 SetrModuleObjectTsType(ident, lastObjectType);
2660
2661 return moduleObjectType;
2662 }
2663 } // namespace ark::es2panda::checker
2664