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