1 /*
2  *  Self tests to ensure execution environment is sane.  Intended to catch
3  *  compiler/platform problems which cannot be detected at compile time.
4  */
5 
6 #include "duk_internal.h"
7 
8 #if defined(DUK_USE_SELF_TESTS)
9 
10 /*
11  *  Unions and structs for self tests
12  */
13 
14 typedef union {
15 	double d;
16 	duk_uint8_t x[8];
17 } duk__test_double_union;
18 
19 /* Self test failed.  Expects a local variable 'error_count' to exist. */
20 #define DUK__FAILED(msg)  do { \
21 		DUK_D(DUK_DPRINT("self test failed: " #msg " at " DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO))); \
22 		error_count++; \
23 	} while (0)
24 
25 #define DUK__DBLUNION_CMP_TRUE(a,b)  do { \
26 		if (duk_memcmp((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) != 0) { \
27 			DUK__FAILED("double union compares false (expected true)"); \
28 		} \
29 	} while (0)
30 
31 #define DUK__DBLUNION_CMP_FALSE(a,b)  do { \
32 		if (duk_memcmp((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) == 0) { \
33 			DUK__FAILED("double union compares true (expected false)"); \
34 		} \
35 	} while (0)
36 
37 typedef union {
38 	duk_uint32_t i;
39 	duk_uint8_t x[8];
40 } duk__test_u32_union;
41 
42 #if defined(DUK_USE_INTEGER_LE)
43 #define DUK__U32_INIT(u, a, b, c, d) do { \
44 		(u)->x[0] = (d); (u)->x[1] = (c); (u)->x[2] = (b); (u)->x[3] = (a); \
45 	} while (0)
46 #elif defined(DUK_USE_INTEGER_ME)
47 #error integer mixed endian not supported now
48 #elif defined(DUK_USE_INTEGER_BE)
49 #define DUK__U32_INIT(u, a, b, c, d) do { \
50 		(u)->x[0] = (a); (u)->x[1] = (b); (u)->x[2] = (c); (u)->x[3] = (d); \
51 	} while (0)
52 #else
53 #error unknown integer endianness
54 #endif
55 
56 #if defined(DUK_USE_DOUBLE_LE)
57 #define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) do { \
58 		(u)->x[0] = (h); (u)->x[1] = (g); (u)->x[2] = (f); (u)->x[3] = (e); \
59 		(u)->x[4] = (d); (u)->x[5] = (c); (u)->x[6] = (b); (u)->x[7] = (a); \
60 	} while (0)
61 #define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \
62 	((u)->x[0] == (h) && (u)->x[1] == (g) && (u)->x[2] == (f) && (u)->x[3] == (e) && \
63 	 (u)->x[4] == (d) && (u)->x[5] == (c) && (u)->x[6] == (b) && (u)->x[7] == (a))
64 #elif defined(DUK_USE_DOUBLE_ME)
65 #define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) do { \
66 		(u)->x[0] = (d); (u)->x[1] = (c); (u)->x[2] = (b); (u)->x[3] = (a); \
67 		(u)->x[4] = (h); (u)->x[5] = (g); (u)->x[6] = (f); (u)->x[7] = (e); \
68 	} while (0)
69 #define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \
70 	((u)->x[0] == (d) && (u)->x[1] == (c) && (u)->x[2] == (b) && (u)->x[3] == (a) && \
71 	 (u)->x[4] == (h) && (u)->x[5] == (g) && (u)->x[6] == (f) && (u)->x[7] == (e))
72 #elif defined(DUK_USE_DOUBLE_BE)
73 #define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) do { \
74 		(u)->x[0] = (a); (u)->x[1] = (b); (u)->x[2] = (c); (u)->x[3] = (d); \
75 		(u)->x[4] = (e); (u)->x[5] = (f); (u)->x[6] = (g); (u)->x[7] = (h); \
76 	} while (0)
77 #define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \
78 	((u)->x[0] == (a) && (u)->x[1] == (b) && (u)->x[2] == (c) && (u)->x[3] == (d) && \
79 	 (u)->x[4] == (e) && (u)->x[5] == (f) && (u)->x[6] == (g) && (u)->x[7] == (h))
80 #else
81 #error unknown double endianness
82 #endif
83 
84 /*
85  *  Various sanity checks for typing
86  */
87 
duk__selftest_types(void)88 DUK_LOCAL duk_uint_t duk__selftest_types(void) {
89 	duk_uint_t error_count = 0;
90 
91 	if (!(sizeof(duk_int8_t) == 1 &&
92 	      sizeof(duk_uint8_t) == 1 &&
93 	      sizeof(duk_int16_t) == 2 &&
94 	      sizeof(duk_uint16_t) == 2 &&
95 	      sizeof(duk_int32_t) == 4 &&
96 	      sizeof(duk_uint32_t) == 4)) {
97 		DUK__FAILED("duk_(u)int{8,16,32}_t size");
98 	}
99 #if defined(DUK_USE_64BIT_OPS)
100 	if (!(sizeof(duk_int64_t) == 8 &&
101 	      sizeof(duk_uint64_t) == 8)) {
102 		DUK__FAILED("duk_(u)int64_t size");
103 	}
104 #endif
105 
106 	if (!(sizeof(duk_size_t) >= sizeof(duk_uint_t))) {
107 		/* Some internal code now assumes that all duk_uint_t values
108 		 * can be expressed with a duk_size_t.
109 		 */
110 		DUK__FAILED("duk_size_t is smaller than duk_uint_t");
111 	}
112 	if (!(sizeof(duk_int_t) >= 4)) {
113 		DUK__FAILED("duk_int_t is not 32 bits");
114 	}
115 
116 	return error_count;
117 }
118 
119 /*
120  *  Packed tval sanity
121  */
122 
duk__selftest_packed_tval(void)123 DUK_LOCAL duk_uint_t duk__selftest_packed_tval(void) {
124 	duk_uint_t error_count = 0;
125 
126 #if defined(DUK_USE_PACKED_TVAL)
127 	if (sizeof(void *) > 4) {
128 		DUK__FAILED("packed duk_tval in use but sizeof(void *) > 4");
129 	}
130 #endif
131 
132 	return error_count;
133 }
134 
135 /*
136  *  Two's complement arithmetic.
137  */
138 
duk__selftest_twos_complement(void)139 DUK_LOCAL duk_uint_t duk__selftest_twos_complement(void) {
140 	duk_uint_t error_count = 0;
141 	volatile int test;
142 	test = -1;
143 
144 	/* Note that byte order doesn't affect this test: all bytes in
145 	 * 'test' will be 0xFF for two's complement.
146 	 */
147 	if (((volatile duk_uint8_t *) &test)[0] != (duk_uint8_t) 0xff) {
148 		DUK__FAILED("two's complement arithmetic");
149 	}
150 
151 	return error_count;
152 }
153 
154 /*
155  *  Byte order.  Important to self check, because on some exotic platforms
156  *  there is no actual detection but rather assumption based on platform
157  *  defines.
158  */
159 
duk__selftest_byte_order(void)160 DUK_LOCAL duk_uint_t duk__selftest_byte_order(void) {
161 	duk_uint_t error_count = 0;
162 	duk__test_u32_union u1;
163 	duk__test_double_union u2;
164 
165 	/*
166 	 *  >>> struct.pack('>d', 102030405060).encode('hex')
167 	 *  '4237c17c6dc40000'
168 	 */
169 
170 	DUK__U32_INIT(&u1, 0xde, 0xad, 0xbe, 0xef);
171 	DUK__DOUBLE_INIT(&u2, 0x42, 0x37, 0xc1, 0x7c, 0x6d, 0xc4, 0x00, 0x00);
172 
173 	if (u1.i != (duk_uint32_t) 0xdeadbeefUL) {
174 		DUK__FAILED("duk_uint32_t byte order");
175 	}
176 
177 	if (!duk_double_equals(u2.d, 102030405060.0)) {
178 		DUK__FAILED("double byte order");
179 	}
180 
181 	return error_count;
182 }
183 
184 /*
185  *  DUK_BSWAP macros
186  */
187 
duk__selftest_bswap_macros(void)188 DUK_LOCAL duk_uint_t duk__selftest_bswap_macros(void) {
189 	duk_uint_t error_count = 0;
190 	volatile duk_uint32_t x32_input, x32_output;
191 	duk_uint32_t x32;
192 	volatile duk_uint16_t x16_input, x16_output;
193 	duk_uint16_t x16;
194 	duk_double_union du;
195 	duk_double_t du_diff;
196 #if defined(DUK_BSWAP64)
197 	volatile duk_uint64_t x64_input, x64_output;
198 	duk_uint64_t x64;
199 #endif
200 
201 	/* Cover both compile time and runtime bswap operations, as these
202 	 * may have different bugs.
203 	 */
204 
205 	x16_input = 0xbeefUL;
206 	x16 = x16_input;
207 	x16 = DUK_BSWAP16(x16);
208 	x16_output = x16;
209 	if (x16_output != (duk_uint16_t) 0xefbeUL) {
210 		DUK__FAILED("DUK_BSWAP16");
211 	}
212 
213 	x16 = 0xbeefUL;
214 	x16 = DUK_BSWAP16(x16);
215 	if (x16 != (duk_uint16_t) 0xefbeUL) {
216 		DUK__FAILED("DUK_BSWAP16");
217 	}
218 
219 	x32_input = 0xdeadbeefUL;
220 	x32 = x32_input;
221 	x32 = DUK_BSWAP32(x32);
222 	x32_output = x32;
223 	if (x32_output != (duk_uint32_t) 0xefbeaddeUL) {
224 		DUK__FAILED("DUK_BSWAP32");
225 	}
226 
227 	x32 = 0xdeadbeefUL;
228 	x32 = DUK_BSWAP32(x32);
229 	if (x32 != (duk_uint32_t) 0xefbeaddeUL) {
230 		DUK__FAILED("DUK_BSWAP32");
231 	}
232 
233 #if defined(DUK_BSWAP64)
234 	x64_input = DUK_U64_CONSTANT(0x8899aabbccddeeff);
235 	x64 = x64_input;
236 	x64 = DUK_BSWAP64(x64);
237 	x64_output = x64;
238 	if (x64_output != (duk_uint64_t) DUK_U64_CONSTANT(0xffeeddccbbaa9988)) {
239 		DUK__FAILED("DUK_BSWAP64");
240 	}
241 
242 	x64 = DUK_U64_CONSTANT(0x8899aabbccddeeff);
243 	x64 = DUK_BSWAP64(x64);
244 	if (x64 != (duk_uint64_t) DUK_U64_CONSTANT(0xffeeddccbbaa9988)) {
245 		DUK__FAILED("DUK_BSWAP64");
246 	}
247 #endif
248 
249 	/* >>> struct.unpack('>d', '4000112233445566'.decode('hex'))
250 	 * (2.008366013071895,)
251 	 */
252 
253 	du.uc[0] = 0x40; du.uc[1] = 0x00; du.uc[2] = 0x11; du.uc[3] = 0x22;
254 	du.uc[4] = 0x33; du.uc[5] = 0x44; du.uc[6] = 0x55; du.uc[7] = 0x66;
255 	DUK_DBLUNION_DOUBLE_NTOH(&du);
256 	du_diff = du.d - 2.008366013071895;
257 #if 0
258 	DUK_D(DUK_DPRINT("du_diff: %lg\n", (double) du_diff));
259 #endif
260 	if (du_diff > 1e-15) {
261 		/* Allow very small lenience because some compilers won't parse
262 		 * exact IEEE double constants (happened in matrix testing with
263 		 * Linux gcc-4.8 -m32 at least).
264 		 */
265 #if 0
266 		DUK_D(DUK_DPRINT("Result of DUK_DBLUNION_DOUBLE_NTOH: %02x %02x %02x %02x %02x %02x %02x %02x\n",
267 		            (unsigned int) du.uc[0], (unsigned int) du.uc[1],
268 		            (unsigned int) du.uc[2], (unsigned int) du.uc[3],
269 		            (unsigned int) du.uc[4], (unsigned int) du.uc[5],
270 		            (unsigned int) du.uc[6], (unsigned int) du.uc[7]));
271 #endif
272 		DUK__FAILED("DUK_DBLUNION_DOUBLE_NTOH");
273 	}
274 
275 	return error_count;
276 }
277 
278 /*
279  *  Basic double / byte union memory layout.
280  */
281 
duk__selftest_double_union_size(void)282 DUK_LOCAL duk_uint_t duk__selftest_double_union_size(void) {
283 	duk_uint_t error_count = 0;
284 
285 	if (sizeof(duk__test_double_union) != 8) {
286 		DUK__FAILED("invalid union size");
287 	}
288 
289 	return error_count;
290 }
291 
292 /*
293  *  Union aliasing, see misc/clang_aliasing.c.
294  */
295 
duk__selftest_double_aliasing(void)296 DUK_LOCAL duk_uint_t duk__selftest_double_aliasing(void) {
297 	/* This testcase fails when Emscripten-generated code runs on Firefox.
298 	 * It's not an issue because the failure should only affect packed
299 	 * duk_tval representation, which is not used with Emscripten.
300 	 */
301 #if defined(DUK_USE_PACKED_TVAL)
302 	duk_uint_t error_count = 0;
303 	duk__test_double_union a, b;
304 
305 	/* Test signaling NaN and alias assignment in all endianness combinations.
306 	 */
307 
308 	/* little endian */
309 	a.x[0] = 0x11; a.x[1] = 0x22; a.x[2] = 0x33; a.x[3] = 0x44;
310 	a.x[4] = 0x00; a.x[5] = 0x00; a.x[6] = 0xf1; a.x[7] = 0xff;
311 	b = a;
312 	DUK__DBLUNION_CMP_TRUE(&a, &b);
313 
314 	/* big endian */
315 	a.x[0] = 0xff; a.x[1] = 0xf1; a.x[2] = 0x00; a.x[3] = 0x00;
316 	a.x[4] = 0x44; a.x[5] = 0x33; a.x[6] = 0x22; a.x[7] = 0x11;
317 	b = a;
318 	DUK__DBLUNION_CMP_TRUE(&a, &b);
319 
320 	/* mixed endian */
321 	a.x[0] = 0x00; a.x[1] = 0x00; a.x[2] = 0xf1; a.x[3] = 0xff;
322 	a.x[4] = 0x11; a.x[5] = 0x22; a.x[6] = 0x33; a.x[7] = 0x44;
323 	b = a;
324 	DUK__DBLUNION_CMP_TRUE(&a, &b);
325 
326 	return error_count;
327 #else
328 	DUK_D(DUK_DPRINT("skip double aliasing self test when duk_tval is not packed"));
329 	return 0;
330 #endif
331 }
332 
333 /*
334  *  Zero sign, see misc/tcc_zerosign2.c.
335  */
336 
duk__selftest_double_zero_sign(void)337 DUK_LOCAL duk_uint_t duk__selftest_double_zero_sign(void) {
338 	duk_uint_t error_count = 0;
339 	duk__test_double_union a, b;
340 
341 	a.d = 0.0;
342 	b.d = -a.d;
343 	DUK__DBLUNION_CMP_FALSE(&a, &b);
344 
345 	return error_count;
346 }
347 
348 /*
349  *  Rounding mode: Duktape assumes round-to-nearest, check that this is true.
350  *  If we had C99 fenv.h we could check that fegetround() == FE_TONEAREST,
351  *  but we don't want to rely on that header; and even if we did, it's good
352  *  to ensure the rounding actually works.
353  */
354 
duk__selftest_double_rounding(void)355 DUK_LOCAL duk_uint_t duk__selftest_double_rounding(void) {
356 	duk_uint_t error_count = 0;
357 	duk__test_double_union a, b, c;
358 
359 #if 0
360 	/* Include <fenv.h> and test manually; these trigger failures: */
361 	fesetround(FE_UPWARD);
362 	fesetround(FE_DOWNWARD);
363 	fesetround(FE_TOWARDZERO);
364 
365 	/* This is the default and passes. */
366 	fesetround(FE_TONEAREST);
367 #endif
368 
369 	/* Rounding tests check that none of the other modes (round to
370 	 * +Inf, round to -Inf, round to zero) can be active:
371 	 * http://www.gnu.org/software/libc/manual/html_node/Rounding.html
372 	 */
373 
374 	/* 1.0 + 2^(-53): result is midway between 1.0 and 1.0 + ulp.
375 	 * Round to nearest: 1.0
376 	 * Round to +Inf:    1.0 + ulp
377 	 * Round to -Inf:    1.0
378 	 * Round to zero:    1.0
379 	 * => Correct result eliminates round to +Inf.
380 	 */
381 	DUK__DOUBLE_INIT(&a, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
382 	DUK__DOUBLE_INIT(&b, 0x3c, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
383 	duk_memset((void *) &c, 0, sizeof(c));
384 	c.d = a.d + b.d;
385 	if (!DUK__DOUBLE_COMPARE(&c, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)) {
386 		DUK_D(DUK_DPRINT("broken result (native endiannesss): %02x %02x %02x %02x %02x %02x %02x %02x",
387 		                 (unsigned int) c.x[0], (unsigned int) c.x[1],
388 		                 (unsigned int) c.x[2], (unsigned int) c.x[3],
389 		                 (unsigned int) c.x[4], (unsigned int) c.x[5],
390 		                 (unsigned int) c.x[6], (unsigned int) c.x[7]));
391 		DUK__FAILED("invalid result from 1.0 + 0.5ulp");
392 	}
393 
394 	/* (1.0 + ulp) + 2^(-53): result is midway between 1.0 + ulp and 1.0 + 2*ulp.
395 	 * Round to nearest: 1.0 + 2*ulp (round to even mantissa)
396 	 * Round to +Inf:    1.0 + 2*ulp
397 	 * Round to -Inf:    1.0 + ulp
398 	 * Round to zero:    1.0 + ulp
399 	 * => Correct result eliminates round to -Inf and round to zero.
400 	 */
401 	DUK__DOUBLE_INIT(&a, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01);
402 	DUK__DOUBLE_INIT(&b, 0x3c, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
403 	duk_memset((void *) &c, 0, sizeof(c));
404 	c.d = a.d + b.d;
405 	if (!DUK__DOUBLE_COMPARE(&c, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02)) {
406 		DUK_D(DUK_DPRINT("broken result (native endiannesss): %02x %02x %02x %02x %02x %02x %02x %02x",
407 		                 (unsigned int) c.x[0], (unsigned int) c.x[1],
408 		                 (unsigned int) c.x[2], (unsigned int) c.x[3],
409 		                 (unsigned int) c.x[4], (unsigned int) c.x[5],
410 		                 (unsigned int) c.x[6], (unsigned int) c.x[7]));
411 		DUK__FAILED("invalid result from (1.0 + ulp) + 0.5ulp");
412 	}
413 
414 	/* Could do negative number testing too, but the tests above should
415 	 * differentiate between IEEE 754 rounding modes.
416 	 */
417 	return error_count;
418 }
419 
420 /*
421  *  fmod(): often a portability issue in embedded or bare platform targets.
422  *  Check for at least minimally correct behavior.  Unlike some other math
423  *  functions (like cos()) Duktape relies on fmod() internally too.
424  */
425 
duk__selftest_fmod(void)426 DUK_LOCAL duk_uint_t duk__selftest_fmod(void) {
427 	duk_uint_t error_count = 0;
428 	duk__test_double_union u1, u2;
429 	volatile duk_double_t t1, t2, t3;
430 
431 	/* fmod() with integer argument and exponent 2^32 is used by e.g.
432 	 * ToUint32() and some Duktape internals.
433 	 */
434 	u1.d = DUK_FMOD(10.0, 4294967296.0);
435 	u2.d = 10.0;
436 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
437 
438 	u1.d = DUK_FMOD(4294967306.0, 4294967296.0);
439 	u2.d = 10.0;
440 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
441 
442 	u1.d = DUK_FMOD(73014444042.0, 4294967296.0);
443 	u2.d = 10.0;
444 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
445 
446 	/* 52-bit integer split into two parts:
447 	 * >>> 0x1fedcba9876543
448 	 * 8987183256397123
449 	 * >>> float(0x1fedcba9876543) / float(2**53)
450 	 * 0.9977777777777778
451 	 */
452 	u1.d = DUK_FMOD(8987183256397123.0, 4294967296.0);
453 	u2.d = (duk_double_t) 0xa9876543UL;
454 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
455 	t1 = 8987183256397123.0;
456 	t2 = 4294967296.0;
457 	t3 = t1 / t2;
458 	u1.d = DUK_FLOOR(t3);
459 	u2.d = (duk_double_t) 0x1fedcbUL;
460 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
461 
462 	/* C99 behavior is for fmod() result sign to mathc argument sign. */
463 	u1.d = DUK_FMOD(-10.0, 4294967296.0);
464 	u2.d = -10.0;
465 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
466 
467 	u1.d = DUK_FMOD(-4294967306.0, 4294967296.0);
468 	u2.d = -10.0;
469 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
470 
471 	u1.d = DUK_FMOD(-73014444042.0, 4294967296.0);
472 	u2.d = -10.0;
473 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
474 
475 	return error_count;
476 }
477 
478 /*
479  *  Struct size/alignment if platform requires it
480  *
481  *  There are some compiler specific struct padding pragmas etc in use, this
482  *  selftest ensures they're correctly detected and used.
483  */
484 
duk__selftest_struct_align(void)485 DUK_LOCAL duk_uint_t duk__selftest_struct_align(void) {
486 	duk_uint_t error_count = 0;
487 
488 #if (DUK_USE_ALIGN_BY == 4)
489 	if ((sizeof(duk_hbuffer_fixed) % 4) != 0) {
490 		DUK__FAILED("sizeof(duk_hbuffer_fixed) not aligned to 4");
491 	}
492 #elif (DUK_USE_ALIGN_BY == 8)
493 	if ((sizeof(duk_hbuffer_fixed) % 8) != 0) {
494 		DUK__FAILED("sizeof(duk_hbuffer_fixed) not aligned to 8");
495 	}
496 #elif (DUK_USE_ALIGN_BY == 1)
497 	/* no check */
498 #else
499 #error invalid DUK_USE_ALIGN_BY
500 #endif
501 	return error_count;
502 }
503 
504 /*
505  *  64-bit arithmetic
506  *
507  *  There are some platforms/compilers where 64-bit types are available
508  *  but don't work correctly.  Test for known cases.
509  */
510 
duk__selftest_64bit_arithmetic(void)511 DUK_LOCAL duk_uint_t duk__selftest_64bit_arithmetic(void) {
512 	duk_uint_t error_count = 0;
513 #if defined(DUK_USE_64BIT_OPS)
514 	volatile duk_int64_t i;
515 	volatile duk_double_t d;
516 
517 	/* Catch a double-to-int64 cast issue encountered in practice. */
518 	d = 2147483648.0;
519 	i = (duk_int64_t) d;
520 	if (i != DUK_I64_CONSTANT(0x80000000)) {
521 		DUK__FAILED("casting 2147483648.0 to duk_int64_t failed");
522 	}
523 #else
524 	/* nop */
525 #endif
526 	return error_count;
527 }
528 
529 /*
530  *  Casting
531  */
532 
duk__selftest_cast_double_to_small_uint(void)533 DUK_LOCAL duk_uint_t duk__selftest_cast_double_to_small_uint(void) {
534 	/*
535 	 *  https://github.com/svaarala/duktape/issues/127#issuecomment-77863473
536 	 */
537 
538 	duk_uint_t error_count = 0;
539 
540 	duk_double_t d1, d2;
541 	duk_small_uint_t u;
542 
543 	duk_double_t d1v, d2v;
544 	duk_small_uint_t uv;
545 
546 	/* Test without volatiles */
547 
548 	d1 = 1.0;
549 	u = (duk_small_uint_t) d1;
550 	d2 = (duk_double_t) u;
551 
552 	if (!(duk_double_equals(d1, 1.0) && u == 1 && duk_double_equals(d2, 1.0) && duk_double_equals(d1, d2))) {
553 		DUK__FAILED("double to duk_small_uint_t cast failed");
554 	}
555 
556 	/* Same test with volatiles */
557 
558 	d1v = 1.0;
559 	uv = (duk_small_uint_t) d1v;
560 	d2v = (duk_double_t) uv;
561 
562 	if (!(duk_double_equals(d1v, 1.0) && uv == 1 && duk_double_equals(d2v, 1.0) && duk_double_equals(d1v, d2v))) {
563 		DUK__FAILED("double to duk_small_uint_t cast failed");
564 	}
565 
566 	return error_count;
567 }
568 
duk__selftest_cast_double_to_uint32(void)569 DUK_LOCAL duk_uint_t duk__selftest_cast_double_to_uint32(void) {
570 	/*
571 	 *  This test fails on an exotic ARM target; double-to-uint
572 	 *  cast is incorrectly clamped to -signed- int highest value.
573 	 *
574 	 *  https://github.com/svaarala/duktape/issues/336
575 	 */
576 
577 	duk_uint_t error_count = 0;
578 	duk_double_t dv;
579 	duk_uint32_t uv;
580 
581 	dv = 3735928559.0;  /* 0xdeadbeef in decimal */
582 	uv = (duk_uint32_t) dv;
583 
584 	if (uv != 0xdeadbeefUL) {
585 		DUK__FAILED("double to duk_uint32_t cast failed");
586 	}
587 
588 	return error_count;
589 }
590 
591 /*
592  *  Minimal test of user supplied allocation functions
593  *
594  *    - Basic alloc + realloc + free cycle
595  *
596  *    - Realloc to significantly larger size to (hopefully) trigger a
597  *      relocation and check that relocation copying works
598  */
599 
duk__selftest_alloc_funcs(duk_alloc_function alloc_func,duk_realloc_function realloc_func,duk_free_function free_func,void * udata)600 DUK_LOCAL duk_uint_t duk__selftest_alloc_funcs(duk_alloc_function alloc_func,
601                                                duk_realloc_function realloc_func,
602                                                duk_free_function free_func,
603                                                void *udata) {
604 	duk_uint_t error_count = 0;
605 	void *ptr;
606 	void *new_ptr;
607 	duk_small_int_t i, j;
608 	unsigned char x;
609 
610 	if (alloc_func == NULL || realloc_func == NULL || free_func == NULL) {
611 		return 0;
612 	}
613 
614 	for (i = 1; i <= 256; i++) {
615 		ptr = alloc_func(udata, (duk_size_t) i);
616 		if (ptr == NULL) {
617 			DUK_D(DUK_DPRINT("alloc failed, ignore"));
618 			continue;  /* alloc failed, ignore */
619 		}
620 		for (j = 0; j < i; j++) {
621 			((unsigned char *) ptr)[j] = (unsigned char) (0x80 + j);
622 		}
623 		new_ptr = realloc_func(udata, ptr, 1024);
624 		if (new_ptr == NULL) {
625 			DUK_D(DUK_DPRINT("realloc failed, ignore"));
626 			free_func(udata, ptr);
627 			continue;  /* realloc failed, ignore */
628 		}
629 		ptr = new_ptr;
630 		for (j = 0; j < i; j++) {
631 			x = ((unsigned char *) ptr)[j];
632 			if (x != (unsigned char) (0x80 + j)) {
633 				DUK_D(DUK_DPRINT("byte at index %ld doesn't match after realloc: %02lx",
634 				                 (long) j, (unsigned long) x));
635 				DUK__FAILED("byte compare after realloc");
636 				break;
637 			}
638 		}
639 		free_func(udata, ptr);
640 	}
641 
642 	return error_count;
643 }
644 
645 /*
646  *  Self test main
647  */
648 
duk_selftest_run_tests(duk_alloc_function alloc_func,duk_realloc_function realloc_func,duk_free_function free_func,void * udata)649 DUK_INTERNAL duk_uint_t duk_selftest_run_tests(duk_alloc_function alloc_func,
650                                                duk_realloc_function realloc_func,
651                                                duk_free_function free_func,
652                                                void *udata) {
653 	duk_uint_t error_count = 0;
654 
655 	DUK_D(DUK_DPRINT("self test starting"));
656 
657 	error_count += duk__selftest_types();
658 	error_count += duk__selftest_packed_tval();
659 	error_count += duk__selftest_twos_complement();
660 	error_count += duk__selftest_byte_order();
661 	error_count += duk__selftest_bswap_macros();
662 	error_count += duk__selftest_double_union_size();
663 	error_count += duk__selftest_double_aliasing();
664 	error_count += duk__selftest_double_zero_sign();
665 	error_count += duk__selftest_double_rounding();
666 	error_count += duk__selftest_fmod();
667 	error_count += duk__selftest_struct_align();
668 	error_count += duk__selftest_64bit_arithmetic();
669 	error_count += duk__selftest_cast_double_to_small_uint();
670 	error_count += duk__selftest_cast_double_to_uint32();
671 	error_count += duk__selftest_alloc_funcs(alloc_func, realloc_func, free_func, udata);
672 
673 	DUK_D(DUK_DPRINT("self test complete, total error count: %ld", (long) error_count));
674 
675 	return error_count;
676 }
677 
678 #endif  /* DUK_USE_SELF_TESTS */
679