/* * Self tests to ensure execution environment is sane. Intended to catch * compiler/platform problems which cannot be detected at compile time. */ #include "duk_internal.h" #if defined(DUK_USE_SELF_TESTS) /* * Unions and structs for self tests */ typedef union { double d; duk_uint8_t c[8]; } duk__test_double_union; #define DUK__DBLUNION_CMP_TRUE(a,b) do { \ if (DUK_MEMCMP((void *) (a), (void *) (b), sizeof(duk__test_double_union)) != 0) { \ DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double union compares false (expected true)"); \ } \ } while (0) #define DUK__DBLUNION_CMP_FALSE(a,b) do { \ if (DUK_MEMCMP((void *) (a), (void *) (b), sizeof(duk__test_double_union)) == 0) { \ DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double union compares true (expected false)"); \ } \ } while (0) typedef union { duk_uint32_t i; duk_uint8_t c[8]; } duk__test_u32_union; /* * Various sanity checks for typing */ DUK_LOCAL void duk__selftest_types(void) { if (!(sizeof(duk_int8_t) == 1 && sizeof(duk_uint8_t) == 1 && sizeof(duk_int16_t) == 2 && sizeof(duk_uint16_t) == 2 && sizeof(duk_int32_t) == 4 && sizeof(duk_uint32_t) == 4)) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_(u)int{8,16,32}_t size"); } #if defined(DUK_USE_64BIT_OPS) if (!(sizeof(duk_int64_t) == 8 && sizeof(duk_uint64_t) == 8)) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_(u)int64_t size"); } #endif if (!(sizeof(duk_size_t) >= sizeof(duk_uint_t))) { /* Some internal code now assumes that all duk_uint_t values * can be expressed with a duk_size_t. */ DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_size_t is smaller than duk_uint_t"); } if (!(sizeof(duk_int_t) >= 4)) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_int_t is not 32 bits"); } } /* * Packed tval sanity */ DUK_LOCAL void duk__selftest_packed_tval(void) { #if defined(DUK_USE_PACKED_TVAL) if (sizeof(void *) > 4) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: packed duk_tval in use but sizeof(void *) > 4"); } #endif } /* * Two's complement arithmetic. */ DUK_LOCAL void duk__selftest_twos_complement(void) { volatile int test; test = -1; if (((duk_uint8_t *) &test)[0] != (duk_uint8_t) 0xff) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: two's complement arithmetic"); } } /* * Byte order. Important to self check, because on some exotic platforms * there is no actual detection but rather assumption based on platform * defines. */ DUK_LOCAL void duk__selftest_byte_order(void) { duk__test_u32_union u1; duk__test_double_union u2; /* * >>> struct.pack('>d', 102030405060).encode('hex') * '4237c17c6dc40000' */ #if defined(DUK_USE_INTEGER_LE) u1.c[0] = 0xef; u1.c[1] = 0xbe; u1.c[2] = 0xad; u1.c[3] = 0xde; #elif defined(DUK_USE_INTEGER_ME) #error integer mixed endian not supported now #elif defined(DUK_USE_INTEGER_BE) u1.c[0] = 0xde; u1.c[1] = 0xad; u1.c[2] = 0xbe; u1.c[3] = 0xef; #else #error unknown integer endianness #endif #if defined(DUK_USE_DOUBLE_LE) u2.c[0] = 0x00; u2.c[1] = 0x00; u2.c[2] = 0xc4; u2.c[3] = 0x6d; u2.c[4] = 0x7c; u2.c[5] = 0xc1; u2.c[6] = 0x37; u2.c[7] = 0x42; #elif defined(DUK_USE_DOUBLE_ME) u2.c[0] = 0x7c; u2.c[1] = 0xc1; u2.c[2] = 0x37; u2.c[3] = 0x42; u2.c[4] = 0x00; u2.c[5] = 0x00; u2.c[6] = 0xc4; u2.c[7] = 0x6d; #elif defined(DUK_USE_DOUBLE_BE) u2.c[0] = 0x42; u2.c[1] = 0x37; u2.c[2] = 0xc1; u2.c[3] = 0x7c; u2.c[4] = 0x6d; u2.c[5] = 0xc4; u2.c[6] = 0x00; u2.c[7] = 0x00; #else #error unknown double endianness #endif if (u1.i != (duk_uint32_t) 0xdeadbeefUL) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_uint32_t byte order"); } if (u2.d != (double) 102030405060.0) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double byte order"); } } /* * Basic double / byte union memory layout. */ DUK_LOCAL void duk__selftest_double_union_size(void) { if (sizeof(duk__test_double_union) != 8) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: invalid union size"); } } /* * Union aliasing, see misc/clang_aliasing.c. */ DUK_LOCAL void duk__selftest_double_aliasing(void) { duk__test_double_union a, b; /* This testcase fails when Emscripten-generated code runs on Firefox. * It's not an issue because the failure should only affect packed * duk_tval representation, which is not used with Emscripten. */ #if defined(DUK_USE_NO_DOUBLE_ALIASING_SELFTEST) #if defined(DUK_USE_PACKED_TVAL) #error inconsistent defines: skipping double aliasing selftest when using packed duk_tval #endif return; #endif /* Test signaling NaN and alias assignment in all * endianness combinations. */ /* little endian */ a.c[0] = 0x11; a.c[1] = 0x22; a.c[2] = 0x33; a.c[3] = 0x44; a.c[4] = 0x00; a.c[5] = 0x00; a.c[6] = 0xf1; a.c[7] = 0xff; b = a; DUK__DBLUNION_CMP_TRUE(&a, &b); /* big endian */ a.c[0] = 0xff; a.c[1] = 0xf1; a.c[2] = 0x00; a.c[3] = 0x00; a.c[4] = 0x44; a.c[5] = 0x33; a.c[6] = 0x22; a.c[7] = 0x11; b = a; DUK__DBLUNION_CMP_TRUE(&a, &b); /* mixed endian */ a.c[0] = 0x00; a.c[1] = 0x00; a.c[2] = 0xf1; a.c[3] = 0xff; a.c[4] = 0x11; a.c[5] = 0x22; a.c[6] = 0x33; a.c[7] = 0x44; b = a; DUK__DBLUNION_CMP_TRUE(&a, &b); } /* * Zero sign, see misc/tcc_zerosign2.c. */ DUK_LOCAL void duk__selftest_double_zero_sign(void) { volatile duk__test_double_union a, b; a.d = 0.0; b.d = -a.d; DUK__DBLUNION_CMP_FALSE(&a, &b); } /* * Struct size/alignment if platform requires it * * There are some compiler specific struct padding pragmas etc in use, this * selftest ensures they're correctly detected and used. */ DUK_LOCAL void duk__selftest_struct_align(void) { #if defined(DUK_USE_ALIGN_4) if ((sizeof(duk_hbuffer_fixed) % 4) != 0) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: sizeof(duk_hbuffer_fixed) not aligned to 4"); } #elif defined(DUK_USE_ALIGN_8) if ((sizeof(duk_hbuffer_fixed) % 8) != 0) { DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: sizeof(duk_hbuffer_fixed) not aligned to 8"); } #else /* no check */ #endif } /* * Self test main */ DUK_INTERNAL void duk_selftest_run_tests(void) { duk__selftest_types(); duk__selftest_packed_tval(); duk__selftest_twos_complement(); duk__selftest_byte_order(); duk__selftest_double_union_size(); duk__selftest_double_aliasing(); duk__selftest_double_zero_sign(); duk__selftest_struct_align(); } #undef DUK__DBLUNION_CMP_TRUE #undef DUK__DBLUNION_CMP_FALSE #endif /* DUK_USE_SELF_TESTS */