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 #ifndef ES2PANDA_COMPILER_CHECKER_ETS_TYPE_RELATION_CONTEXT_H 17 #define ES2PANDA_COMPILER_CHECKER_ETS_TYPE_RELATION_CONTEXT_H 18 19 #include "checker/ETSchecker.h" 20 21 namespace ark::es2panda::checker { 22 class ETSChecker; 23 24 class AssignmentContext { 25 public: 26 AssignmentContext(TypeRelation *relation, ir::Expression *node, Type *source, Type *target, 27 const lexer::SourcePosition &pos, std::initializer_list<TypeErrorMessageElement> list, 28 TypeRelationFlag flags = TypeRelationFlag::NONE) 29 { 30 flags_ |= ((flags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING; 31 flags_ |= ((flags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING; 32 flags_ |= ((flags & TypeRelationFlag::NO_WIDENING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::WIDENING; 33 34 auto *const etsChecker = relation->GetChecker()->AsETSChecker(); 35 36 if (target->IsETSArrayType() && node->IsArrayExpression()) { 37 ValidateArrayTypeInitializerByElement(relation, node->AsArrayExpression(), target->AsETSArrayType()); 38 return; 39 } 40 41 flags_ |= flags; 42 relation->SetNode(node); 43 44 // NOTE (oeotvos) The narrowing flag will be applied here. It means, that the result of "let tmp: int = 1.5" 45 // will be 1, which could cause problems. 46 if (source->HasTypeFlag(TypeFlag::CONSTANT)) { 47 flags_ |= TypeRelationFlag::NARROWING; 48 } 49 50 relation->SetFlags(flags_); 51 52 if (!relation->IsAssignableTo(source, target)) { 53 if (((flags_ & TypeRelationFlag::UNBOXING) != 0) && !relation->IsTrue() && source->IsETSObjectType() && 54 !target->IsETSObjectType()) { 55 etsChecker->CheckUnboxedTypesAssignable(relation, source, target); 56 } 57 if (((flags_ & TypeRelationFlag::BOXING) != 0) && target->IsETSObjectType() && !relation->IsTrue()) { 58 etsChecker->CheckBoxedSourceTypeAssignable(relation, source, target); 59 } 60 } 61 62 if (!relation->IsTrue() && (flags_ & TypeRelationFlag::NO_THROW) == 0) { 63 relation->RaiseError(list, pos); 64 } 65 66 relation->SetNode(nullptr); 67 relation->SetFlags(TypeRelationFlag::NONE); 68 assignable_ = relation->IsTrue(); 69 } 70 IsAssignable()71 bool IsAssignable() const 72 { 73 return assignable_; 74 } 75 76 void ValidateArrayTypeInitializerByElement(TypeRelation *relation, ir::ArrayExpression *node, ETSArrayType *target); 77 78 private: 79 TypeRelationFlag flags_ = TypeRelationFlag::IN_ASSIGNMENT_CONTEXT; 80 bool assignable_ {false}; 81 }; 82 83 class InvocationContext { 84 public: 85 InvocationContext(TypeRelation *relation, ir::Expression *node, Type *source, Type *target, 86 const lexer::SourcePosition &pos, std::initializer_list<TypeErrorMessageElement> list, 87 TypeRelationFlag initialFlags = TypeRelationFlag::NONE) 88 { 89 flags_ |= 90 ((initialFlags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING; 91 flags_ |= 92 ((initialFlags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING; 93 94 auto *const etsChecker = relation->GetChecker()->AsETSChecker(); 95 96 relation->SetNode(node); 97 relation->SetFlags(flags_ | initialFlags); 98 99 if (!relation->IsAssignableTo(source, target)) { 100 if (((flags_ & TypeRelationFlag::UNBOXING) != 0U) && !relation->IsTrue() && source->IsETSObjectType() && 101 !target->IsETSObjectType()) { 102 etsChecker->CheckUnboxedSourceTypeWithWideningAssignable(relation, source, target); 103 } 104 if (((flags_ & TypeRelationFlag::BOXING) != 0) && target->IsETSObjectType() && !relation->IsTrue()) { 105 etsChecker->CheckBoxedSourceTypeAssignable(relation, source, target); 106 } 107 } 108 109 relation->SetNode(nullptr); 110 relation->SetFlags(TypeRelationFlag::NONE); 111 112 if (!relation->IsTrue()) { 113 if ((initialFlags & TypeRelationFlag::NO_THROW) == 0) { 114 relation->RaiseError(list, pos); 115 } 116 return; 117 } 118 119 invocable_ = true; 120 } 121 IsInvocable()122 bool IsInvocable() const 123 { 124 return invocable_; 125 } 126 127 private: 128 TypeRelationFlag flags_ = TypeRelationFlag::NONE; 129 bool invocable_ {false}; 130 }; 131 132 class ConstraintCheckScope { 133 public: ConstraintCheckScope(ETSChecker * checker)134 explicit ConstraintCheckScope(ETSChecker *checker) : checker_(checker), isheld_(true) 135 { 136 size_t &counter = checker_->ConstraintCheckScopesCount(); 137 ASSERT(counter != 0 || checker_->PendingConstraintCheckRecords().empty()); 138 counter++; 139 } 140 ~ConstraintCheckScope()141 ~ConstraintCheckScope() 142 { 143 if (isheld_) { 144 Unlock(); 145 } 146 } 147 148 void TryCheckConstraints(); 149 150 NO_COPY_SEMANTIC(ConstraintCheckScope); 151 NO_MOVE_SEMANTIC(ConstraintCheckScope); 152 153 private: Unlock()154 bool Unlock() 155 { 156 ASSERT(isheld_); 157 isheld_ = false; 158 return --checker_->ConstraintCheckScopesCount() == 0; 159 } 160 161 ETSChecker *checker_; 162 bool isheld_ {}; 163 }; 164 165 class InstantiationContext { 166 public: InstantiationContext(ETSChecker * checker,ETSObjectType * type,ir::TSTypeParameterInstantiation * typeArgs,const lexer::SourcePosition & pos)167 InstantiationContext(ETSChecker *checker, ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs, 168 const lexer::SourcePosition &pos) 169 : checker_(checker) 170 { 171 if (ValidateTypeArguments(type, typeArgs, pos)) { 172 return; 173 } 174 InstantiateType(type, typeArgs); 175 } 176 InstantiationContext(ETSChecker * checker,ETSObjectType * type,ArenaVector<Type * > && typeArgs,const lexer::SourcePosition & pos)177 InstantiationContext(ETSChecker *checker, ETSObjectType *type, ArenaVector<Type *> &&typeArgs, 178 const lexer::SourcePosition &pos) 179 : checker_(checker) 180 { 181 if (type->HasObjectFlag(ETSObjectFlags::ENUM)) { 182 return; 183 } 184 InstantiateType(type, std::move(typeArgs), pos); 185 } 186 Result()187 ETSObjectType *Result() 188 { 189 return result_; 190 } 191 192 private: 193 bool ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs, 194 const lexer::SourcePosition &pos); 195 196 void InstantiateType(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs); 197 198 void InstantiateType(ETSObjectType *type, ArenaVector<Type *> &&typeArgTypes, const lexer::SourcePosition &pos); 199 util::StringView GetHashFromTypeArguments(ArenaVector<Type *> &typeArgTypes); 200 201 ETSChecker *checker_; 202 ETSObjectType *result_ {}; 203 }; 204 205 } // namespace ark::es2panda::checker 206 207 #endif 208