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