]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 c[8]; | |
17 | } duk__test_double_union; | |
18 | ||
19 | #define DUK__DBLUNION_CMP_TRUE(a,b) do { \ | |
11fdf7f2 | 20 | if (DUK_MEMCMP((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) != 0) { \ |
7c673cae FG |
21 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double union compares false (expected true)"); \ |
22 | } \ | |
23 | } while (0) | |
24 | ||
25 | #define DUK__DBLUNION_CMP_FALSE(a,b) do { \ | |
11fdf7f2 | 26 | if (DUK_MEMCMP((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) == 0) { \ |
7c673cae FG |
27 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double union compares true (expected false)"); \ |
28 | } \ | |
29 | } while (0) | |
30 | ||
31 | typedef union { | |
32 | duk_uint32_t i; | |
33 | duk_uint8_t c[8]; | |
34 | } duk__test_u32_union; | |
35 | ||
36 | /* | |
37 | * Various sanity checks for typing | |
38 | */ | |
39 | ||
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"); | |
48 | } | |
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"); | |
53 | } | |
54 | #endif | |
55 | ||
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. | |
59 | */ | |
60 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_size_t is smaller than duk_uint_t"); | |
61 | } | |
62 | if (!(sizeof(duk_int_t) >= 4)) { | |
63 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_int_t is not 32 bits"); | |
64 | } | |
65 | } | |
66 | ||
67 | /* | |
68 | * Packed tval sanity | |
69 | */ | |
70 | ||
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"); | |
75 | } | |
76 | #endif | |
77 | } | |
78 | ||
79 | /* | |
80 | * Two's complement arithmetic. | |
81 | */ | |
82 | ||
83 | DUK_LOCAL void duk__selftest_twos_complement(void) { | |
84 | volatile int test; | |
85 | test = -1; | |
11fdf7f2 TL |
86 | |
87 | /* Note that byte order doesn't affect this test: all bytes in | |
88 | * 'test' will be 0xFF for two's complement. | |
89 | */ | |
90 | if (((volatile duk_uint8_t *) &test)[0] != (duk_uint8_t) 0xff) { | |
7c673cae FG |
91 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: two's complement arithmetic"); |
92 | } | |
93 | } | |
94 | ||
95 | /* | |
96 | * Byte order. Important to self check, because on some exotic platforms | |
97 | * there is no actual detection but rather assumption based on platform | |
98 | * defines. | |
99 | */ | |
100 | ||
101 | DUK_LOCAL void duk__selftest_byte_order(void) { | |
102 | duk__test_u32_union u1; | |
103 | duk__test_double_union u2; | |
104 | ||
105 | /* | |
106 | * >>> struct.pack('>d', 102030405060).encode('hex') | |
107 | * '4237c17c6dc40000' | |
108 | */ | |
109 | #if defined(DUK_USE_INTEGER_LE) | |
110 | u1.c[0] = 0xef; u1.c[1] = 0xbe; u1.c[2] = 0xad; u1.c[3] = 0xde; | |
111 | #elif defined(DUK_USE_INTEGER_ME) | |
112 | #error integer mixed endian not supported now | |
113 | #elif defined(DUK_USE_INTEGER_BE) | |
114 | u1.c[0] = 0xde; u1.c[1] = 0xad; u1.c[2] = 0xbe; u1.c[3] = 0xef; | |
115 | #else | |
116 | #error unknown integer endianness | |
117 | #endif | |
118 | ||
119 | #if defined(DUK_USE_DOUBLE_LE) | |
120 | u2.c[0] = 0x00; u2.c[1] = 0x00; u2.c[2] = 0xc4; u2.c[3] = 0x6d; | |
121 | u2.c[4] = 0x7c; u2.c[5] = 0xc1; u2.c[6] = 0x37; u2.c[7] = 0x42; | |
122 | #elif defined(DUK_USE_DOUBLE_ME) | |
123 | u2.c[0] = 0x7c; u2.c[1] = 0xc1; u2.c[2] = 0x37; u2.c[3] = 0x42; | |
124 | u2.c[4] = 0x00; u2.c[5] = 0x00; u2.c[6] = 0xc4; u2.c[7] = 0x6d; | |
125 | #elif defined(DUK_USE_DOUBLE_BE) | |
126 | u2.c[0] = 0x42; u2.c[1] = 0x37; u2.c[2] = 0xc1; u2.c[3] = 0x7c; | |
127 | u2.c[4] = 0x6d; u2.c[5] = 0xc4; u2.c[6] = 0x00; u2.c[7] = 0x00; | |
128 | #else | |
129 | #error unknown double endianness | |
130 | #endif | |
131 | ||
132 | if (u1.i != (duk_uint32_t) 0xdeadbeefUL) { | |
133 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_uint32_t byte order"); | |
134 | } | |
135 | ||
136 | if (u2.d != (double) 102030405060.0) { | |
137 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double byte order"); | |
138 | } | |
139 | } | |
140 | ||
141 | /* | |
142 | * DUK_BSWAP macros | |
143 | */ | |
144 | ||
145 | DUK_LOCAL void duk__selftest_bswap_macros(void) { | |
146 | duk_uint32_t x32; | |
147 | duk_uint16_t x16; | |
148 | duk_double_union du; | |
149 | duk_double_t du_diff; | |
150 | ||
151 | x16 = 0xbeefUL; | |
152 | x16 = DUK_BSWAP16(x16); | |
153 | if (x16 != (duk_uint16_t) 0xefbeUL) { | |
154 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: DUK_BSWAP16"); | |
155 | } | |
156 | ||
157 | x32 = 0xdeadbeefUL; | |
158 | x32 = DUK_BSWAP32(x32); | |
159 | if (x32 != (duk_uint32_t) 0xefbeaddeUL) { | |
160 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: DUK_BSWAP32"); | |
161 | } | |
162 | ||
163 | /* >>> struct.unpack('>d', '4000112233445566'.decode('hex')) | |
164 | * (2.008366013071895,) | |
165 | */ | |
166 | ||
167 | du.uc[0] = 0x40; du.uc[1] = 0x00; du.uc[2] = 0x11; du.uc[3] = 0x22; | |
168 | du.uc[4] = 0x33; du.uc[5] = 0x44; du.uc[6] = 0x55; du.uc[7] = 0x66; | |
169 | DUK_DBLUNION_DOUBLE_NTOH(&du); | |
170 | du_diff = du.d - 2.008366013071895; | |
171 | #if 0 | |
172 | DUK_FPRINTF(DUK_STDERR, "du_diff: %lg\n", (double) du_diff); | |
173 | #endif | |
174 | if (du_diff > 1e-15) { | |
175 | /* Allow very small lenience because some compilers won't parse | |
176 | * exact IEEE double constants (happened in matrix testing with | |
177 | * Linux gcc-4.8 -m32 at least). | |
178 | */ | |
179 | #if 0 | |
180 | DUK_FPRINTF(DUK_STDERR, "Result of DUK_DBLUNION_DOUBLE_NTOH: %02x %02x %02x %02x %02x %02x %02x %02x\n", | |
181 | (unsigned int) du.uc[0], (unsigned int) du.uc[1], | |
182 | (unsigned int) du.uc[2], (unsigned int) du.uc[3], | |
183 | (unsigned int) du.uc[4], (unsigned int) du.uc[5], | |
184 | (unsigned int) du.uc[6], (unsigned int) du.uc[7]); | |
185 | #endif | |
186 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: DUK_DBLUNION_DOUBLE_NTOH"); | |
187 | } | |
188 | } | |
189 | ||
190 | /* | |
191 | * Basic double / byte union memory layout. | |
192 | */ | |
193 | ||
194 | DUK_LOCAL void duk__selftest_double_union_size(void) { | |
195 | if (sizeof(duk__test_double_union) != 8) { | |
196 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: invalid union size"); | |
197 | } | |
198 | } | |
199 | ||
200 | /* | |
201 | * Union aliasing, see misc/clang_aliasing.c. | |
202 | */ | |
203 | ||
204 | DUK_LOCAL void duk__selftest_double_aliasing(void) { | |
205 | duk__test_double_union a, b; | |
206 | ||
207 | /* This testcase fails when Emscripten-generated code runs on Firefox. | |
208 | * It's not an issue because the failure should only affect packed | |
209 | * duk_tval representation, which is not used with Emscripten. | |
210 | */ | |
11fdf7f2 TL |
211 | #if !defined(DUK_USE_PACKED_TVAL) |
212 | DUK_D(DUK_DPRINT("skip double aliasing self test when duk_tval is not packed")); | |
7c673cae FG |
213 | return; |
214 | #endif | |
215 | ||
11fdf7f2 | 216 | /* Test signaling NaN and alias assignment in all endianness combinations. |
7c673cae FG |
217 | */ |
218 | ||
219 | /* little endian */ | |
220 | a.c[0] = 0x11; a.c[1] = 0x22; a.c[2] = 0x33; a.c[3] = 0x44; | |
221 | a.c[4] = 0x00; a.c[5] = 0x00; a.c[6] = 0xf1; a.c[7] = 0xff; | |
222 | b = a; | |
223 | DUK__DBLUNION_CMP_TRUE(&a, &b); | |
224 | ||
225 | /* big endian */ | |
226 | a.c[0] = 0xff; a.c[1] = 0xf1; a.c[2] = 0x00; a.c[3] = 0x00; | |
227 | a.c[4] = 0x44; a.c[5] = 0x33; a.c[6] = 0x22; a.c[7] = 0x11; | |
228 | b = a; | |
229 | DUK__DBLUNION_CMP_TRUE(&a, &b); | |
230 | ||
231 | /* mixed endian */ | |
232 | a.c[0] = 0x00; a.c[1] = 0x00; a.c[2] = 0xf1; a.c[3] = 0xff; | |
233 | a.c[4] = 0x11; a.c[5] = 0x22; a.c[6] = 0x33; a.c[7] = 0x44; | |
234 | b = a; | |
235 | DUK__DBLUNION_CMP_TRUE(&a, &b); | |
236 | } | |
237 | ||
238 | /* | |
239 | * Zero sign, see misc/tcc_zerosign2.c. | |
240 | */ | |
241 | ||
242 | DUK_LOCAL void duk__selftest_double_zero_sign(void) { | |
11fdf7f2 | 243 | duk__test_double_union a, b; |
7c673cae FG |
244 | |
245 | a.d = 0.0; | |
246 | b.d = -a.d; | |
247 | DUK__DBLUNION_CMP_FALSE(&a, &b); | |
248 | } | |
249 | ||
250 | /* | |
251 | * Struct size/alignment if platform requires it | |
252 | * | |
253 | * There are some compiler specific struct padding pragmas etc in use, this | |
254 | * selftest ensures they're correctly detected and used. | |
255 | */ | |
256 | ||
257 | DUK_LOCAL void duk__selftest_struct_align(void) { | |
258 | #if (DUK_USE_ALIGN_BY == 4) | |
259 | if ((sizeof(duk_hbuffer_fixed) % 4) != 0) { | |
260 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: sizeof(duk_hbuffer_fixed) not aligned to 4"); | |
261 | } | |
262 | #elif (DUK_USE_ALIGN_BY == 8) | |
263 | if ((sizeof(duk_hbuffer_fixed) % 8) != 0) { | |
264 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: sizeof(duk_hbuffer_fixed) not aligned to 8"); | |
265 | } | |
266 | #elif (DUK_USE_ALIGN_BY == 1) | |
267 | /* no check */ | |
268 | #else | |
269 | #error invalid DUK_USE_ALIGN_BY | |
270 | #endif | |
271 | } | |
272 | ||
273 | /* | |
274 | * 64-bit arithmetic | |
275 | * | |
276 | * There are some platforms/compilers where 64-bit types are available | |
277 | * but don't work correctly. Test for known cases. | |
278 | */ | |
279 | ||
280 | DUK_LOCAL void duk__selftest_64bit_arithmetic(void) { | |
281 | #if defined(DUK_USE_64BIT_OPS) | |
282 | volatile duk_int64_t i; | |
283 | volatile duk_double_t d; | |
284 | ||
285 | /* Catch a double-to-int64 cast issue encountered in practice. */ | |
286 | d = 2147483648.0; | |
287 | i = (duk_int64_t) d; | |
288 | if (i != 0x80000000LL) { | |
289 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: casting 2147483648.0 to duk_int64_t failed"); | |
290 | } | |
291 | #else | |
292 | /* nop */ | |
293 | #endif | |
294 | } | |
295 | ||
296 | /* | |
297 | * Casting | |
298 | */ | |
299 | ||
300 | DUK_LOCAL void duk__selftest_cast_double_to_small_uint(void) { | |
301 | /* | |
302 | * https://github.com/svaarala/duktape/issues/127#issuecomment-77863473 | |
303 | */ | |
304 | ||
305 | duk_double_t d1, d2; | |
306 | duk_small_uint_t u; | |
307 | ||
308 | duk_double_t d1v, d2v; | |
309 | duk_small_uint_t uv; | |
310 | ||
311 | /* Test without volatiles */ | |
312 | ||
313 | d1 = 1.0; | |
314 | u = (duk_small_uint_t) d1; | |
315 | d2 = (duk_double_t) u; | |
316 | ||
317 | if (!(d1 == 1.0 && u == 1 && d2 == 1.0 && d1 == d2)) { | |
318 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double to duk_small_uint_t cast failed"); | |
319 | } | |
320 | ||
321 | /* Same test with volatiles */ | |
322 | ||
323 | d1v = 1.0; | |
324 | uv = (duk_small_uint_t) d1v; | |
325 | d2v = (duk_double_t) uv; | |
326 | ||
327 | if (!(d1v == 1.0 && uv == 1 && d2v == 1.0 && d1v == d2v)) { | |
328 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double to duk_small_uint_t cast failed"); | |
329 | } | |
330 | } | |
331 | ||
332 | DUK_LOCAL void duk__selftest_cast_double_to_uint32(void) { | |
333 | /* | |
334 | * This test fails on an exotic ARM target; double-to-uint | |
335 | * cast is incorrectly clamped to -signed- int highest value. | |
336 | * | |
337 | * https://github.com/svaarala/duktape/issues/336 | |
338 | */ | |
339 | ||
340 | duk_double_t dv; | |
341 | duk_uint32_t uv; | |
342 | ||
343 | dv = 3735928559.0; /* 0xdeadbeef in decimal */ | |
344 | uv = (duk_uint32_t) dv; | |
345 | ||
346 | if (uv != 0xdeadbeefUL) { | |
347 | DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double to duk_uint32_t cast failed"); | |
348 | } | |
349 | } | |
350 | ||
351 | /* | |
352 | * Self test main | |
353 | */ | |
354 | ||
355 | DUK_INTERNAL void duk_selftest_run_tests(void) { | |
356 | duk__selftest_types(); | |
357 | duk__selftest_packed_tval(); | |
358 | duk__selftest_twos_complement(); | |
359 | duk__selftest_byte_order(); | |
360 | duk__selftest_bswap_macros(); | |
361 | duk__selftest_double_union_size(); | |
362 | duk__selftest_double_aliasing(); | |
363 | duk__selftest_double_zero_sign(); | |
364 | duk__selftest_struct_align(); | |
365 | duk__selftest_64bit_arithmetic(); | |
366 | duk__selftest_cast_double_to_small_uint(); | |
367 | duk__selftest_cast_double_to_uint32(); | |
368 | } | |
369 | ||
370 | #undef DUK__DBLUNION_CMP_TRUE | |
371 | #undef DUK__DBLUNION_CMP_FALSE | |
372 | ||
373 | #endif /* DUK_USE_SELF_TESTS */ |