• 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 #include "checker/types/globalTypesHolder.h"
23 
24 namespace ark::es2panda::checker::conversion {
Identity(TypeRelation * const relation,Type * const source,Type * const target)25 void Identity(TypeRelation *const relation, Type *const source, Type *const target)
26 {
27     relation->IsIdenticalTo(source, target);
28 }
29 
WideningPrimitive(TypeRelation * const relation,Type * const source,Type * const target)30 void WideningPrimitive(TypeRelation *const relation, Type *const source, Type *const target)
31 {
32     ASSERT(source->IsETSPrimitiveType() && target->IsETSPrimitiveType());
33 
34     WideningConverter(relation->GetChecker()->AsETSChecker(), relation, target, source);
35 }
36 
NarrowingPrimitive(TypeRelation * const relation,Type * const source,Type * const target)37 void NarrowingPrimitive(TypeRelation *const relation, Type *const source, Type *const target)
38 {
39     ASSERT(source->IsETSPrimitiveType() && target->IsETSPrimitiveType());
40 
41     NarrowingConverter(relation->GetChecker()->AsETSChecker(), relation, target, source);
42 }
43 
WideningNarrowingPrimitive(TypeRelation * const relation,ByteType * const source,CharType * const target)44 void 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 
WideningReference(TypeRelation * const relation,ETSObjectType * const source,ETSObjectType * const target)54 void WideningReference(TypeRelation *const relation, ETSObjectType *const source, ETSObjectType *const target)
55 {
56     relation->IsSupertypeOf(target, source);
57 }
58 
WideningReference(TypeRelation * const relation,ETSArrayType * const source,ETSObjectType * const target)59 void WideningReference(TypeRelation *const relation, ETSArrayType *const source, ETSObjectType *const target)
60 {
61     relation->IsSupertypeOf(target, source);
62 }
63 
WideningReference(TypeRelation * const relation,ETSArrayType * const source,ETSArrayType * const target)64 void WideningReference(TypeRelation *const relation, ETSArrayType *const source, ETSArrayType *const target)
65 {
66     relation->IsSupertypeOf(target, source);
67 }
68 
69 namespace {
70 
IsAllowedNarrowingReferenceConversionObjectObject(TypeRelation * const relation,ETSObjectType * const source,ETSObjectType * const target)71 bool 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 
IsAllowedNarrowingReferenceConversion(TypeRelation * const relation,Type * const source,Type * const target)117 bool 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 
IsUncheckedNarrowingReferenceConversion(TypeRelation * const relation,Type * const source,Type * const target)187 bool 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 
NarrowingReferenceImpl(TypeRelation * const relation,Type * const source,Type * const target)208 void 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 
NarrowingReference(TypeRelation * const relation,ETSObjectType * const source,ETSObjectType * const target)225 void NarrowingReference(TypeRelation *const relation, ETSObjectType *const source, ETSObjectType *const target)
226 {
227     NarrowingReferenceImpl(relation, source, target);
228 }
229 
NarrowingReference(TypeRelation * const relation,ETSArrayType * const source,ETSArrayType * const target)230 void 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 
NarrowingReference(TypeRelation * const relation,ETSObjectType * const source,ETSArrayType * const target)240 void 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 
RollbackBoxingIfFailed(TypeRelation * const relation)250 static inline void RollbackBoxingIfFailed(TypeRelation *const relation)
251 {
252     if (!relation->IsTrue()) {
253         relation->GetNode()->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
254     }
255 }
256 
Boxing(TypeRelation * const relation,Type * const source)257 ETSObjectType *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 
Unboxing(TypeRelation * const relation,ETSObjectType * const source)269 Type *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 
UnboxingWideningPrimitive(TypeRelation * const relation,ETSObjectType * const source,Type * const target)281 void 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 
UnboxingNarrowingPrimitive(TypeRelation * const relation,ETSObjectType * const source,Type * const target)292 void 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 
UnboxingWideningNarrowingPrimitive(TypeRelation * const relation,ETSObjectType * const source,Type * const target)302 void 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 
NarrowingReferenceUnboxing(TypeRelation * const relation,ETSObjectType * const source,Type * const target)312 void NarrowingReferenceUnboxing(TypeRelation *const relation, ETSObjectType *const source, Type *const target)
313 {
314     auto *const boxedTarget = relation->GetChecker()->AsETSChecker()->MaybeBoxInRelation(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 
BoxingWideningReference(TypeRelation * const relation,Type * const source,ETSObjectType * const target)326 void 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 
String(TypeRelation * const relation,Type * const source)337 void 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 
Forbidden(TypeRelation * const relation)357 void Forbidden(TypeRelation *const relation)
358 {
359     relation->Result(false);
360 }
361 
362 }  // namespace ark::es2panda::checker::conversion
363