1/*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2004-2011, 2013, 2016 Apple Inc. All rights reserved.
4 * Copyright (C) 2007 Samuel Weinig <sam@webkit.org>
5 * Copyright (C) 2013 Michael Pruett <michael@68k.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "config.h"
23#include "JSDOMConvertNumbers.h"
24
25#include "JSDOMExceptionHandling.h"
26#include <JavaScriptCore/HeapInlines.h>
27#include <JavaScriptCore/JSCJSValueInlines.h>
28#include <wtf/MathExtras.h>
29#include <wtf/text/StringConcatenateNumbers.h>
30#include <wtf/text/WTFString.h>
31
32namespace WebCore {
33using namespace JSC;
34
35enum class IntegerConversionConfiguration { Normal, EnforceRange, Clamp };
36
37static const int32_t kMaxInt32 = 0x7fffffff;
38static const int32_t kMinInt32 = -kMaxInt32 - 1;
39static const uint32_t kMaxUInt32 = 0xffffffffU;
40static const int64_t kJSMaxInteger = 0x20000000000000LL - 1; // 2^53 - 1, largest integer exactly representable in ECMAScript.
41
42static String rangeErrorString(double value, double min, double max)
43{
44 return makeString("Value ", value, " is outside the range [", min, ", ", max, ']');
45}
46
47static double enforceRange(ExecState& state, double x, double minimum, double maximum)
48{
49 VM& vm = state.vm();
50 auto scope = DECLARE_THROW_SCOPE(vm);
51
52 if (std::isnan(x) || std::isinf(x)) {
53 throwTypeError(&state, scope, rangeErrorString(x, minimum, maximum));
54 return 0;
55 }
56 x = trunc(x);
57 if (x < minimum || x > maximum) {
58 throwTypeError(&state, scope, rangeErrorString(x, minimum, maximum));
59 return 0;
60 }
61 return x;
62}
63
64namespace {
65
66template <typename T>
67struct IntTypeLimits {
68};
69
70template <>
71struct IntTypeLimits<int8_t> {
72 static const int8_t minValue = -128;
73 static const int8_t maxValue = 127;
74 static const unsigned numberOfValues = 256; // 2^8
75};
76
77template <>
78struct IntTypeLimits<uint8_t> {
79 static const uint8_t maxValue = 255;
80 static const unsigned numberOfValues = 256; // 2^8
81};
82
83template <>
84struct IntTypeLimits<int16_t> {
85 static const short minValue = -32768;
86 static const short maxValue = 32767;
87 static const unsigned numberOfValues = 65536; // 2^16
88};
89
90template <>
91struct IntTypeLimits<uint16_t> {
92 static const unsigned short maxValue = 65535;
93 static const unsigned numberOfValues = 65536; // 2^16
94};
95
96}
97
98template <typename T, IntegerConversionConfiguration configuration>
99static inline T toSmallerInt(ExecState& state, JSValue value)
100{
101 VM& vm = state.vm();
102 auto scope = DECLARE_THROW_SCOPE(vm);
103
104 static_assert(std::is_signed<T>::value && std::is_integral<T>::value, "Should only be used for signed integral types");
105
106 typedef IntTypeLimits<T> LimitsTrait;
107 // Fast path if the value is already a 32-bit signed integer in the right range.
108 if (value.isInt32()) {
109 int32_t d = value.asInt32();
110 if (d >= LimitsTrait::minValue && d <= LimitsTrait::maxValue)
111 return static_cast<T>(d);
112 switch (configuration) {
113 case IntegerConversionConfiguration::Normal:
114 break;
115 case IntegerConversionConfiguration::EnforceRange:
116 throwTypeError(&state, scope);
117 return 0;
118 case IntegerConversionConfiguration::Clamp:
119 return d < LimitsTrait::minValue ? LimitsTrait::minValue : LimitsTrait::maxValue;
120 }
121 d %= LimitsTrait::numberOfValues;
122 return static_cast<T>(d > LimitsTrait::maxValue ? d - LimitsTrait::numberOfValues : d);
123 }
124
125 double x = value.toNumber(&state);
126 RETURN_IF_EXCEPTION(scope, 0);
127
128 switch (configuration) {
129 case IntegerConversionConfiguration::Normal:
130 break;
131 case IntegerConversionConfiguration::EnforceRange:
132 return enforceRange(state, x, LimitsTrait::minValue, LimitsTrait::maxValue);
133 case IntegerConversionConfiguration::Clamp:
134 return std::isnan(x) ? 0 : clampTo<T>(x);
135 }
136
137 if (std::isnan(x) || std::isinf(x) || !x)
138 return 0;
139
140 x = x < 0 ? -floor(fabs(x)) : floor(fabs(x));
141 x = fmod(x, LimitsTrait::numberOfValues);
142
143 return static_cast<T>(x > LimitsTrait::maxValue ? x - LimitsTrait::numberOfValues : x);
144}
145
146template <typename T, IntegerConversionConfiguration configuration>
147static inline T toSmallerUInt(ExecState& state, JSValue value)
148{
149 VM& vm = state.vm();
150 auto scope = DECLARE_THROW_SCOPE(vm);
151
152 static_assert(std::is_unsigned<T>::value && std::is_integral<T>::value, "Should only be used for unsigned integral types");
153
154 typedef IntTypeLimits<T> LimitsTrait;
155 // Fast path if the value is already a 32-bit unsigned integer in the right range.
156 if (value.isUInt32()) {
157 uint32_t d = value.asUInt32();
158 if (d <= LimitsTrait::maxValue)
159 return static_cast<T>(d);
160 switch (configuration) {
161 case IntegerConversionConfiguration::Normal:
162 return static_cast<T>(d);
163 case IntegerConversionConfiguration::EnforceRange:
164 throwTypeError(&state, scope);
165 return 0;
166 case IntegerConversionConfiguration::Clamp:
167 return LimitsTrait::maxValue;
168 }
169 }
170
171 double x = value.toNumber(&state);
172 RETURN_IF_EXCEPTION(scope, 0);
173
174 switch (configuration) {
175 case IntegerConversionConfiguration::Normal:
176 break;
177 case IntegerConversionConfiguration::EnforceRange:
178 return enforceRange(state, x, 0, LimitsTrait::maxValue);
179 case IntegerConversionConfiguration::Clamp:
180 return std::isnan(x) ? 0 : clampTo<T>(x);
181 }
182
183 if (std::isnan(x) || std::isinf(x) || !x)
184 return 0;
185
186 x = x < 0 ? -floor(fabs(x)) : floor(fabs(x));
187 return static_cast<T>(fmod(x, LimitsTrait::numberOfValues));
188}
189
190template<> int8_t convertToIntegerEnforceRange<int8_t>(JSC::ExecState& state, JSC::JSValue value)
191{
192 return toSmallerInt<int8_t, IntegerConversionConfiguration::EnforceRange>(state, value);
193}
194
195template<> uint8_t convertToIntegerEnforceRange<uint8_t>(JSC::ExecState& state, JSC::JSValue value)
196{
197 return toSmallerUInt<uint8_t, IntegerConversionConfiguration::EnforceRange>(state, value);
198}
199
200template<> int8_t convertToIntegerClamp<int8_t>(JSC::ExecState& state, JSC::JSValue value)
201{
202 return toSmallerInt<int8_t, IntegerConversionConfiguration::Clamp>(state, value);
203}
204
205template<> uint8_t convertToIntegerClamp<uint8_t>(JSC::ExecState& state, JSC::JSValue value)
206{
207 return toSmallerUInt<uint8_t, IntegerConversionConfiguration::Clamp>(state, value);
208}
209
210template<> int8_t convertToInteger<int8_t>(JSC::ExecState& state, JSC::JSValue value)
211{
212 return toSmallerInt<int8_t, IntegerConversionConfiguration::Normal>(state, value);
213}
214
215template<> uint8_t convertToInteger<uint8_t>(JSC::ExecState& state, JSC::JSValue value)
216{
217 return toSmallerUInt<uint8_t, IntegerConversionConfiguration::Normal>(state, value);
218}
219
220template<> int16_t convertToIntegerEnforceRange<int16_t>(JSC::ExecState& state, JSC::JSValue value)
221{
222 return toSmallerInt<int16_t, IntegerConversionConfiguration::EnforceRange>(state, value);
223}
224
225template<> uint16_t convertToIntegerEnforceRange<uint16_t>(JSC::ExecState& state, JSC::JSValue value)
226{
227 return toSmallerUInt<uint16_t, IntegerConversionConfiguration::EnforceRange>(state, value);
228}
229
230template<> int16_t convertToIntegerClamp<int16_t>(JSC::ExecState& state, JSC::JSValue value)
231{
232 return toSmallerInt<int16_t, IntegerConversionConfiguration::Clamp>(state, value);
233}
234
235template<> uint16_t convertToIntegerClamp<uint16_t>(JSC::ExecState& state, JSC::JSValue value)
236{
237 return toSmallerUInt<uint16_t, IntegerConversionConfiguration::Clamp>(state, value);
238}
239
240template<> int16_t convertToInteger<int16_t>(JSC::ExecState& state, JSC::JSValue value)
241{
242 return toSmallerInt<int16_t, IntegerConversionConfiguration::Normal>(state, value);
243}
244
245template<> uint16_t convertToInteger<uint16_t>(JSC::ExecState& state, JSC::JSValue value)
246{
247 return toSmallerUInt<uint16_t, IntegerConversionConfiguration::Normal>(state, value);
248}
249
250template<> int32_t convertToIntegerEnforceRange<int32_t>(JSC::ExecState& state, JSC::JSValue value)
251{
252 if (value.isInt32())
253 return value.asInt32();
254
255 VM& vm = state.vm();
256 auto scope = DECLARE_THROW_SCOPE(vm);
257
258 double x = value.toNumber(&state);
259 RETURN_IF_EXCEPTION(scope, 0);
260 return enforceRange(state, x, kMinInt32, kMaxInt32);
261}
262
263template<> uint32_t convertToIntegerEnforceRange<uint32_t>(JSC::ExecState& state, JSC::JSValue value)
264{
265 if (value.isUInt32())
266 return value.asUInt32();
267
268 VM& vm = state.vm();
269 auto scope = DECLARE_THROW_SCOPE(vm);
270
271 double x = value.toNumber(&state);
272 RETURN_IF_EXCEPTION(scope, 0);
273 return enforceRange(state, x, 0, kMaxUInt32);
274}
275
276template<> int32_t convertToIntegerClamp<int32_t>(JSC::ExecState& state, JSC::JSValue value)
277{
278 if (value.isInt32())
279 return value.asInt32();
280
281 double x = value.toNumber(&state);
282 return std::isnan(x) ? 0 : clampTo<int32_t>(x);
283}
284
285template<> uint32_t convertToIntegerClamp<uint32_t>(JSC::ExecState& state, JSC::JSValue value)
286{
287 if (value.isUInt32())
288 return value.asUInt32();
289
290 double x = value.toNumber(&state);
291 return std::isnan(x) ? 0 : clampTo<uint32_t>(x);
292}
293
294template<> int32_t convertToInteger<int32_t>(JSC::ExecState& state, JSC::JSValue value)
295{
296 return value.toInt32(&state);
297}
298
299template<> uint32_t convertToInteger<uint32_t>(JSC::ExecState& state, JSC::JSValue value)
300{
301 return value.toUInt32(&state);
302}
303
304template<> int64_t convertToIntegerEnforceRange<int64_t>(JSC::ExecState& state, JSC::JSValue value)
305{
306 if (value.isInt32())
307 return value.asInt32();
308
309 VM& vm = state.vm();
310 auto scope = DECLARE_THROW_SCOPE(vm);
311
312 double x = value.toNumber(&state);
313 RETURN_IF_EXCEPTION(scope, 0);
314 return enforceRange(state, x, -kJSMaxInteger, kJSMaxInteger);
315}
316
317template<> uint64_t convertToIntegerEnforceRange<uint64_t>(JSC::ExecState& state, JSC::JSValue value)
318{
319 if (value.isUInt32())
320 return value.asUInt32();
321
322 VM& vm = state.vm();
323 auto scope = DECLARE_THROW_SCOPE(vm);
324
325 double x = value.toNumber(&state);
326 RETURN_IF_EXCEPTION(scope, 0);
327 return enforceRange(state, x, 0, kJSMaxInteger);
328}
329
330template<> int64_t convertToIntegerClamp<int64_t>(JSC::ExecState& state, JSC::JSValue value)
331{
332 if (value.isInt32())
333 return value.asInt32();
334
335 double x = value.toNumber(&state);
336 return std::isnan(x) ? 0 : static_cast<int64_t>(std::min<double>(std::max<double>(x, -kJSMaxInteger), kJSMaxInteger));
337}
338
339template<> uint64_t convertToIntegerClamp<uint64_t>(JSC::ExecState& state, JSC::JSValue value)
340{
341 if (value.isUInt32())
342 return value.asUInt32();
343
344 double x = value.toNumber(&state);
345 return std::isnan(x) ? 0 : static_cast<uint64_t>(std::min<double>(std::max<double>(x, 0), kJSMaxInteger));
346}
347
348template<> int64_t convertToInteger<int64_t>(JSC::ExecState& state, JSC::JSValue value)
349{
350 if (value.isInt32())
351 return value.asInt32();
352
353 double x = value.toNumber(&state);
354
355 // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
356 unsigned long long n;
357 doubleToInteger(x, n);
358 return n;
359}
360
361template<> uint64_t convertToInteger<uint64_t>(JSC::ExecState& state, JSC::JSValue value)
362{
363 if (value.isUInt32())
364 return value.asUInt32();
365
366 double x = value.toNumber(&state);
367
368 // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
369 unsigned long long n;
370 doubleToInteger(x, n);
371 return n;
372}
373
374} // namespace WebCore
375