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