Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /**
8 : * Conversions from jsval to primitive values
9 : */
10 :
11 : #ifndef mozilla_dom_PrimitiveConversions_h
12 : #define mozilla_dom_PrimitiveConversions_h
13 :
14 : #include <limits>
15 : #include <math.h>
16 : #include <stdint.h>
17 :
18 : #include "jsapi.h"
19 : #include "js/Conversions.h"
20 : #include "mozilla/Assertions.h"
21 : #include "mozilla/ErrorResult.h"
22 : #include "mozilla/FloatingPoint.h"
23 :
24 : namespace mozilla {
25 : namespace dom {
26 :
27 : template<typename T>
28 : struct TypeName {
29 : };
30 :
31 : template<>
32 : struct TypeName<int8_t> {
33 : static const char* value() {
34 : return "byte";
35 : }
36 : };
37 : template<>
38 : struct TypeName<uint8_t> {
39 0 : static const char* value() {
40 0 : return "octet";
41 : }
42 : };
43 : template<>
44 : struct TypeName<int16_t> {
45 : static const char* value() {
46 : return "short";
47 : }
48 : };
49 : template<>
50 : struct TypeName<uint16_t> {
51 0 : static const char* value() {
52 0 : return "unsigned short";
53 : }
54 : };
55 : template<>
56 : struct TypeName<int32_t> {
57 : static const char* value() {
58 : return "long";
59 : }
60 : };
61 : template<>
62 : struct TypeName<uint32_t> {
63 0 : static const char* value() {
64 0 : return "unsigned long";
65 : }
66 : };
67 : template<>
68 : struct TypeName<int64_t> {
69 : static const char* value() {
70 : return "long long";
71 : }
72 : };
73 : template<>
74 : struct TypeName<uint64_t> {
75 0 : static const char* value() {
76 0 : return "unsigned long long";
77 : }
78 : };
79 :
80 :
81 : enum ConversionBehavior {
82 : eDefault,
83 : eEnforceRange,
84 : eClamp
85 : };
86 :
87 : template<typename T, ConversionBehavior B>
88 : struct PrimitiveConversionTraits {
89 : };
90 :
91 : template<typename T>
92 : struct DisallowedConversion {
93 : typedef int jstype;
94 : typedef int intermediateType;
95 :
96 : private:
97 : static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v,
98 : jstype* retval) {
99 : MOZ_CRASH("This should never be instantiated!");
100 : }
101 : };
102 :
103 : struct PrimitiveConversionTraits_smallInt {
104 : // The output of JS::ToInt32 is determined as follows:
105 : // 1) The value is converted to a double
106 : // 2) Anything that's not a finite double returns 0
107 : // 3) The double is rounded towards zero to the nearest integer
108 : // 4) The resulting integer is reduced mod 2^32. The output of this
109 : // operation is an integer in the range [0, 2^32).
110 : // 5) If the resulting number is >= 2^31, 2^32 is subtracted from it.
111 : //
112 : // The result of all this is a number in the range [-2^31, 2^31)
113 : //
114 : // WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types
115 : // are defined in the same way, except that step 4 uses reduction mod
116 : // 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5
117 : // is only done for the signed types.
118 : //
119 : // C/C++ define integer conversion semantics to unsigned types as taking
120 : // your input integer mod (1 + largest value representable in the
121 : // unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32,
122 : // converting to the unsigned int of the relevant width will correctly
123 : // perform step 4; in particular, the 2^32 possibly subtracted in step 5
124 : // will become 0.
125 : //
126 : // Once we have step 4 done, we're just going to assume 2s-complement
127 : // representation and cast directly to the type we really want.
128 : //
129 : // So we can cast directly for all unsigned types and for int32_t; for
130 : // the smaller-width signed types we need to cast through the
131 : // corresponding unsigned type.
132 : typedef int32_t jstype;
133 : typedef int32_t intermediateType;
134 243 : static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v,
135 : jstype* retval) {
136 243 : return JS::ToInt32(cx, v, retval);
137 : }
138 : };
139 : template<>
140 : struct PrimitiveConversionTraits<int8_t, eDefault> : PrimitiveConversionTraits_smallInt {
141 : typedef uint8_t intermediateType;
142 : };
143 : template<>
144 : struct PrimitiveConversionTraits<uint8_t, eDefault> : PrimitiveConversionTraits_smallInt {
145 : };
146 : template<>
147 : struct PrimitiveConversionTraits<int16_t, eDefault> : PrimitiveConversionTraits_smallInt {
148 : typedef uint16_t intermediateType;
149 : };
150 : template<>
151 : struct PrimitiveConversionTraits<uint16_t, eDefault> : PrimitiveConversionTraits_smallInt {
152 : };
153 : template<>
154 : struct PrimitiveConversionTraits<int32_t, eDefault> : PrimitiveConversionTraits_smallInt {
155 : };
156 : template<>
157 : struct PrimitiveConversionTraits<uint32_t, eDefault> : PrimitiveConversionTraits_smallInt {
158 : };
159 :
160 : template<>
161 : struct PrimitiveConversionTraits<int64_t, eDefault> {
162 : typedef int64_t jstype;
163 : typedef int64_t intermediateType;
164 0 : static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v,
165 : jstype* retval) {
166 0 : return JS::ToInt64(cx, v, retval);
167 : }
168 : };
169 :
170 : template<>
171 : struct PrimitiveConversionTraits<uint64_t, eDefault> {
172 : typedef uint64_t jstype;
173 : typedef uint64_t intermediateType;
174 0 : static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v,
175 : jstype* retval) {
176 0 : return JS::ToUint64(cx, v, retval);
177 : }
178 : };
179 :
180 : template<typename T>
181 : struct PrimitiveConversionTraits_Limits {
182 0 : static inline T min() {
183 0 : return std::numeric_limits<T>::min();
184 : }
185 0 : static inline T max() {
186 0 : return std::numeric_limits<T>::max();
187 : }
188 : };
189 :
190 : template<>
191 : struct PrimitiveConversionTraits_Limits<int64_t> {
192 0 : static inline int64_t min() {
193 0 : return -(1LL << 53) + 1;
194 : }
195 0 : static inline int64_t max() {
196 0 : return (1LL << 53) - 1;
197 : }
198 : };
199 :
200 : template<>
201 : struct PrimitiveConversionTraits_Limits<uint64_t> {
202 0 : static inline uint64_t min() {
203 0 : return 0;
204 : }
205 0 : static inline uint64_t max() {
206 0 : return (1LL << 53) - 1;
207 : }
208 : };
209 :
210 : template<typename T, bool (*Enforce)(JSContext* cx, const double& d, T* retval)>
211 : struct PrimitiveConversionTraits_ToCheckedIntHelper {
212 : typedef T jstype;
213 : typedef T intermediateType;
214 :
215 0 : static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v,
216 : jstype* retval) {
217 : double intermediate;
218 0 : if (!JS::ToNumber(cx, v, &intermediate)) {
219 0 : return false;
220 : }
221 :
222 0 : return Enforce(cx, intermediate, retval);
223 : }
224 : };
225 :
226 : template<typename T>
227 : inline bool
228 0 : PrimitiveConversionTraits_EnforceRange(JSContext* cx, const double& d, T* retval)
229 : {
230 : static_assert(std::numeric_limits<T>::is_integer,
231 : "This can only be applied to integers!");
232 :
233 0 : if (!mozilla::IsFinite(d)) {
234 0 : return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_NON_FINITE, TypeName<T>::value());
235 : }
236 :
237 0 : bool neg = (d < 0);
238 0 : double rounded = floor(neg ? -d : d);
239 0 : rounded = neg ? -rounded : rounded;
240 0 : if (rounded < PrimitiveConversionTraits_Limits<T>::min() ||
241 0 : rounded > PrimitiveConversionTraits_Limits<T>::max()) {
242 0 : return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_OUT_OF_RANGE, TypeName<T>::value());
243 : }
244 :
245 0 : *retval = static_cast<T>(rounded);
246 0 : return true;
247 : }
248 :
249 : template<typename T>
250 : struct PrimitiveConversionTraits<T, eEnforceRange> :
251 : public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_EnforceRange<T> > {
252 : };
253 :
254 : template<typename T>
255 : inline bool
256 0 : PrimitiveConversionTraits_Clamp(JSContext* cx, const double& d, T* retval)
257 : {
258 : static_assert(std::numeric_limits<T>::is_integer,
259 : "This can only be applied to integers!");
260 :
261 0 : if (mozilla::IsNaN(d)) {
262 0 : *retval = 0;
263 0 : return true;
264 : }
265 0 : if (d >= PrimitiveConversionTraits_Limits<T>::max()) {
266 0 : *retval = PrimitiveConversionTraits_Limits<T>::max();
267 0 : return true;
268 : }
269 0 : if (d <= PrimitiveConversionTraits_Limits<T>::min()) {
270 0 : *retval = PrimitiveConversionTraits_Limits<T>::min();
271 0 : return true;
272 : }
273 :
274 0 : MOZ_ASSERT(mozilla::IsFinite(d));
275 :
276 : // Banker's rounding (round ties towards even).
277 : // We move away from 0 by 0.5f and then truncate. That gets us the right
278 : // answer for any starting value except plus or minus N.5. With a starting
279 : // value of that form, we now have plus or minus N+1. If N is odd, this is
280 : // the correct result. If N is even, plus or minus N is the correct result.
281 0 : double toTruncate = (d < 0) ? d - 0.5 : d + 0.5;
282 :
283 0 : T truncated = static_cast<T>(toTruncate);
284 :
285 0 : if (truncated == toTruncate) {
286 : /*
287 : * It was a tie (since moving away from 0 by 0.5 gave us the exact integer
288 : * we want). Since we rounded away from 0, we either already have an even
289 : * number or we have an odd number but the number we want is one closer to
290 : * 0. So just unconditionally masking out the ones bit should do the trick
291 : * to get us the value we want.
292 : */
293 0 : truncated &= ~1;
294 : }
295 :
296 0 : *retval = truncated;
297 0 : return true;
298 : }
299 :
300 : template<typename T>
301 : struct PrimitiveConversionTraits<T, eClamp> :
302 : public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_Clamp<T> > {
303 : };
304 :
305 :
306 : template<ConversionBehavior B>
307 : struct PrimitiveConversionTraits<bool, B> : public DisallowedConversion<bool> {};
308 :
309 : template<>
310 : struct PrimitiveConversionTraits<bool, eDefault> {
311 : typedef bool jstype;
312 : typedef bool intermediateType;
313 1652 : static inline bool converter(JSContext* /* unused */, JS::Handle<JS::Value> v,
314 : jstype* retval) {
315 1652 : *retval = JS::ToBoolean(v);
316 1652 : return true;
317 : }
318 : };
319 :
320 :
321 : template<ConversionBehavior B>
322 : struct PrimitiveConversionTraits<float, B> : public DisallowedConversion<float> {};
323 :
324 : template<ConversionBehavior B>
325 : struct PrimitiveConversionTraits<double, B> : public DisallowedConversion<double> {};
326 :
327 : struct PrimitiveConversionTraits_float {
328 : typedef double jstype;
329 : typedef double intermediateType;
330 4 : static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v,
331 : jstype* retval) {
332 4 : return JS::ToNumber(cx, v, retval);
333 : }
334 : };
335 :
336 : template<>
337 : struct PrimitiveConversionTraits<float, eDefault> : PrimitiveConversionTraits_float {
338 : };
339 : template<>
340 : struct PrimitiveConversionTraits<double, eDefault> : PrimitiveConversionTraits_float {
341 : };
342 :
343 :
344 : template<typename T, ConversionBehavior B>
345 1899 : bool ValueToPrimitive(JSContext* cx, JS::Handle<JS::Value> v, T* retval)
346 : {
347 : typename PrimitiveConversionTraits<T, B>::jstype t;
348 1899 : if (!PrimitiveConversionTraits<T, B>::converter(cx, v, &t))
349 0 : return false;
350 :
351 1899 : *retval = static_cast<T>(
352 : static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>(t));
353 1899 : return true;
354 : }
355 :
356 : } // namespace dom
357 : } // namespace mozilla
358 :
359 : #endif /* mozilla_dom_PrimitiveConversions_h */
|