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