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