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 ECMASCRIPT_JS_NUMBER_H 17 #define ECMASCRIPT_JS_NUMBER_H 18 19 #include "ecmascript/base/number_helper.h" 20 21 #include "ecmascript/ecma_macros.h" 22 #include "ecmascript/ecma_vm.h" 23 #include "ecmascript/js_hclass.h" 24 #include "ecmascript/js_tagged_value.h" 25 #include "ecmascript/js_thread.h" 26 #include "ecmascript/object_factory.h" 27 28 namespace panda { 29 namespace ecmascript { 30 class JSTaggedNumber final : public JSTaggedValue { 31 public: 32 constexpr JSTaggedNumber() = default; JSTaggedNumber(double v)33 explicit JSTaggedNumber(double v) : JSTaggedValue(v) {} JSTaggedNumber(int v)34 constexpr explicit JSTaggedNumber(int v) : JSTaggedValue(v) {} JSTaggedNumber(unsigned int v)35 explicit JSTaggedNumber(unsigned int v) : JSTaggedValue(v) {} JSTaggedNumber(JSTaggedValue v)36 explicit JSTaggedNumber(JSTaggedValue v) : JSTaggedValue(v.GetRawData()) 37 { 38 ASSERT_PRINT(v.IsNumber(), "can not convert non Number JSTaggedValue to JSTaggedNumber"); 39 } 40 41 ~JSTaggedNumber() = default; 42 DEFAULT_COPY_SEMANTIC(JSTaggedNumber); 43 DEFAULT_MOVE_SEMANTIC(JSTaggedNumber); 44 Exception()45 static inline constexpr JSTaggedNumber Exception() 46 { 47 return JSTaggedNumber(VALUE_EXCEPTION); 48 } 49 IsException()50 inline bool IsException() const 51 { 52 return JSTaggedValue::IsException(); 53 } 54 ToInt32()55 inline int32_t ToInt32() const 56 { 57 if (IsInt()) { 58 return GetInt(); 59 } 60 return base::NumberHelper::DoubleToInt(GetDouble(), base::INT32_BITS); 61 } 62 ToUint32()63 inline uint32_t ToUint32() const 64 { 65 return ToInt32(); 66 } 67 ToInt16()68 inline int16_t ToInt16() const 69 { 70 return base::NumberHelper::DoubleToInt(GetNumber(), base::INT16_BITS); 71 } 72 ToUint16()73 inline uint16_t ToUint16() const 74 { 75 return ToInt16(); 76 } 77 ToInt8()78 inline int8_t ToInt8() const 79 { 80 return base::NumberHelper::DoubleToInt(GetNumber(), base::INT8_BITS); 81 } 82 ToUint8()83 inline uint8_t ToUint8() const 84 { 85 return ToInt8(); 86 } 87 ToString(const JSThread * thread)88 inline JSHandle<EcmaString> ToString(const JSThread *thread) const 89 { 90 return base::NumberHelper::NumberToString(thread, *this); 91 } 92 93 JSTaggedNumber operator-(JSTaggedNumber number) const 94 { 95 if (IsInt() && number.IsInt()) { 96 int64_t a0 = GetInt(); 97 int64_t a1 = number.GetInt(); 98 int64_t res = a0 - a1; 99 if (res > INT32_MAX || res < INT32_MIN) { 100 return JSTaggedNumber(static_cast<double>(res)); 101 } 102 return JSTaggedNumber(static_cast<int>(res)); 103 } 104 return JSTaggedNumber(GetNumber() - number.GetNumber()); 105 } 106 107 JSTaggedNumber operator*(JSTaggedNumber number) const 108 { 109 if (IsInt() && number.IsInt()) { 110 int64_t intA = GetInt(); 111 int64_t intB = number.GetInt(); 112 int64_t res = intA * intB; 113 if (res > INT32_MAX || res < INT32_MIN) { 114 return JSTaggedNumber(static_cast<double>(res)); 115 } 116 if (res == 0 && (intA < 0 || intB < 0)) { 117 return JSTaggedNumber(-0.0); 118 } 119 return JSTaggedNumber(static_cast<int>(res)); 120 } 121 return JSTaggedNumber(GetNumber() * number.GetNumber()); 122 } 123 124 JSTaggedNumber operator++() const 125 { 126 if (IsInt()) { 127 int32_t value = GetInt(); 128 if (value == INT32_MAX) { 129 return JSTaggedNumber(static_cast<double>(value) + 1.0); 130 } 131 return JSTaggedNumber(value + 1); 132 } 133 ASSERT(IsDouble()); 134 double doubleVal = GetDouble(); 135 if (UNLIKELY(std::isnan(doubleVal) || !std::isfinite(doubleVal))) { 136 return JSTaggedNumber(doubleVal); 137 } 138 return JSTaggedNumber(doubleVal + 1.0); 139 } 140 141 JSTaggedNumber operator--() const 142 { 143 if (IsInt()) { 144 int32_t value = GetInt(); 145 if (value == INT32_MIN) { 146 return JSTaggedNumber(static_cast<double>(value) - 1.0); 147 } 148 return JSTaggedNumber(value - 1); 149 } 150 ASSERT(IsDouble()); 151 double doubleVal = GetDouble(); 152 if (UNLIKELY(std::isnan(doubleVal) || !std::isfinite(doubleVal))) { 153 return JSTaggedNumber(doubleVal); 154 } 155 return JSTaggedNumber(doubleVal - 1.0); 156 } 157 158 inline bool operator!=(const JSTaggedNumber &number) const 159 { 160 return GetNumber() != number.GetNumber(); 161 } 162 163 /* static */ SameValue(JSTaggedNumber x,JSTaggedNumber y)164 inline static bool SameValue(JSTaggedNumber x, JSTaggedNumber y) 165 { 166 double xValue = x.GetNumber(); 167 double yValue = y.GetNumber(); 168 // SameNumberValue(NaN, NaN) is true. 169 if (xValue != yValue) { 170 return std::isnan(xValue) && std::isnan(yValue); 171 } 172 // SameNumberValue(0.0, -0.0) is false. 173 return (std::signbit(xValue) == std::signbit(yValue)); 174 } 175 FromIntOrDouble(JSThread * thread,JSTaggedValue tagged)176 inline static JSTaggedNumber FromIntOrDouble(JSThread *thread, JSTaggedValue tagged) 177 { 178 if (tagged.IsInt() || tagged.IsDouble()) { 179 return JSTaggedNumber(tagged); 180 } 181 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert value to a number", JSTaggedNumber::Exception()); 182 } 183 184 private: JSTaggedNumber(JSTaggedType v)185 constexpr explicit JSTaggedNumber(JSTaggedType v) : JSTaggedValue(v) {} 186 }; 187 } // namespace ecmascript 188 } // namespace panda 189 #endif // ECMASCRIPT_JS_NUMBER_H 190