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 "conversion.h" 17 18#include "checker/ets/boxingConverter.h" 19#include "checker/ets/narrowingConverter.h" 20#include "checker/ets/unboxingConverter.h" 21#include "checker/ets/wideningConverter.h" 22#include "checker/types/globalTypesHolder.h" 23 24namespace ark::es2panda::checker::conversion { 25void Identity(TypeRelation *const relation, Type *const source, Type *const target) 26{ 27 relation->IsIdenticalTo(source, target); 28} 29 30void WideningPrimitive(TypeRelation *const relation, Type *const source, Type *const target) 31{ 32 ASSERT(source->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && target->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)); 33 34 WideningConverter(relation->GetChecker()->AsETSChecker(), relation, target, source); 35} 36 37void NarrowingPrimitive(TypeRelation *const relation, Type *const source, Type *const target) 38{ 39 ASSERT(source->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && target->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)); 40 41 NarrowingConverter(relation->GetChecker()->AsETSChecker(), relation, target, source); 42} 43 44void WideningNarrowingPrimitive(TypeRelation *const relation, ByteType *const source, CharType *const target) 45{ 46 auto *const tempInt = relation->GetChecker()->AsETSChecker()->GetGlobalTypesHolder()->GlobalIntType(); 47 WideningPrimitive(relation, source, tempInt); 48 if (!relation->IsTrue()) { 49 return; 50 } 51 NarrowingPrimitive(relation, tempInt, target); 52} 53 54void WideningReference(TypeRelation *const relation, ETSObjectType *const source, ETSObjectType *const target) 55{ 56 relation->IsSupertypeOf(target, source); 57} 58 59void WideningReference(TypeRelation *const relation, ETSArrayType *const source, ETSObjectType *const target) 60{ 61 relation->IsSupertypeOf(target, source); 62} 63 64void WideningReference(TypeRelation *const relation, ETSArrayType *const source, ETSArrayType *const target) 65{ 66 relation->IsSupertypeOf(target, source); 67} 68 69namespace { 70 71bool IsAllowedNarrowingReferenceConversionObjectObject(TypeRelation *const relation, ETSObjectType *const source, 72 ETSObjectType *const target) 73{ 74 // 1. S and T are class types, and either |S| <: |T| or |T| <: |S|. 75 // NOTE: use type erased S and T 76 relation->Result(false); 77 if (relation->IsSupertypeOf(target, source) || relation->IsSupertypeOf(source, target)) { 78 return true; 79 } 80 81 // 2. S and T are interface types. 82 if (source->HasObjectFlag(ETSObjectFlags::INTERFACE) && target->HasObjectFlag(ETSObjectFlags::INTERFACE)) { 83 return true; 84 } 85 86 // 3. S is a class type, T is an interface type, and S names class not marked as final. 87 if (source->HasObjectFlag(ETSObjectFlags::CLASS) && target->HasObjectFlag(ETSObjectFlags::INTERFACE) && 88 !source->GetDeclNode()->IsFinal()) { 89 return true; 90 } 91 92 // 4. S is a class type, T is an interface type, and S names a class that is marked as final and that 93 // implements the interface named by T. 94 if (source->HasObjectFlag(ETSObjectFlags::CLASS) && target->HasObjectFlag(ETSObjectFlags::INTERFACE) && 95 source->GetDeclNode()->IsFinal() && relation->IsSupertypeOf(source, target)) { 96 return true; 97 } 98 99 // 5. S is an interface type, T is a class type, and T names a class not marked as final. 100 if (source->HasObjectFlag(ETSObjectFlags::INTERFACE) && target->HasObjectFlag(ETSObjectFlags::CLASS) && 101 !target->GetDeclNode()->IsFinal()) { 102 return true; 103 } 104 105 // 6. S is an interface type, T is a class type, and T names a class that is marked as final and that 106 // implements the interface named by S. 107 relation->Result(false); 108 if (source->HasObjectFlag(ETSObjectFlags::INTERFACE) && target->HasObjectFlag(ETSObjectFlags::CLASS) && 109 target->GetDeclNode()->IsFinal() && relation->IsSupertypeOf(target, source)) { 110 return true; 111 } 112 113 auto *const etsChecker = relation->GetChecker()->AsETSChecker(); 114 return relation->IsIdenticalTo(etsChecker->GetNonConstantType(source), etsChecker->GetNonConstantType(target)); 115} 116 117bool IsAllowedNarrowingReferenceConversion(TypeRelation *const relation, Type *const source, Type *const target) 118{ 119 ASSERT(source->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT) && 120 target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)); 121 122 // 11.1.6. Narrowing Reference Conversion 123 // A narrowing reference conversion exists from reference type S to a reference type T if all of the following are 124 // true: 125 126 // - S is not a subtype of T 127 relation->Result(false); 128 if (relation->IsSupertypeOf(target, source)) { 129 return false; 130 } 131 132 // - If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype 133 // of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct 134 // NOTE: implement 135 136 // - One of the following cases applies: 137 138 if (source->HasTypeFlag(TypeFlag::ETS_OBJECT) && target->HasTypeFlag(TypeFlag::ETS_OBJECT)) { 139 if (IsAllowedNarrowingReferenceConversionObjectObject(relation, source->AsETSObjectType(), 140 target->AsETSObjectType())) { 141 return true; 142 } 143 } 144 145 if (source->HasTypeFlag(TypeFlag::ETS_OBJECT) && target->HasTypeFlag(TypeFlag::ETS_ARRAY)) { 146 // 7. S is the class type Object of the interface type java.io.Serializable or Cloneable (the only interfaces 147 // implemented by arrays (link to class objects for arrays)), and T is an array type. 148 // NOTE: implement 149 return true; 150 } 151 152 if (source->HasTypeFlag(TypeFlag::ETS_ARRAY) && target->HasTypeFlag(TypeFlag::ETS_ARRAY)) { 153 // 8. S is an array type SC[], that is, an array of components of type SC; T is an array type TC[], that is, an 154 // array of components of type TC; and a narrowing reference conversion exists from SC to TC. 155 auto *sc = source->AsETSArrayType()->ElementType(); 156 auto *tc = target->AsETSArrayType()->ElementType(); 157 158 if (sc->IsETSObjectType() && tc->IsETSObjectType()) { 159 relation->Result(false); 160 NarrowingReference(relation, sc->AsETSObjectType(), tc->AsETSObjectType()); 161 if (relation->IsTrue()) { 162 return true; 163 } 164 } 165 } 166 167 // 9. S is a type variable, and a narrowing reference conversion exists from the upper bound of S to T. 168 // NOTE:: implement 169 170 // 10. T is a type variable, and either a widening reference conversion or a narrowing reference conversion exists 171 // from S to the upper bound of T. 172 // NOTE: implement 173 174 // 11. S is an intersection type S1 & … & Sn, and for all i (1 ≤ i ≤ n), either a widening reference 175 // conversion or a 176 // narrowing reference conversion exists from Si to T. 177 // NOTE: implement 178 179 // 12. T is an intersection type T1 & … & Tn, and for all i (1 ≤ i ≤ n), either a widening reference 180 // conversion or a 181 // narrowing reference conversion exists from S to Ti. 182 // NOTE: implement 183 184 return false; 185} 186 187bool IsUncheckedNarrowingReferenceConversion([[maybe_unused]] TypeRelation *const relation, 188 [[maybe_unused]] Type *const source, [[maybe_unused]] Type *const target) 189{ 190 ASSERT(source->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT) && 191 target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)); 192 193 // The unchecked narrowing reference conversions are as follows: 194 // - A narrowing reference conversion from a type S to a parameterized class or interface 195 // type T is unchecked, unless at least one of the following is true: 196 // - All of the type arguments of T are unbounded wildcard. 197 // - T <: S, and S has no subtype X other than T where the type arguments of X 198 // are not contained in the type arguments of T. 199 // - A narrowing reference conversion from a type S to a type variable T is unchecked. 200 // - A narrowing reference conversion from a type S to an intersection type T1 & … & Tn 201 // is unchecked if there exists a Ti (1 ≤ i ≤ n) such that S is not a subtype of Ti, and a 202 // narrowing reference conversion from S to Ti is unchecked. 203 // NOTE: implement 204 205 return false; 206} 207 208void NarrowingReferenceImpl(TypeRelation *const relation, Type *const source, Type *const target) 209{ 210 ASSERT(target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)); 211 212 if (!IsAllowedNarrowingReferenceConversion(relation, source, target)) { 213 Forbidden(relation); 214 return; 215 } 216 217 if (!IsUncheckedNarrowingReferenceConversion(relation, source, target)) { 218 relation->RemoveFlags(TypeRelationFlag::UNCHECKED_CAST); 219 } 220 221 relation->Result(true); 222} 223} // namespace 224 225void NarrowingReference(TypeRelation *const relation, ETSObjectType *const source, ETSObjectType *const target) 226{ 227 NarrowingReferenceImpl(relation, source, target); 228} 229 230void NarrowingReference(TypeRelation *const relation, ETSArrayType *const source, ETSArrayType *const target) 231{ 232 if (source->ElementType()->IsETSArrayType() && target->ElementType()->IsETSArrayType()) { 233 NarrowingReference(relation, source->ElementType()->AsETSArrayType(), target->ElementType()->AsETSArrayType()); 234 return; 235 } 236 237 NarrowingReferenceImpl(relation, source, target); 238} 239 240void NarrowingReference(TypeRelation *const relation, ETSObjectType *const source, ETSArrayType *const target) 241{ 242 if (target->ElementType()->IsETSArrayType()) { 243 NarrowingReference(relation, source, target->ElementType()->AsETSArrayType()); 244 return; 245 } 246 247 NarrowingReferenceImpl(relation, source, target); 248} 249 250static inline void RollbackBoxingIfFailed(TypeRelation *const relation) 251{ 252 if (!relation->IsTrue()) { 253 relation->GetNode()->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE); 254 } 255} 256 257ETSObjectType *Boxing(TypeRelation *const relation, Type *const source) 258{ 259 auto *const etsChecker = relation->GetChecker()->AsETSChecker(); 260 const BoxingConverter boxed(etsChecker, relation, source); 261 if (!relation->IsTrue()) { 262 return nullptr; 263 } 264 auto *const boxedType = boxed.Result()->AsETSObjectType(); 265 relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetBoxingFlag(boxedType)); 266 return boxedType; 267} 268 269Type *Unboxing(TypeRelation *const relation, ETSObjectType *const source) 270{ 271 auto *const etsChecker = relation->GetChecker()->AsETSChecker(); 272 const UnboxingConverter unboxed(etsChecker, relation, source); 273 if (!relation->IsTrue()) { 274 return nullptr; 275 } 276 auto *const unboxedType = unboxed.Result(); 277 relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetUnboxingFlag(unboxedType)); 278 return unboxedType; 279} 280 281void UnboxingWideningPrimitive(TypeRelation *const relation, ETSObjectType *const source, Type *const target) 282{ 283 auto *const unboxedSource = Unboxing(relation, source); 284 if (!relation->IsTrue()) { 285 return; 286 } 287 ASSERT(unboxedSource != nullptr); 288 WideningPrimitive(relation, target, unboxedSource); 289 RollbackBoxingIfFailed(relation); 290} 291 292void UnboxingNarrowingPrimitive(TypeRelation *const relation, ETSObjectType *const source, Type *const target) 293{ 294 auto *const unboxedSource = Unboxing(relation, source); 295 if (!relation->IsTrue()) { 296 return; 297 } 298 ASSERT(unboxedSource != nullptr); 299 NarrowingPrimitive(relation, target, unboxedSource); 300} 301 302void UnboxingWideningNarrowingPrimitive(TypeRelation *const relation, ETSObjectType *const source, Type *const target) 303{ 304 auto *const unboxedSource = Unboxing(relation, source); 305 if (!relation->IsTrue()) { 306 return; 307 } 308 ASSERT(unboxedSource != nullptr); 309 WideningNarrowingPrimitive(relation, unboxedSource->AsByteType(), target->AsCharType()); 310} 311 312void NarrowingReferenceUnboxing(TypeRelation *const relation, ETSObjectType *const source, Type *const target) 313{ 314 auto *const boxedTarget = relation->GetChecker()->AsETSChecker()->PrimitiveTypeAsETSBuiltinType(target); 315 if (boxedTarget == nullptr) { 316 return; 317 } 318 ASSERT(boxedTarget != nullptr); 319 NarrowingReference(relation, source, boxedTarget->AsETSObjectType()); 320 if (!relation->IsTrue()) { 321 return; 322 } 323 Unboxing(relation, boxedTarget->AsETSObjectType()); 324} 325 326void BoxingWideningReference(TypeRelation *const relation, Type *const source, ETSObjectType *const target) 327{ 328 auto *const boxedSource = Boxing(relation, source); 329 if (!relation->IsTrue()) { 330 return; 331 } 332 ASSERT(boxedSource != nullptr); 333 WideningReference(relation, boxedSource, target); 334 RollbackBoxingIfFailed(relation); 335} 336 337void String(TypeRelation *const relation, Type *const source) 338{ 339 if (source->HasTypeFlag(TypeFlag::BYTE | TypeFlag::SHORT)) { 340 auto *const tempInt = relation->GetChecker()->AsETSChecker()->GetGlobalTypesHolder()->GlobalIntType(); 341 WideningPrimitive(relation, source, tempInt); 342 Boxing(relation, tempInt); 343 relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::CONVERT_TO_STRING); 344 return; 345 } 346 347 if (source->HasTypeFlag(TypeFlag::ETS_BOOLEAN | TypeFlag::CHAR | TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT | 348 TypeFlag::DOUBLE)) { 349 Boxing(relation, source); 350 relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::CONVERT_TO_STRING); 351 return; 352 } 353 354 ASSERT(source->HasTypeFlag(TypeFlag::ETS_OBJECT)); 355} 356 357void Forbidden(TypeRelation *const relation) 358{ 359 relation->Result(false); 360} 361 362} // namespace ark::es2panda::checker::conversion 363