PrimitiveConversions.h 10.0 KB

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