/* * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_CONVERTER_H #define ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_CONVERTER_H #include "checker/ets/typeConverter.h" #include "checker/ETSchecker.h" #include "util/helpers.h" namespace ark::es2panda::checker { class NarrowingConverter : public TypeConverter { public: explicit NarrowingConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source) : TypeConverter(checker, relation, target, source) { if (!relation->ApplyNarrowing()) { return; } ASSERT(relation->GetNode()); switch (ETSChecker::ETSChecker::ETSType(target)) { case TypeFlag::BYTE: { ApplyNarrowing(TypeFlag::NARROWABLE_TO_BYTE); break; } case TypeFlag::CHAR: { ApplyNarrowing(TypeFlag::NARROWABLE_TO_CHAR); break; } case TypeFlag::SHORT: { ApplyNarrowing(TypeFlag::NARROWABLE_TO_SHORT); break; } case TypeFlag::INT: { ApplyNarrowing(TypeFlag::NARROWABLE_TO_INT); break; } case TypeFlag::LONG: { ApplyNarrowing(TypeFlag::NARROWABLE_TO_LONG); break; } case TypeFlag::FLOAT: { ApplyNarrowing(TypeFlag::NARROWABLE_TO_FLOAT); break; } default: { break; } } } private: template void ApplyNarrowing(TypeFlag flag) { if (!Source()->HasTypeFlag(flag)) { return; } switch (ETSChecker::ETSChecker::ETSType(Source())) { case TypeFlag::CHAR: { ApplyNarrowing(); break; } case TypeFlag::SHORT: { ApplyNarrowing(); break; } case TypeFlag::INT: { ApplyNarrowing(); break; } case TypeFlag::LONG: { ApplyNarrowing(); break; } case TypeFlag::FLOAT: { ApplyNarrowing(); break; } case TypeFlag::DOUBLE: { ApplyNarrowing(); break; } default: { break; } } } template To CastFloatingPointToIntOrLong(From value) { if (std::isinf(value)) { if (std::signbit(value)) { return std::numeric_limits::min(); } return std::numeric_limits::max(); } ASSERT(std::is_floating_point_v); ASSERT(std::is_integral_v); To minInt = std::numeric_limits::min(); To maxInt = std::numeric_limits::max(); auto floatMinInt = static_cast(minInt); auto floatMaxInt = static_cast(maxInt); if (value > floatMinInt) { if (value < floatMaxInt) { return static_cast(value); } return maxInt; } if (std::isnan(value)) { return 0; } return minInt; } template TType CalculateNarrowedValue(Type *target, Type *source, SType value) { switch (ETSChecker::ETSChecker::ETSType(target)) { case TypeFlag::BYTE: case TypeFlag::CHAR: case TypeFlag::SHORT: { if (source->HasTypeFlag(checker::TypeFlag::DOUBLE) || source->HasTypeFlag(checker::TypeFlag::FLOAT)) { return static_cast(CastFloatingPointToIntOrLong(value)); } return static_cast(value); } case TypeFlag::INT: case TypeFlag::LONG: { if (source->HasTypeFlag(checker::TypeFlag::DOUBLE) || source->HasTypeFlag(checker::TypeFlag::FLOAT)) { return CastFloatingPointToIntOrLong(value); } return static_cast(value); } case TypeFlag::FLOAT: case TypeFlag::DOUBLE: { return static_cast(value); } default: { UNREACHABLE(); } } } template void ApplyNarrowing() { using SType = typename SourceType::UType; using TType = typename TargetType::UType; if (Source()->HasTypeFlag(TypeFlag::CONSTANT)) { SType value = reinterpret_cast(Source())->GetValue(); if (!Relation()->InCastingContext() && Source()->HasTypeFlag(TypeFlag::ETS_FLOATING_POINT) && Target()->HasTypeFlag(TypeFlag::ETS_INTEGRAL)) { auto narrowedValue = CalculateNarrowedValue(Target(), Source(), value); if (narrowedValue != value) { Relation()->Result(RelationResult::ERROR); return; } } if (Relation()->InCastingContext() || util::Helpers::IsTargetFitInSourceRange(value)) { auto narrowedValue = CalculateNarrowedValue(Target(), Source(), value); TargetType *newType = Checker()->Allocator()->New(narrowedValue); Relation()->GetNode()->SetTsType(newType); Relation()->Result(true); return; } Relation()->Result(RelationResult::ERROR); return; } Relation()->Result(true); } }; } // namespace ark::es2panda::checker #endif