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#ifndef ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_CONVERTER_H
17#define ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_CONVERTER_H
18
19#include "checker/ets/typeConverter.h"
20#include "checker/ETSchecker.h"
21#include "util/helpers.h"
22
23namespace ark::es2panda::checker {
24class NarrowingConverter : public TypeConverter {
25public:
26    explicit NarrowingConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source)
27        : TypeConverter(checker, relation, target, source)
28    {
29        if (!relation->ApplyNarrowing()) {
30            return;
31        }
32
33        ASSERT(relation->GetNode());
34
35        switch (ETSChecker::ETSChecker::ETSType(target)) {
36            case TypeFlag::BYTE: {
37                ApplyNarrowing<ByteType>(TypeFlag::NARROWABLE_TO_BYTE);
38                break;
39            }
40            case TypeFlag::CHAR: {
41                ApplyNarrowing<CharType>(TypeFlag::NARROWABLE_TO_CHAR);
42                break;
43            }
44            case TypeFlag::SHORT: {
45                ApplyNarrowing<ShortType>(TypeFlag::NARROWABLE_TO_SHORT);
46                break;
47            }
48            case TypeFlag::INT: {
49                ApplyNarrowing<IntType>(TypeFlag::NARROWABLE_TO_INT);
50                break;
51            }
52            case TypeFlag::LONG: {
53                ApplyNarrowing<LongType>(TypeFlag::NARROWABLE_TO_LONG);
54                break;
55            }
56            case TypeFlag::FLOAT: {
57                ApplyNarrowing<FloatType>(TypeFlag::NARROWABLE_TO_FLOAT);
58                break;
59            }
60
61            default: {
62                break;
63            }
64        }
65    }
66
67private:
68    template <typename TargetType>
69    void ApplyNarrowing(TypeFlag flag)
70    {
71        if (!Source()->HasTypeFlag(flag)) {
72            return;
73        }
74
75        switch (ETSChecker::ETSChecker::ETSType(Source())) {
76            case TypeFlag::CHAR: {
77                ApplyNarrowing<TargetType, CharType>();
78                break;
79            }
80            case TypeFlag::SHORT: {
81                ApplyNarrowing<TargetType, ShortType>();
82                break;
83            }
84            case TypeFlag::INT: {
85                ApplyNarrowing<TargetType, IntType>();
86                break;
87            }
88            case TypeFlag::LONG: {
89                ApplyNarrowing<TargetType, LongType>();
90                break;
91            }
92            case TypeFlag::FLOAT: {
93                ApplyNarrowing<TargetType, FloatType>();
94                break;
95            }
96            case TypeFlag::DOUBLE: {
97                ApplyNarrowing<TargetType, DoubleType>();
98                break;
99            }
100            default: {
101                break;
102            }
103        }
104    }
105
106    template <typename From, typename To>
107    To CastFloatingPointToIntOrLong(From value)
108    {
109        if (std::isinf(value)) {
110            if (std::signbit(value)) {
111                return std::numeric_limits<To>::min();
112            }
113            return std::numeric_limits<To>::max();
114        }
115        ASSERT(std::is_floating_point_v<From>);
116        ASSERT(std::is_integral_v<To>);
117        To minInt = std::numeric_limits<To>::min();
118        To maxInt = std::numeric_limits<To>::max();
119        auto floatMinInt = static_cast<From>(minInt);
120        auto floatMaxInt = static_cast<From>(maxInt);
121
122        if (value > floatMinInt) {
123            if (value < floatMaxInt) {
124                return static_cast<To>(value);
125            }
126            return maxInt;
127        }
128        if (std::isnan(value)) {
129            return 0;
130        }
131        return minInt;
132    }
133
134    template <typename TType, typename SType>
135    TType CalculateNarrowedValue(Type *target, Type *source, SType value)
136    {
137        switch (ETSChecker::ETSChecker::ETSType(target)) {
138            case TypeFlag::BYTE:
139            case TypeFlag::CHAR:
140            case TypeFlag::SHORT: {
141                if (source->HasTypeFlag(checker::TypeFlag::DOUBLE) || source->HasTypeFlag(checker::TypeFlag::FLOAT)) {
142                    return static_cast<TType>(CastFloatingPointToIntOrLong<SType, int32_t>(value));
143                }
144                return static_cast<TType>(value);
145            }
146            case TypeFlag::INT:
147            case TypeFlag::LONG: {
148                if (source->HasTypeFlag(checker::TypeFlag::DOUBLE) || source->HasTypeFlag(checker::TypeFlag::FLOAT)) {
149                    return CastFloatingPointToIntOrLong<SType, TType>(value);
150                }
151                return static_cast<TType>(value);
152            }
153            case TypeFlag::FLOAT:
154            case TypeFlag::DOUBLE: {
155                return static_cast<TType>(value);
156            }
157            default: {
158                UNREACHABLE();
159            }
160        }
161    }
162
163    template <typename TargetType, typename SourceType>
164    void ApplyNarrowing()
165    {
166        using SType = typename SourceType::UType;
167        using TType = typename TargetType::UType;
168
169        if (Source()->HasTypeFlag(TypeFlag::CONSTANT)) {
170            SType value = reinterpret_cast<SourceType *>(Source())->GetValue();
171            if (!Relation()->InCastingContext() && Source()->HasTypeFlag(TypeFlag::ETS_FLOATING_POINT) &&
172                Target()->HasTypeFlag(TypeFlag::ETS_INTEGRAL)) {
173                auto narrowedValue = CalculateNarrowedValue<TType, SType>(Target(), Source(), value);
174                if (narrowedValue != value) {
175                    Relation()->Result(RelationResult::ERROR);
176                    return;
177                }
178            }
179
180            if (Relation()->InCastingContext() || util::Helpers::IsTargetFitInSourceRange<TType, SType>(value)) {
181                auto narrowedValue = CalculateNarrowedValue<TType, SType>(Target(), Source(), value);
182                TargetType *newType = Checker()->Allocator()->New<TargetType>(narrowedValue);
183                Relation()->GetNode()->SetTsType(newType);
184                Relation()->Result(true);
185                return;
186            }
187
188            Relation()->Result(RelationResult::ERROR);
189            return;
190        }
191
192        Relation()->Result(true);
193    }
194};
195}  // namespace ark::es2panda::checker
196
197#endif
198