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