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