2 * Self tests to ensure execution environment is sane. Intended to catch
3 * compiler/platform problems which cannot be detected at compile time.
6 #include "duk_internal.h"
8 #if defined(DUK_USE_SELF_TESTS)
11 * Unions and structs for self tests
17 } duk__test_double_union
;
19 #define DUK__DBLUNION_CMP_TRUE(a,b) do { \
20 if (DUK_MEMCMP((void *) (a), (void *) (b), sizeof(duk__test_double_union)) != 0) { \
21 DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double union compares false (expected true)"); \
25 #define DUK__DBLUNION_CMP_FALSE(a,b) do { \
26 if (DUK_MEMCMP((void *) (a), (void *) (b), sizeof(duk__test_double_union)) == 0) { \
27 DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double union compares true (expected false)"); \
34 } duk__test_u32_union
;
37 * Various sanity checks for typing
40 DUK_LOCAL
void duk__selftest_types(void) {
41 if (!(sizeof(duk_int8_t
) == 1 &&
42 sizeof(duk_uint8_t
) == 1 &&
43 sizeof(duk_int16_t
) == 2 &&
44 sizeof(duk_uint16_t
) == 2 &&
45 sizeof(duk_int32_t
) == 4 &&
46 sizeof(duk_uint32_t
) == 4)) {
47 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: duk_(u)int{8,16,32}_t size");
49 #if defined(DUK_USE_64BIT_OPS)
50 if (!(sizeof(duk_int64_t
) == 8 &&
51 sizeof(duk_uint64_t
) == 8)) {
52 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: duk_(u)int64_t size");
56 if (!(sizeof(duk_size_t
) >= sizeof(duk_uint_t
))) {
57 /* Some internal code now assumes that all duk_uint_t values
58 * can be expressed with a duk_size_t.
60 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: duk_size_t is smaller than duk_uint_t");
62 if (!(sizeof(duk_int_t
) >= 4)) {
63 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: duk_int_t is not 32 bits");
71 DUK_LOCAL
void duk__selftest_packed_tval(void) {
72 #if defined(DUK_USE_PACKED_TVAL)
73 if (sizeof(void *) > 4) {
74 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: packed duk_tval in use but sizeof(void *) > 4");
80 * Two's complement arithmetic.
83 DUK_LOCAL
void duk__selftest_twos_complement(void) {
86 if (((duk_uint8_t
*) &test
)[0] != (duk_uint8_t
) 0xff) {
87 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: two's complement arithmetic");
92 * Byte order. Important to self check, because on some exotic platforms
93 * there is no actual detection but rather assumption based on platform
97 DUK_LOCAL
void duk__selftest_byte_order(void) {
98 duk__test_u32_union u1
;
99 duk__test_double_union u2
;
102 * >>> struct.pack('>d', 102030405060).encode('hex')
105 #if defined(DUK_USE_INTEGER_LE)
106 u1
.c
[0] = 0xef; u1
.c
[1] = 0xbe; u1
.c
[2] = 0xad; u1
.c
[3] = 0xde;
107 #elif defined(DUK_USE_INTEGER_ME)
108 #error integer mixed endian not supported now
109 #elif defined(DUK_USE_INTEGER_BE)
110 u1
.c
[0] = 0xde; u1
.c
[1] = 0xad; u1
.c
[2] = 0xbe; u1
.c
[3] = 0xef;
112 #error unknown integer endianness
115 #if defined(DUK_USE_DOUBLE_LE)
116 u2
.c
[0] = 0x00; u2
.c
[1] = 0x00; u2
.c
[2] = 0xc4; u2
.c
[3] = 0x6d;
117 u2
.c
[4] = 0x7c; u2
.c
[5] = 0xc1; u2
.c
[6] = 0x37; u2
.c
[7] = 0x42;
118 #elif defined(DUK_USE_DOUBLE_ME)
119 u2
.c
[0] = 0x7c; u2
.c
[1] = 0xc1; u2
.c
[2] = 0x37; u2
.c
[3] = 0x42;
120 u2
.c
[4] = 0x00; u2
.c
[5] = 0x00; u2
.c
[6] = 0xc4; u2
.c
[7] = 0x6d;
121 #elif defined(DUK_USE_DOUBLE_BE)
122 u2
.c
[0] = 0x42; u2
.c
[1] = 0x37; u2
.c
[2] = 0xc1; u2
.c
[3] = 0x7c;
123 u2
.c
[4] = 0x6d; u2
.c
[5] = 0xc4; u2
.c
[6] = 0x00; u2
.c
[7] = 0x00;
125 #error unknown double endianness
128 if (u1
.i
!= (duk_uint32_t
) 0xdeadbeefUL
) {
129 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: duk_uint32_t byte order");
132 if (u2
.d
!= (double) 102030405060.0) {
133 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: double byte order");
141 DUK_LOCAL
void duk__selftest_bswap_macros(void) {
145 duk_double_t du_diff
;
148 x16
= DUK_BSWAP16(x16
);
149 if (x16
!= (duk_uint16_t
) 0xefbeUL
) {
150 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: DUK_BSWAP16");
154 x32
= DUK_BSWAP32(x32
);
155 if (x32
!= (duk_uint32_t
) 0xefbeaddeUL
) {
156 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: DUK_BSWAP32");
159 /* >>> struct.unpack('>d', '4000112233445566'.decode('hex'))
160 * (2.008366013071895,)
163 du
.uc
[0] = 0x40; du
.uc
[1] = 0x00; du
.uc
[2] = 0x11; du
.uc
[3] = 0x22;
164 du
.uc
[4] = 0x33; du
.uc
[5] = 0x44; du
.uc
[6] = 0x55; du
.uc
[7] = 0x66;
165 DUK_DBLUNION_DOUBLE_NTOH(&du
);
166 du_diff
= du
.d
- 2.008366013071895;
168 DUK_FPRINTF(DUK_STDERR
, "du_diff: %lg\n", (double) du_diff
);
170 if (du_diff
> 1e-15) {
171 /* Allow very small lenience because some compilers won't parse
172 * exact IEEE double constants (happened in matrix testing with
173 * Linux gcc-4.8 -m32 at least).
176 DUK_FPRINTF(DUK_STDERR
, "Result of DUK_DBLUNION_DOUBLE_NTOH: %02x %02x %02x %02x %02x %02x %02x %02x\n",
177 (unsigned int) du
.uc
[0], (unsigned int) du
.uc
[1],
178 (unsigned int) du
.uc
[2], (unsigned int) du
.uc
[3],
179 (unsigned int) du
.uc
[4], (unsigned int) du
.uc
[5],
180 (unsigned int) du
.uc
[6], (unsigned int) du
.uc
[7]);
182 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: DUK_DBLUNION_DOUBLE_NTOH");
187 * Basic double / byte union memory layout.
190 DUK_LOCAL
void duk__selftest_double_union_size(void) {
191 if (sizeof(duk__test_double_union
) != 8) {
192 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: invalid union size");
197 * Union aliasing, see misc/clang_aliasing.c.
200 DUK_LOCAL
void duk__selftest_double_aliasing(void) {
201 duk__test_double_union a
, b
;
203 /* This testcase fails when Emscripten-generated code runs on Firefox.
204 * It's not an issue because the failure should only affect packed
205 * duk_tval representation, which is not used with Emscripten.
207 #if defined(DUK_USE_NO_DOUBLE_ALIASING_SELFTEST)
208 #if defined(DUK_USE_PACKED_TVAL)
209 #error inconsistent defines: skipping double aliasing selftest when using packed duk_tval
214 /* Test signaling NaN and alias assignment in all
215 * endianness combinations.
219 a
.c
[0] = 0x11; a
.c
[1] = 0x22; a
.c
[2] = 0x33; a
.c
[3] = 0x44;
220 a
.c
[4] = 0x00; a
.c
[5] = 0x00; a
.c
[6] = 0xf1; a
.c
[7] = 0xff;
222 DUK__DBLUNION_CMP_TRUE(&a
, &b
);
225 a
.c
[0] = 0xff; a
.c
[1] = 0xf1; a
.c
[2] = 0x00; a
.c
[3] = 0x00;
226 a
.c
[4] = 0x44; a
.c
[5] = 0x33; a
.c
[6] = 0x22; a
.c
[7] = 0x11;
228 DUK__DBLUNION_CMP_TRUE(&a
, &b
);
231 a
.c
[0] = 0x00; a
.c
[1] = 0x00; a
.c
[2] = 0xf1; a
.c
[3] = 0xff;
232 a
.c
[4] = 0x11; a
.c
[5] = 0x22; a
.c
[6] = 0x33; a
.c
[7] = 0x44;
234 DUK__DBLUNION_CMP_TRUE(&a
, &b
);
238 * Zero sign, see misc/tcc_zerosign2.c.
241 DUK_LOCAL
void duk__selftest_double_zero_sign(void) {
242 volatile duk__test_double_union a
, b
;
246 DUK__DBLUNION_CMP_FALSE(&a
, &b
);
250 * Struct size/alignment if platform requires it
252 * There are some compiler specific struct padding pragmas etc in use, this
253 * selftest ensures they're correctly detected and used.
256 DUK_LOCAL
void duk__selftest_struct_align(void) {
257 #if (DUK_USE_ALIGN_BY == 4)
258 if ((sizeof(duk_hbuffer_fixed
) % 4) != 0) {
259 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: sizeof(duk_hbuffer_fixed) not aligned to 4");
261 #elif (DUK_USE_ALIGN_BY == 8)
262 if ((sizeof(duk_hbuffer_fixed
) % 8) != 0) {
263 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: sizeof(duk_hbuffer_fixed) not aligned to 8");
265 #elif (DUK_USE_ALIGN_BY == 1)
268 #error invalid DUK_USE_ALIGN_BY
275 * There are some platforms/compilers where 64-bit types are available
276 * but don't work correctly. Test for known cases.
279 DUK_LOCAL
void duk__selftest_64bit_arithmetic(void) {
280 #if defined(DUK_USE_64BIT_OPS)
281 volatile duk_int64_t i
;
282 volatile duk_double_t d
;
284 /* Catch a double-to-int64 cast issue encountered in practice. */
287 if (i
!= 0x80000000LL
) {
288 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: casting 2147483648.0 to duk_int64_t failed");
299 DUK_LOCAL
void duk__selftest_cast_double_to_small_uint(void) {
301 * https://github.com/svaarala/duktape/issues/127#issuecomment-77863473
307 duk_double_t d1v
, d2v
;
310 /* Test without volatiles */
313 u
= (duk_small_uint_t
) d1
;
314 d2
= (duk_double_t
) u
;
316 if (!(d1
== 1.0 && u
== 1 && d2
== 1.0 && d1
== d2
)) {
317 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: double to duk_small_uint_t cast failed");
320 /* Same test with volatiles */
323 uv
= (duk_small_uint_t
) d1v
;
324 d2v
= (duk_double_t
) uv
;
326 if (!(d1v
== 1.0 && uv
== 1 && d2v
== 1.0 && d1v
== d2v
)) {
327 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: double to duk_small_uint_t cast failed");
331 DUK_LOCAL
void duk__selftest_cast_double_to_uint32(void) {
333 * This test fails on an exotic ARM target; double-to-uint
334 * cast is incorrectly clamped to -signed- int highest value.
336 * https://github.com/svaarala/duktape/issues/336
342 dv
= 3735928559.0; /* 0xdeadbeef in decimal */
343 uv
= (duk_uint32_t
) dv
;
345 if (uv
!= 0xdeadbeefUL
) {
346 DUK_PANIC(DUK_ERR_INTERNAL_ERROR
, "self test failed: double to duk_uint32_t cast failed");
354 DUK_INTERNAL
void duk_selftest_run_tests(void) {
355 duk__selftest_types();
356 duk__selftest_packed_tval();
357 duk__selftest_twos_complement();
358 duk__selftest_byte_order();
359 duk__selftest_bswap_macros();
360 duk__selftest_double_union_size();
361 duk__selftest_double_aliasing();
362 duk__selftest_double_zero_sign();
363 duk__selftest_struct_align();
364 duk__selftest_64bit_arithmetic();
365 duk__selftest_cast_double_to_small_uint();
366 duk__selftest_cast_double_to_uint32();
369 #undef DUK__DBLUNION_CMP_TRUE
370 #undef DUK__DBLUNION_CMP_FALSE
372 #endif /* DUK_USE_SELF_TESTS */