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
24namespace ark::es2panda::checker::conversion {
25void Identity(TypeRelation *const relation, Type *const source, Type *const target)
26{
27    relation->IsIdenticalTo(source, target);
28}
29
30void 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
37void 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
44void 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
54void WideningReference(TypeRelation *const relation, ETSObjectType *const source, ETSObjectType *const target)
55{
56    relation->IsSupertypeOf(target, source);
57}
58
59void WideningReference(TypeRelation *const relation, ETSArrayType *const source, ETSObjectType *const target)
60{
61    relation->IsSupertypeOf(target, source);
62}
63
64void WideningReference(TypeRelation *const relation, ETSArrayType *const source, ETSArrayType *const target)
65{
66    relation->IsSupertypeOf(target, source);
67}
68
69namespace {
70
71bool 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
117bool 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
187bool 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
208void 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
225void NarrowingReference(TypeRelation *const relation, ETSObjectType *const source, ETSObjectType *const target)
226{
227    NarrowingReferenceImpl(relation, source, target);
228}
229
230void 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
240void 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
250static inline void RollbackBoxingIfFailed(TypeRelation *const relation)
251{
252    if (!relation->IsTrue()) {
253        relation->GetNode()->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
254    }
255}
256
257ETSObjectType *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
269Type *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
281void 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
292void 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
302void 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
312void NarrowingReferenceUnboxing(TypeRelation *const relation, ETSObjectType *const source, Type *const target)
313{
314    auto *const boxedTarget = relation->GetChecker()->AsETSChecker()->PrimitiveTypeAsETSBuiltinType(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
326void 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
337void 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
357void Forbidden(TypeRelation *const relation)
358{
359    relation->Result(false);
360}
361
362}  // namespace ark::es2panda::checker::conversion
363