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->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && target->HasTypeFlag(TypeFlag::ETS_PRIMITIVE));
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->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && target->HasTypeFlag(TypeFlag::ETS_PRIMITIVE));
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 return (source->HasObjectFlag(ETSObjectFlags::INTERFACE) && target->HasObjectFlag(ETSObjectFlags::CLASS) &&
109 target->GetDeclNode()->IsFinal() && relation->IsSupertypeOf(target, source));
110 }
111
IsAllowedNarrowingReferenceConversion(TypeRelation * const relation,Type * const source,Type * const target)112 bool IsAllowedNarrowingReferenceConversion(TypeRelation *const relation, Type *const source, Type *const target)
113 {
114 ASSERT(source->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT) &&
115 target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT));
116
117 // 11.1.6. Narrowing Reference Conversion
118 // A narrowing reference conversion exists from reference type S to a reference type T if all of the following are
119 // true:
120
121 // - S is not a subtype of T
122 relation->Result(false);
123 if (relation->IsSupertypeOf(target, source)) {
124 return false;
125 }
126
127 // - If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype
128 // of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct
129 // NOTE: implement
130
131 // - One of the following cases applies:
132
133 if (source->HasTypeFlag(TypeFlag::ETS_OBJECT) && target->HasTypeFlag(TypeFlag::ETS_OBJECT)) {
134 if (IsAllowedNarrowingReferenceConversionObjectObject(relation, source->AsETSObjectType(),
135 target->AsETSObjectType())) {
136 return true;
137 }
138 }
139
140 if (source->HasTypeFlag(TypeFlag::ETS_OBJECT) && target->HasTypeFlag(TypeFlag::ETS_ARRAY)) {
141 // 7. S is the class type Object of the interface type java.io.Serializable or Cloneable (the only interfaces
142 // implemented by arrays (link to class objects for arrays)), and T is an array type.
143 // NOTE: implement
144 return true;
145 }
146
147 if (source->HasTypeFlag(TypeFlag::ETS_ARRAY) && target->HasTypeFlag(TypeFlag::ETS_ARRAY)) {
148 // 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
149 // array of components of type TC; and a narrowing reference conversion exists from SC to TC.
150 auto *sc = source->AsETSArrayType()->ElementType();
151 auto *tc = target->AsETSArrayType()->ElementType();
152
153 if (sc->IsETSObjectType() && tc->IsETSObjectType()) {
154 relation->Result(false);
155 NarrowingReference(relation, sc->AsETSObjectType(), tc->AsETSObjectType());
156 if (relation->IsTrue()) {
157 return true;
158 }
159 }
160 }
161
162 // 9. S is a type variable, and a narrowing reference conversion exists from the upper bound of S to T.
163 // NOTE:: implement
164
165 // 10. T is a type variable, and either a widening reference conversion or a narrowing reference conversion exists
166 // from S to the upper bound of T.
167 // NOTE: implement
168
169 // 11. S is an intersection type S1 & … & Sn, and for all i (1 ≤ i ≤ n), either a widening reference
170 // conversion or a
171 // narrowing reference conversion exists from Si to T.
172 // NOTE: implement
173
174 // 12. T is an intersection type T1 & … & Tn, and for all i (1 ≤ i ≤ n), either a widening reference
175 // conversion or a
176 // narrowing reference conversion exists from S to Ti.
177 // NOTE: implement
178
179 return false;
180 }
181
IsUncheckedNarrowingReferenceConversion(TypeRelation * const relation,Type * const source,Type * const target)182 bool IsUncheckedNarrowingReferenceConversion([[maybe_unused]] TypeRelation *const relation,
183 [[maybe_unused]] Type *const source, [[maybe_unused]] Type *const target)
184 {
185 ASSERT(source->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT) &&
186 target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT));
187
188 // The unchecked narrowing reference conversions are as follows:
189 // - A narrowing reference conversion from a type S to a parameterized class or interface
190 // type T is unchecked, unless at least one of the following is true:
191 // - All of the type arguments of T are unbounded wildcard.
192 // - T <: S, and S has no subtype X other than T where the type arguments of X
193 // are not contained in the type arguments of T.
194 // - A narrowing reference conversion from a type S to a type variable T is unchecked.
195 // - A narrowing reference conversion from a type S to an intersection type T1 & … & Tn
196 // is unchecked if there exists a Ti (1 ≤ i ≤ n) such that S is not a subtype of Ti, and a
197 // narrowing reference conversion from S to Ti is unchecked.
198 // NOTE: implement
199
200 return false;
201 }
202
NarrowingReferenceImpl(TypeRelation * const relation,Type * const source,Type * const target)203 void NarrowingReferenceImpl(TypeRelation *const relation, Type *const source, Type *const target)
204 {
205 ASSERT(target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT));
206
207 if (!IsAllowedNarrowingReferenceConversion(relation, source, target)) {
208 Forbidden(relation);
209 return;
210 }
211
212 if (!IsUncheckedNarrowingReferenceConversion(relation, source, target)) {
213 relation->RemoveFlags(TypeRelationFlag::UNCHECKED_CAST);
214 }
215
216 relation->Result(true);
217 }
218 } // namespace
219
NarrowingReference(TypeRelation * const relation,ETSObjectType * const source,ETSObjectType * const target)220 void NarrowingReference(TypeRelation *const relation, ETSObjectType *const source, ETSObjectType *const target)
221 {
222 NarrowingReferenceImpl(relation, source, target);
223 }
224
NarrowingReference(TypeRelation * const relation,ETSArrayType * const source,ETSArrayType * const target)225 void NarrowingReference(TypeRelation *const relation, ETSArrayType *const source, ETSArrayType *const target)
226 {
227 if (source->ElementType()->IsETSArrayType() && target->ElementType()->IsETSArrayType()) {
228 NarrowingReference(relation, source->ElementType()->AsETSArrayType(), target->ElementType()->AsETSArrayType());
229 return;
230 }
231
232 NarrowingReferenceImpl(relation, source, target);
233 }
234
NarrowingReference(TypeRelation * const relation,ETSObjectType * const source,ETSArrayType * const target)235 void NarrowingReference(TypeRelation *const relation, ETSObjectType *const source, ETSArrayType *const target)
236 {
237 if (target->ElementType()->IsETSArrayType()) {
238 NarrowingReference(relation, source, target->ElementType()->AsETSArrayType());
239 return;
240 }
241
242 NarrowingReferenceImpl(relation, source, target);
243 }
244
RollbackBoxingIfFailed(TypeRelation * const relation)245 static inline void RollbackBoxingIfFailed(TypeRelation *const relation)
246 {
247 if (!relation->IsTrue()) {
248 relation->GetNode()->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
249 }
250 }
251
Boxing(TypeRelation * const relation,Type * const source)252 ETSObjectType *Boxing(TypeRelation *const relation, Type *const source)
253 {
254 auto *const etsChecker = relation->GetChecker()->AsETSChecker();
255 const BoxingConverter boxed(etsChecker, relation, source);
256 if (!relation->IsTrue()) {
257 return nullptr;
258 }
259 auto *const boxedType = boxed.Result()->AsETSObjectType();
260 relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetBoxingFlag(boxedType));
261 return boxedType;
262 }
263
Unboxing(TypeRelation * const relation,ETSObjectType * const source)264 Type *Unboxing(TypeRelation *const relation, ETSObjectType *const source)
265 {
266 auto *const etsChecker = relation->GetChecker()->AsETSChecker();
267 const UnboxingConverter unboxed(etsChecker, relation, source);
268 if (!relation->IsTrue()) {
269 return nullptr;
270 }
271 auto *const unboxedType = unboxed.Result();
272 relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetUnboxingFlag(unboxedType));
273 return unboxedType;
274 }
275
UnboxingWideningPrimitive(TypeRelation * const relation,ETSObjectType * const source,Type * const target)276 void UnboxingWideningPrimitive(TypeRelation *const relation, ETSObjectType *const source, Type *const target)
277 {
278 auto *const unboxedSource = Unboxing(relation, source);
279 if (!relation->IsTrue()) {
280 return;
281 }
282 ASSERT(unboxedSource != nullptr);
283 WideningPrimitive(relation, target, unboxedSource);
284 RollbackBoxingIfFailed(relation);
285 }
286
UnboxingNarrowingPrimitive(TypeRelation * const relation,ETSObjectType * const source,Type * const target)287 void UnboxingNarrowingPrimitive(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 ASSERT(unboxedSource != nullptr);
294 NarrowingPrimitive(relation, target, unboxedSource);
295 }
296
UnboxingWideningNarrowingPrimitive(TypeRelation * const relation,ETSObjectType * const source,Type * const target)297 void UnboxingWideningNarrowingPrimitive(TypeRelation *const relation, ETSObjectType *const source, Type *const target)
298 {
299 auto *const unboxedSource = Unboxing(relation, source);
300 if (!relation->IsTrue()) {
301 return;
302 }
303 ASSERT(unboxedSource != nullptr);
304 WideningNarrowingPrimitive(relation, unboxedSource->AsByteType(), target->AsCharType());
305 }
306
NarrowingReferenceUnboxing(TypeRelation * const relation,ETSObjectType * const source,Type * const target)307 void NarrowingReferenceUnboxing(TypeRelation *const relation, ETSObjectType *const source, Type *const target)
308 {
309 auto *const boxedTarget = relation->GetChecker()->AsETSChecker()->PrimitiveTypeAsETSBuiltinType(target);
310 if (boxedTarget == nullptr) {
311 return;
312 }
313 ASSERT(boxedTarget != nullptr);
314 NarrowingReference(relation, source, boxedTarget->AsETSObjectType());
315 if (!relation->IsTrue()) {
316 return;
317 }
318 Unboxing(relation, boxedTarget->AsETSObjectType());
319 }
320
BoxingWideningReference(TypeRelation * const relation,Type * const source,ETSObjectType * const target)321 void BoxingWideningReference(TypeRelation *const relation, Type *const source, ETSObjectType *const target)
322 {
323 auto *const boxedSource = Boxing(relation, source);
324 if (!relation->IsTrue()) {
325 return;
326 }
327 ASSERT(boxedSource != nullptr);
328 WideningReference(relation, boxedSource, target);
329 RollbackBoxingIfFailed(relation);
330 }
331
String(TypeRelation * const relation,Type * const source)332 void String(TypeRelation *const relation, Type *const source)
333 {
334 if (source->HasTypeFlag(TypeFlag::BYTE | TypeFlag::SHORT)) {
335 auto *const tempInt = relation->GetChecker()->AsETSChecker()->GetGlobalTypesHolder()->GlobalIntType();
336 WideningPrimitive(relation, source, tempInt);
337 Boxing(relation, tempInt);
338 relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::CONVERT_TO_STRING);
339 return;
340 }
341
342 if (source->HasTypeFlag(TypeFlag::ETS_BOOLEAN | TypeFlag::CHAR | TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT |
343 TypeFlag::DOUBLE)) {
344 Boxing(relation, source);
345 relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::CONVERT_TO_STRING);
346 return;
347 }
348
349 ASSERT(source->HasTypeFlag(TypeFlag::ETS_OBJECT));
350 }
351
Forbidden(TypeRelation * const relation)352 void Forbidden(TypeRelation *const relation)
353 {
354 relation->Result(false);
355 }
356
357 } // namespace ark::es2panda::checker::conversion
358