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 | |
32 | namespace WebCore { |
33 | using namespace JSC; |
34 | |
35 | enum class IntegerConversionConfiguration { Normal, EnforceRange, Clamp }; |
36 | |
37 | static const int32_t kMaxInt32 = 0x7fffffff; |
38 | static const int32_t kMinInt32 = -kMaxInt32 - 1; |
39 | static const uint32_t kMaxUInt32 = 0xffffffffU; |
40 | static const int64_t kJSMaxInteger = 0x20000000000000LL - 1; // 2^53 - 1, largest integer exactly representable in ECMAScript. |
41 | |
42 | static String rangeErrorString(double value, double min, double max) |
43 | { |
44 | return makeString("Value " , value, " is outside the range [" , min, ", " , max, ']'); |
45 | } |
46 | |
47 | static 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 | |
64 | namespace { |
65 | |
66 | template <typename T> |
67 | struct IntTypeLimits { |
68 | }; |
69 | |
70 | template <> |
71 | struct 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 | |
77 | template <> |
78 | struct IntTypeLimits<uint8_t> { |
79 | static const uint8_t maxValue = 255; |
80 | static const unsigned numberOfValues = 256; // 2^8 |
81 | }; |
82 | |
83 | template <> |
84 | struct 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 | |
90 | template <> |
91 | struct IntTypeLimits<uint16_t> { |
92 | static const unsigned short maxValue = 65535; |
93 | static const unsigned numberOfValues = 65536; // 2^16 |
94 | }; |
95 | |
96 | } |
97 | |
98 | template <typename T, IntegerConversionConfiguration configuration> |
99 | static 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 | |
146 | template <typename T, IntegerConversionConfiguration configuration> |
147 | static 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 | |
190 | template<> int8_t convertToIntegerEnforceRange<int8_t>(JSC::ExecState& state, JSC::JSValue value) |
191 | { |
192 | return toSmallerInt<int8_t, IntegerConversionConfiguration::EnforceRange>(state, value); |
193 | } |
194 | |
195 | template<> uint8_t convertToIntegerEnforceRange<uint8_t>(JSC::ExecState& state, JSC::JSValue value) |
196 | { |
197 | return toSmallerUInt<uint8_t, IntegerConversionConfiguration::EnforceRange>(state, value); |
198 | } |
199 | |
200 | template<> int8_t convertToIntegerClamp<int8_t>(JSC::ExecState& state, JSC::JSValue value) |
201 | { |
202 | return toSmallerInt<int8_t, IntegerConversionConfiguration::Clamp>(state, value); |
203 | } |
204 | |
205 | template<> uint8_t convertToIntegerClamp<uint8_t>(JSC::ExecState& state, JSC::JSValue value) |
206 | { |
207 | return toSmallerUInt<uint8_t, IntegerConversionConfiguration::Clamp>(state, value); |
208 | } |
209 | |
210 | template<> int8_t convertToInteger<int8_t>(JSC::ExecState& state, JSC::JSValue value) |
211 | { |
212 | return toSmallerInt<int8_t, IntegerConversionConfiguration::Normal>(state, value); |
213 | } |
214 | |
215 | template<> uint8_t convertToInteger<uint8_t>(JSC::ExecState& state, JSC::JSValue value) |
216 | { |
217 | return toSmallerUInt<uint8_t, IntegerConversionConfiguration::Normal>(state, value); |
218 | } |
219 | |
220 | template<> int16_t convertToIntegerEnforceRange<int16_t>(JSC::ExecState& state, JSC::JSValue value) |
221 | { |
222 | return toSmallerInt<int16_t, IntegerConversionConfiguration::EnforceRange>(state, value); |
223 | } |
224 | |
225 | template<> uint16_t convertToIntegerEnforceRange<uint16_t>(JSC::ExecState& state, JSC::JSValue value) |
226 | { |
227 | return toSmallerUInt<uint16_t, IntegerConversionConfiguration::EnforceRange>(state, value); |
228 | } |
229 | |
230 | template<> int16_t convertToIntegerClamp<int16_t>(JSC::ExecState& state, JSC::JSValue value) |
231 | { |
232 | return toSmallerInt<int16_t, IntegerConversionConfiguration::Clamp>(state, value); |
233 | } |
234 | |
235 | template<> uint16_t convertToIntegerClamp<uint16_t>(JSC::ExecState& state, JSC::JSValue value) |
236 | { |
237 | return toSmallerUInt<uint16_t, IntegerConversionConfiguration::Clamp>(state, value); |
238 | } |
239 | |
240 | template<> int16_t convertToInteger<int16_t>(JSC::ExecState& state, JSC::JSValue value) |
241 | { |
242 | return toSmallerInt<int16_t, IntegerConversionConfiguration::Normal>(state, value); |
243 | } |
244 | |
245 | template<> uint16_t convertToInteger<uint16_t>(JSC::ExecState& state, JSC::JSValue value) |
246 | { |
247 | return toSmallerUInt<uint16_t, IntegerConversionConfiguration::Normal>(state, value); |
248 | } |
249 | |
250 | template<> 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 | |
263 | template<> 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 | |
276 | template<> 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 | |
285 | template<> 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 | |
294 | template<> int32_t convertToInteger<int32_t>(JSC::ExecState& state, JSC::JSValue value) |
295 | { |
296 | return value.toInt32(&state); |
297 | } |
298 | |
299 | template<> uint32_t convertToInteger<uint32_t>(JSC::ExecState& state, JSC::JSValue value) |
300 | { |
301 | return value.toUInt32(&state); |
302 | } |
303 | |
304 | template<> 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 | |
317 | template<> 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 | |
330 | template<> 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 | |
339 | template<> 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 | |
348 | template<> 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 | |
361 | template<> 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 | |