# coding=utf-8 import glob, hashlib, os, re, shutil, subprocess, sys import tools.shared from tools.shared import * from runner import RunnerCore, path_from_root, checked_sanity, test_modes class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline def is_le32(self): return not ('i386-pc-linux-gnu' in COMPILER_OPTS or self.env.get('EMCC_LLVM_TARGET') == 'i386-pc-linux-gnu') def test_hello_world(self): src = ''' #include int main() { printf("hello, world!\\n"); return 0; } ''' self.do_run(src, 'hello, world!') assert 'EMSCRIPTEN_GENERATED_FUNCTIONS' not in open(self.in_dir('src.cpp.o.js')).read(), 'must not emit this unneeded internal thing' def test_intvars(self): if self.emcc_args == None: return self.skip('needs ta2') src = ''' #include int global = 20; int *far; int main() { int x = 5; int y = x+17; int z = (y-1)/2; // Should stay an integer after division! y += 1; int w = x*3+4; int k = w < 15 ? 99 : 101; far = &k; *far += global; int i = k > 100; // Should be an int, not a bool! int j = i << 6; j >>= 1; j = j ^ 5; int h = 1; h |= 0; int p = h; p &= 0; printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", x, y, z, w, k, i, j, h, p); long hash = -1; size_t perturb; int ii = 0; for (perturb = hash; ; perturb >>= 5) { printf("%d:%d", ii, perturb); ii++; if (ii == 9) break; printf(","); } printf("*\\n"); printf("*%.1d,%.2d*\\n", 56, 9); // Fixed-point math on 64-bit ints. Tricky to support since we have no 64-bit shifts in JS { struct Fixed { static int Mult(int a, int b) { return ((long long)a * (long long)b) >> 16; } }; printf("fixed:%d\\n", Fixed::Mult(150000, 140000)); } printf("*%ld*%p\\n", (long)21, &hash); // The %p should not enter an infinite loop! return 0; } ''' self.do_run(src, '*5,23,10,19,121,1,37,1,0*\n0:-1,1:134217727,2:4194303,3:131071,4:4095,5:127,6:3,7:0,8:0*\n*56,09*\nfixed:320434\n*21*') def test_sintvars(self): Settings.CORRECT_SIGNS = 1 # Relevant to this test src = ''' #include struct S { char *match_start; char *strstart; }; int main() { struct S _s; struct S *s = &_s; unsigned short int sh; s->match_start = (char*)32522; s->strstart = (char*)(32780); printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start)); sh = s->strstart - s->match_start; printf("*%d,%d*\\n", sh, sh>>7); s->match_start = (char*)32999; s->strstart = (char*)(32780); printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start)); sh = s->strstart - s->match_start; printf("*%d,%d*\\n", sh, sh>>7); } ''' output = '*32780,32522,258*\n*258,2*\n*32780,32999,-219*\n*65317,510*' Settings.CORRECT_OVERFLOWS = 0 # We should not need overflow correction to get this right self.do_run(src, output, force_c=True) def test_i64(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') src = ''' #include int main() { long long a = 0x2b00505c10; long long b = a >> 29; long long c = a >> 32; long long d = a >> 34; printf("*%Ld,%Ld,%Ld,%Ld*\\n", a, b, c, d); unsigned long long ua = 0x2b00505c10; unsigned long long ub = ua >> 29; unsigned long long uc = ua >> 32; unsigned long long ud = ua >> 34; printf("*%Ld,%Ld,%Ld,%Ld*\\n", ua, ub, uc, ud); long long x = 0x0000def123450789ULL; // any bigger than this, and we long long y = 0x00020ef123456089ULL; // start to run into the double precision limit! printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2); printf("*"); long long z = 13; int n = 0; while (z > 1) { printf("%.2f,", (float)z); // these must be integers! z = z >> 1; n++; } printf("*%d*\\n", n); return 0; } ''' self.do_run(src, '*184688860176,344,43,10*\n*184688860176,344,43,10*\n*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') src = r''' #include #include #include int64_t returner1() { return 0x0000def123450789ULL; } int64_t returner2(int test) { while (test > 10) test /= 2; // confuse the compiler so it doesn't eliminate this function return test > 5 ? 0x0000def123450123ULL : 0ULL; } void modifier1(int64_t t) { t |= 12; printf("m1: %Ld\n", t); } void modifier2(int64_t &t) { t |= 12; } int truthy() { int x = time(0); while (x > 10) { x |= 7; x /= 2; } return x < 3; } struct IUB { int c; long long d; }; IUB iub[] = { { 55, 17179869201 }, { 122, 25769803837 }, }; int main(int argc, char **argv) { int64_t x1 = 0x1234def123450789ULL; int64_t x2 = 0x1234def123450788ULL; int64_t x3 = 0x1234def123450789ULL; printf("*%Ld\n%d,%d,%d,%d,%d\n%d,%d,%d,%d,%d*\n", x1, x1==x2, x1x2, x1>=x2, // note: some rounding in the printing! x1==x3, x1x3, x1>=x3); printf("*%Ld*\n", returner1()); printf("*%Ld*\n", returner2(30)); uint64_t maxx = -1ULL; printf("*%Lu*\n*%Lu*\n", maxx, maxx >> 5); // Make sure params are not modified if they shouldn't be int64_t t = 123; modifier1(t); printf("*%Ld*\n", t); modifier2(t); printf("*%Ld*\n", t); // global structs with i64s printf("*%d,%Ld*\n*%d,%Ld*\n", iub[0].c, iub[0].d, iub[1].c, iub[1].d); // Bitshifts { int64_t a = -1; int64_t b = a >> 29; int64_t c = a >> 32; int64_t d = a >> 34; printf("*%Ld,%Ld,%Ld,%Ld*\n", a, b, c, d); uint64_t ua = -1; int64_t ub = ua >> 29; int64_t uc = ua >> 32; int64_t ud = ua >> 34; printf("*%Ld,%Ld,%Ld,%Ld*\n", ua, ub, uc, ud); } // Nonconstant bitshifts { int64_t a = -1; int64_t b = a >> (29 - argc + 1); int64_t c = a >> (32 - argc + 1); int64_t d = a >> (34 - argc + 1); printf("*%Ld,%Ld,%Ld,%Ld*\n", a, b, c, d); uint64_t ua = -1; int64_t ub = ua >> (29 - argc + 1); int64_t uc = ua >> (32 - argc + 1); int64_t ud = ua >> (34 - argc + 1); printf("*%Ld,%Ld,%Ld,%Ld*\n", ua, ub, uc, ud); } // Math mixtures with doubles { uint64_t a = 5; double b = 6.8; uint64_t c = a * b; if (truthy()) printf("*%d,%d,%d*\n", (int)&a, (int)&b, (int)&c); // printing addresses prevents optimizations printf("*prod:%llu*\n", c); } // Basic (rounded, for now) math. Just check compilation. int64_t a = 0x1234def123450789ULL; a--; if (truthy()) a--; // confuse optimizer int64_t b = 0x1234000000450789ULL; b++; if (truthy()) b--; // confuse optimizer printf("*%Ld,%Ld,%Ld,%Ld*\n", (a+b)/5000, (a-b)/5000, (a*3)/5000, (a/5)/5000); a -= 17; if (truthy()) a += 5; // confuse optimizer b -= 17; if (truthy()) b += 121; // confuse optimizer printf("*%Lx,%Lx,%Lx,%Lx*\n", b - a, b - a/2, b/2 - a, b - 20); if (truthy()) a += 5/b; // confuse optimizer if (truthy()) b += 121*(3+a/b); // confuse optimizer printf("*%Lx,%Lx,%Lx,%Lx*\n", a - b, a - b/2, a/2 - b, a - 20); return 0; } ''' self.do_run(src, '*1311918518731868041\n' + '0,0,0,1,1\n' + '1,0,1,0,1*\n' + '*245127260211081*\n' + '*245127260209443*\n' + '*18446744073709551615*\n' + '*576460752303423487*\n' + 'm1: 127\n' + '*123*\n' + '*127*\n' + '*55,17179869201*\n' + '*122,25769803837*\n' + '*-1,-1,-1,-1*\n' + '*-1,34359738367,4294967295,1073741823*\n' + '*-1,-1,-1,-1*\n' + '*-1,34359738367,4294967295,1073741823*\n' + '*prod:34*\n' + '*524718382041609,49025451137,787151111239120,52476740749274*\n' + '*ffff210edd000002,91990876ea283be,f6e5210edcdd7c45,1234000000450765*\n' + '*def122fffffe,91adef1232283bb,f6e66f78915d7c42,1234def123450763*\n') src = r''' #include #include int main() { long long i,j,k; i = 0; j = -1, k = 1; printf( "*\n" ); printf( "%s\n", i > j ? "Ok": "Fail" ); printf( "%s\n", k > i ? "Ok": "Fail" ); printf( "%s\n", k > j ? "Ok": "Fail" ); printf( "%s\n", i < j ? "Fail": "Ok" ); printf( "%s\n", k < i ? "Fail": "Ok" ); printf( "%s\n", k < j ? "Fail": "Ok" ); printf( "%s\n", (i-j) >= k ? "Ok": "Fail" ); printf( "%s\n", (i-j) <= k ? "Ok": "Fail" ); printf( "%s\n", i > std::numeric_limits::min() ? "Ok": "Fail" ); printf( "%s\n", i < std::numeric_limits::max() ? "Ok": "Fail" ); printf( "*\n" ); } ''' self.do_run(src, '*\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\n*') # stuff that also needs sign corrections Settings.CORRECT_SIGNS = 1 src = r''' #include #include int main() { // i32 vs i64 int32_t small = -1; int64_t large = -1; printf("*%d*\n", small == large); small++; printf("*%d*\n", small == large); uint32_t usmall = -1; uint64_t ularge = -1; printf("*%d*\n", usmall == ularge); return 0; } ''' self.do_run(src, '*1*\n*0*\n*0*\n') def test_i64_b(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include #include typedef long long int64; #define PRMJ_USEC_PER_SEC 1000000L int main(int argc, char * argv[]) { int64 sec = 1329409675 + argc; int64 usec = 2329509675; int64 mul = int64(sec) * PRMJ_USEC_PER_SEC; int64 add = mul + int64(usec); int add_low = add; int add_high = add >> 32; printf("*%lld,%lld,%u,%u*\n", mul, add, add_low, add_high); int64 x = sec + (usec << 25); x >>= argc*3; printf("*%llu*\n", x); return 0; } ''' self.do_run(src, '*1329409676000000,1329412005509675,3663280683,309527*\n*9770671914067409*\n') def test_i64_cmp(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include typedef long long int64; bool compare(int64 val) { return val == -12; } bool compare2(int64 val) { return val < -12; } int main(int argc, char * argv[]) { printf("*%d,%d,%d,%d,%d,%d*\n", argc, compare(argc-1-12), compare(1000+argc), compare2(argc-1-10), compare2(argc-1-14), compare2(argc+1000)); return 0; } ''' self.do_run(src, '*1,1,0,0,1,0*\n') def test_i64_cmp2(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include #include typedef int32_t INT32; typedef int64_t INT64; typedef uint8_t UINT8; void interface_clock_changed() { UINT8 m_divshift; INT32 m_divisor; //INT64 attos = m_attoseconds_per_cycle; INT64 attos = 279365114840; m_divshift = 0; while (attos >= (1UL << 31)) { m_divshift++; printf("m_divshift is %i, on %Ld >?= %lu\n", m_divshift, attos, 1UL << 31); attos >>= 1; } m_divisor = attos; printf("m_divisor is %i\n",m_divisor); } int main() { interface_clock_changed(); return 0; } ''' self.do_run(src, '''m_divshift is 1, on 279365114840 >?= 2147483648 m_divshift is 2, on 139682557420 >?= 2147483648 m_divshift is 3, on 69841278710 >?= 2147483648 m_divshift is 4, on 34920639355 >?= 2147483648 m_divshift is 5, on 17460319677 >?= 2147483648 m_divshift is 6, on 8730159838 >?= 2147483648 m_divshift is 7, on 4365079919 >?= 2147483648 m_divshift is 8, on 2182539959 >?= 2147483648 m_divisor is 1091269979 ''') def test_i64_double(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include typedef long long int64; #define JSDOUBLE_HI32_SIGNBIT 0x80000000 bool JSDOUBLE_IS_NEGZERO(double d) { union { struct { unsigned int lo, hi; } s; double d; } x; if (d != 0) return false; x.d = d; return (x.s.hi & JSDOUBLE_HI32_SIGNBIT) != 0; } bool JSINT64_IS_NEGZERO(int64 l) { union { int64 i; double d; } x; if (l != 0) return false; x.i = l; return x.d == -0; } int main(int argc, char * argv[]) { printf("*%d,%d,%d,%d*\n", JSDOUBLE_IS_NEGZERO(0), JSDOUBLE_IS_NEGZERO(-0), JSDOUBLE_IS_NEGZERO(-1), JSDOUBLE_IS_NEGZERO(+1)); printf("*%d,%d,%d,%d*\n", JSINT64_IS_NEGZERO(0), JSINT64_IS_NEGZERO(-0), JSINT64_IS_NEGZERO(-1), JSINT64_IS_NEGZERO(+1)); return 0; } ''' self.do_run(src, '*0,0,0,0*\n*1,1,0,0*\n') # same as gcc def test_i64_umul(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include #include typedef uint32_t UINT32; typedef uint64_t UINT64; int main() { volatile UINT32 testu32a = 2375724032U; UINT32 bigu32 = 0xffffffffU; volatile UINT64 testu64a = 14746250828952703000U; while ((UINT64)testu32a * (UINT64)bigu32 < testu64a) { printf("testu64a is %llu\n", testu64a); testu64a /= 2; } return 0; } ''' self.do_run(src, 'testu64a is 14746250828952703000\n') def test_i64_precise(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include #include int main() { uint64_t x = 0, y = 0; for (int i = 0; i < 64; i++) { x += 1ULL << i; y += x; x /= 3; y *= 5; printf("unsigned %d: %llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", i, x, y, x+y, x-y, x*y, y ? x/y : 0, x ? y/x : 0, y ? x%y : 0, x ? y%x : 0); } int64_t x2 = 0, y2 = 0; for (int i = 0; i < 64; i++) { x2 += 1LL << i; y2 += x2; x2 /= 3 * (i % 7 ? -1 : 1); y2 *= 5 * (i % 2 ? -1 : 1); printf("signed %d: %lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld\n", i, x2, y2, x2+y2, x2-y2, x2*y2, y2 ? x2/y2 : 0, x2 ? y2/x2 : 0, y2 ? x2%y2 : 0, x2 ? y2%x2 : 0); } return 0; } ''' self.do_run(src, open(path_from_root('tests', 'i64_precise.txt')).read()) # Verify that even if we ask for precision, if it is not needed it is not included Settings.PRECISE_I64_MATH = 1 src = ''' #include #include int main(int argc, char **argv) { uint64_t x = 2125299906845564, y = 1225891506842664; if (argc == 12) { x = x >> 1; y = y >> 1; } x = x & 12ULL; y = y | 12ULL; x = x ^ y; x <<= 2; y >>= 3; printf("*%llu, %llu*\\n", x, y); } ''' self.do_run(src, '*4903566027370624, 153236438355333*') code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() assert 'goog.math.Long' not in code, 'i64 precise math should not have been included if not actually used' # But if we force it to be included, it is. First, a case where we don't need it Settings.PRECISE_I64_MATH = 2 self.do_run(open(path_from_root('tests', 'hello_world.c')).read(), 'hello') code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() assert 'goog.math.Long' in code, 'i64 precise math should be included if forced' # and now one where we do self.do_run(r''' #include int main( int argc, char ** argv ) { unsigned long a = 0x60DD1695U; unsigned long b = 0xCA8C4E7BU; unsigned long long c = (unsigned long long)a * b; printf( "c = %016llx\n", c ); return 0; } ''', 'c = 4ca38a6bd2973f97') def test_i64_llabs(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') Settings.PRECISE_I64_MATH = 2 self.do_run(r''' #include #include int main(int argc, char ** argv) { printf("%lld,%lld\n", llabs(-576460752303423489), llabs(576460752303423489)); return 0; } ''', '576460752303423489,576460752303423489') def test_i64_zextneg(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include #include int main(int argc, char *argv[]) { uint8_t byte = 0x80; uint16_t two = byte; uint32_t four = byte; uint64_t eight = byte; printf("value: %d,%d,%d,%lld.\n", byte, two, four, eight); return 0; } ''' self.do_run(src, 'value: 128,128,128,128.') def test_i64_7z(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include #include uint64_t a, b; int main(int argc, char *argv[]) { a = argc; b = argv[1][0]; printf("%d,%d\n", a, b); if (a > a + b || a > a + b + 1) { printf("one %lld, %lld", a, b); return 0; } printf("zero %lld, %lld", a, b); return 0; } ''' self.do_run(src, 'zero 2, 104', ['hallo']) def test_i64_i16(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include #include int main(int argc, char ** argv){ int y=-133; int64_t x= ((int64_t)((short)(y)))*(100 + argc); if(x>0) printf(">0\n"); else printf("<=0\n"); } ''' self.do_run(src, '<=0') def test_i64_qdouble(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include typedef long long qint64; /* 64 bit signed */ typedef double qreal; int main(int argc, char **argv) { qreal c = 111; qint64 d = -111 + (argc - 1); c += d; if (c < -1 || c > 1) { printf("Failed!\n"); } else { printf("Succeeded!\n"); } }; ''' self.do_run(src, 'Succeeded!') def test_i64_varargs(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') src = r''' #include #include #include int64_t ccv_cache_generate_signature(char *msg, int len, int64_t sig_start, ...) { if (sig_start < 10123) printf("%s\n", msg+len); va_list v; va_start(v, sig_start); if (sig_start > 1413) printf("%d\n", va_arg(v, int)); else printf("nada\n"); va_end(v); return len*sig_start*(msg[0]+1); } int main(int argc, char **argv) { for (int i = 0; i < argc; i++) { int64_t x; if (i % 123123 == 0) x = ccv_cache_generate_signature(argv[i], i+2, (int64_t)argc*argc, 54.111); else x = ccv_cache_generate_signature(argv[i], i+2, (int64_t)argc*argc, 13); printf("%lld\n", x); } }; ''' self.do_run(src, '''in/this.program nada 1536 a nada 5760 fl nada 6592 sdfasdfasdf nada 7840 ''', 'waka fleefl asdfasdfasdfasdf'.split(' ')) def test_i32_mul_precise(self): if self.emcc_args == None: return self.skip('needs ta2') src = r''' #include int main(int argc, char **argv) { unsigned long d1 = 0x847c9b5d; unsigned long q = 0x549530e1; if (argc > 1000) { q += argc; d1 -= argc; } // confuse optimizer printf("%lu\n", d1*q); return 0; } ''' self.do_run(src, '3217489085') def test_i32_mul_semiprecise(self): if Settings.ASM_JS: return self.skip('asm is always fully precise') Settings.PRECISE_I32_MUL = 0 # we want semiprecise here src = r''' #include typedef unsigned int uint; // from cube2, zlib licensed #define N (624) #define M (397) #define K (0x9908B0DFU) static uint state[N]; static int next = N; void seedMT(uint seed) { state[0] = seed; for(uint i = 1; i < N; i++) // if we do not do this precisely, at least we should coerce to int immediately, not wait state[i] = seed = 1812433253U * (seed ^ (seed >> 30)) + i; next = 0; } int main() { seedMT(5497); for (int i = 0; i < 10; i++) printf("%d: %u\n", i, state[i]); return 0; } ''' self.do_run(src, '''0: 5497 1: 2916432318 2: 2502517762 3: 3151524867 4: 2323729668 5: 2053478917 6: 2409490438 7: 848473607 8: 691103752 9: 3915535113 ''') def test_i16_emcc_intrinsic(self): Settings.CORRECT_SIGNS = 1 # Relevant to this test src = r''' #include int test(unsigned short a, unsigned short b) { unsigned short result = a; result += b; if (result < b) printf("C!"); return result; } int main(void) { printf(",%d,", test(0, 0)); printf(",%d,", test(1, 1)); printf(",%d,", test(65535, 1)); printf(",%d,", test(1, 65535)); printf(",%d,", test(32768, 32767)); printf(",%d,", test(32768, 32768)); return 0; } ''' self.do_run(src, ',0,,2,C!,0,C!,0,,65535,C!,0,') def test_double_i64_conversion(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') src = r''' #include #include #include __attribute((noinline)) bool eq(double d, int64_t i) { int64_t i2 = (int64_t)d; if (i != i2) { printf("%.20g converted to int64 returns %lld, not %lld as expected!\n", d, i2, i); } return i == i2; } int main() { assert(eq(0.0, 0)); assert(eq(-0.0, 0)); assert(eq(0.1, 0)); assert(eq(-0.1, 0)); assert(eq(0.6, 0)); assert(eq(-0.6, 0)); assert(eq(1.0, 1)); assert(eq(-1.0, -1)); assert(eq(1.1, 1)); assert(eq(-1.1, -1)); assert(eq(1.6, 1)); assert(eq(-1.6, -1)); assert(eq(4294967295.0, 4294967295LL)); assert(eq(4294967295.5, 4294967295LL)); assert(eq(4294967296.0, 4294967296LL)); assert(eq(4294967296.5, 4294967296LL)); assert(eq(14294967295.0, 14294967295LL)); assert(eq(14294967295.5, 14294967295LL)); assert(eq(14294967296.0, 14294967296LL)); assert(eq(14294967296.5, 14294967296LL)); assert(eq(-4294967295.0, -4294967295LL)); assert(eq(-4294967295.5, -4294967295LL)); assert(eq(-4294967296.0, -4294967296LL)); assert(eq(-4294967296.5, -4294967296LL)); assert(eq(-14294967295.0, -14294967295LL)); assert(eq(-14294967295.5, -14294967295LL)); assert(eq(-14294967296.0, -14294967296LL)); assert(eq(-14294967296.5, -14294967296LL)); assert(eq(4294967295.3, 4294967295LL)); assert(eq(4294967296.3, 4294967296LL)); assert(eq(14294967295.3, 14294967295LL)); assert(eq(14294967296.3, 14294967296LL)); assert(eq(-4294967295.3, -4294967295LL)); assert(eq(-4294967296.3, -4294967296LL)); assert(eq(-14294967295.3, -14294967295LL)); assert(eq(-14294967296.3, -14294967296LL)); assert(eq(4294967295.8, 4294967295LL)); assert(eq(4294967296.8, 4294967296LL)); assert(eq(14294967295.8, 14294967295LL)); assert(eq(14294967296.8, 14294967296LL)); assert(eq(-4294967295.8, -4294967295LL)); assert(eq(-4294967296.8, -4294967296LL)); assert(eq(-14294967295.8, -14294967295LL)); assert(eq(-14294967296.8, -14294967296LL)); // The following number is the largest double such that all integers smaller than this can exactly be represented in a double. assert(eq(9007199254740992.0, 9007199254740992LL /* == 2^53 */)); assert(eq(-9007199254740992.0, -9007199254740992LL /* == -2^53 */)); printf("OK!\n"); return 0; } ''' self.do_run(src, 'OK!\n'); def test_float32_precise(self): Settings.PRECISE_F32 = 1 src = r''' #include int main(int argc, char **argv) { float x = 1.23456789123456789; float y = 5.20456089123406709; while (argc > 10 || argc % 19 == 15) { // confuse optimizer x /= y; y = 2*y - 1; argc--; } x = x - y; y = 3*y - x/2; x = x*y; y += 0.000000000123123123123; x -= y/7.654; printf("\n%.20f, %.20f\n", x, y); return 0; } ''' self.do_run(src, '\n-72.16590881347656250000, 17.59867858886718750000\n') def test_negative_zero(self): src = r''' #include #include int main() { #define TEST(x, y) \ printf("%.2f, %.2f ==> %.2f\n", x, y, copysign(x, y)); TEST( 5.0f, 5.0f); TEST( 5.0f, -5.0f); TEST(-5.0f, 5.0f); TEST(-5.0f, -5.0f); TEST( 5.0f, 4.0f); TEST( 5.0f, -4.0f); TEST(-5.0f, 4.0f); TEST(-5.0f, -4.0f); TEST( 0.0f, 5.0f); TEST( 0.0f, -5.0f); TEST(-0.0f, 5.0f); TEST(-0.0f, -5.0f); TEST( 5.0f, 0.0f); TEST( 5.0f, -0.0f); TEST(-5.0f, 0.0f); TEST(-5.0f, -0.0f); TEST( 0.0f, 0.0f); TEST( 0.0f, -0.0f); TEST(-0.0f, 0.0f); TEST(-0.0f, -0.0f); return 0; } ''' self.do_run(src, '''5.00, 5.00 ==> 5.00 5.00, -5.00 ==> -5.00 -5.00, 5.00 ==> 5.00 -5.00, -5.00 ==> -5.00 5.00, 4.00 ==> 5.00 5.00, -4.00 ==> -5.00 -5.00, 4.00 ==> 5.00 -5.00, -4.00 ==> -5.00 0.00, 5.00 ==> 0.00 0.00, -5.00 ==> -0.00 -0.00, 5.00 ==> 0.00 -0.00, -5.00 ==> -0.00 5.00, 0.00 ==> 5.00 5.00, -0.00 ==> -5.00 -5.00, 0.00 ==> 5.00 -5.00, -0.00 ==> -5.00 0.00, 0.00 ==> 0.00 0.00, -0.00 ==> -0.00 -0.00, 0.00 ==> 0.00 -0.00, -0.00 ==> -0.00 ''') def test_llvm_intrinsics(self): if self.emcc_args == None: return self.skip('needs ta2') Settings.PRECISE_I64_MATH = 2 # for bswap64 src = r''' #include #include extern "C" { extern unsigned short llvm_bswap_i16(unsigned short x); extern unsigned int llvm_bswap_i32(unsigned int x); extern int32_t llvm_ctlz_i32(int32_t x); extern int64_t llvm_ctlz_i64(int64_t x); extern int32_t llvm_cttz_i32(int32_t x); extern int64_t llvm_cttz_i64(int64_t x); extern int32_t llvm_ctpop_i32(int32_t x); extern int64_t llvm_ctpop_i64(int64_t x); extern int llvm_expect_i32(int x, int y); } int main(void) { unsigned short x = 0xc8ef; printf("%x,%x\n", x&0xff, x >> 8); x = llvm_bswap_i16(x); printf("%x,%x\n", x&0xff, x >> 8); unsigned int y = 0xc5de158a; printf("%x,%x,%x,%x\n", y&0xff, (y>>8)&0xff, (y>>16)&0xff, (y>>24)&0xff); y = llvm_bswap_i32(y); printf("%x,%x,%x,%x\n", y&0xff, (y>>8)&0xff, (y>>16)&0xff, (y>>24)&0xff); printf("%d,%d\n", (int)llvm_ctlz_i64(((int64_t)1) << 40), llvm_ctlz_i32(1<<10)); printf("%d,%d\n", (int)llvm_cttz_i64(((int64_t)1) << 40), llvm_cttz_i32(1<<10)); printf("%d,%d\n", (int)llvm_ctpop_i64((0x3101ULL << 32) | 1), llvm_ctpop_i32(0x3101)); printf("%d\n", (int)llvm_ctpop_i32(-594093059)); printf("%d\n", llvm_expect_i32(x % 27, 3)); int64_t a = 1; a = __builtin_bswap64(a); printf("%lld\n", a); return 0; } ''' self.do_run(src, '''ef,c8 c8,ef 8a,15,de,c5 c5,de,15,8a 23,21 40,10 5,4 22 13 72057594037927936 ''') def test_bswap64(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') src = r''' #include #include #include #include #include typedef unsigned long long quint64; using namespace std; inline quint64 qbswap(quint64 source) { return 0 | ((source & quint64(0x00000000000000ffLL)) << 56) | ((source & quint64(0x000000000000ff00LL)) << 40) | ((source & quint64(0x0000000000ff0000LL)) << 24) | ((source & quint64(0x00000000ff000000LL)) << 8) | ((source & quint64(0x000000ff00000000LL)) >> 8) | ((source & quint64(0x0000ff0000000000LL)) >> 24) | ((source & quint64(0x00ff000000000000LL)) >> 40) | ((source & quint64(0xff00000000000000LL)) >> 56); } int main() { quint64 v = strtoull("4433ffeeddccbb00", NULL, 16); printf("%lld\n", v); const string string64bitInt = "4433ffeeddccbb00"; stringstream s(string64bitInt); quint64 int64bitInt = 0; printf("1\n"); s >> hex >> int64bitInt; printf("2\n"); stringstream out; out << hex << qbswap(int64bitInt); cout << out.str() << endl; cout << hex << int64bitInt << endl; cout << string64bitInt << endl; if (out.str() != "bbccddeeff3344") { cout << "Failed!" << endl; } else { cout << "Succeeded!" << endl; } return 0; } ''' self.do_run(src, '''4914553019779824384 1 2 bbccddeeff3344 4433ffeeddccbb00 4433ffeeddccbb00 Succeeded! ''') def test_sha1(self): if self.emcc_args == None: return self.skip('needs ta2') self.do_run(open(path_from_root('tests', 'sha1.c')).read(), 'SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6') def test_cube2md5(self): if self.emcc_args == None: return self.skip('needs emcc') self.emcc_args += ['--embed-file', 'cube2md5.txt'] shutil.copyfile(path_from_root('tests', 'cube2md5.txt'), os.path.join(self.get_dir(), 'cube2md5.txt')) self.do_run(open(path_from_root('tests', 'cube2md5.cpp')).read(), open(path_from_root('tests', 'cube2md5.ok')).read()) def test_cube2hash(self): try: old_chunk_size = os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or '' os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = '1' # test splitting out each function to a chunk in emscripten.py (21 functions here) # A good test of i64 math if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2 C-style memory aliasing') self.do_run('', 'Usage: hashstring ', libraries=self.get_library('cube2hash', ['cube2hash.bc'], configure=None), includes=[path_from_root('tests', 'cube2hash')]) for text, output in [('fleefl', '892BDB6FD3F62E863D63DA55851700FDE3ACF30204798CE9'), ('fleefl2', 'AA2CC5F96FC9D540CA24FDAF1F71E2942753DB83E8A81B61'), ('64bitisslow', '64D8470573635EC354FEE7B7F87C566FCAF1EFB491041670')]: self.do_run('', 'hash value: ' + output, [text], no_build=True) finally: os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = old_chunk_size assert 'asm1' in test_modes if self.run_name == 'asm1': assert Settings.RELOOP generated = open('src.cpp.o.js').read() main = generated[generated.find('function _main'):] main = main[:main.find('\n}')] num_vars = 0 for v in re.findall('var [^;]+;', main): num_vars += v.count(',') + 1 assert num_vars == 10, 'no variable elimination should have been run, but seeing %d' % num_vars def test_unaligned(self): if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1') src = r''' #include struct S { double x; int y; }; int main() { // the 64-bit value here will not be 8-byte aligned S s0[3] = { {0x12a751f430142, 22}, {0x17a5c85bad144, 98}, {1, 1}}; char buffer[10*sizeof(S)]; int b = int(buffer); S *s = (S*)(b + 4-b%8); s[0] = s0[0]; s[1] = s0[1]; s[2] = s0[2]; printf("*%d : %d : %d\n", sizeof(S), ((unsigned int)&s[0]) % 8 != ((unsigned int)&s[1]) % 8, ((unsigned int)&s[1]) - ((unsigned int)&s[0])); s[0].x++; s[0].y++; s[1].x++; s[1].y++; printf("%.1f,%d,%.1f,%d\n", s[0].x, s[0].y, s[1].x, s[1].y); return 0; } ''' # TODO: A version of this with int64s as well if self.is_le32(): return self.skip('LLVM marks the reads of s as fully aligned, making this test invalid') else: self.do_run(src, '*12 : 1 : 12\n328157500735811.0,23,416012775903557.0,99\n') return # TODO: continue to the next part here # Test for undefined behavior in C. This is not legitimate code, but does exist if Settings.USE_TYPED_ARRAYS != 2: return self.skip('No meaning to unaligned addresses without t2') src = r''' #include int main() { int x[10]; char *p = (char*)&x[0]; p++; short *q = (short*)p; *q = 300; printf("*%d:%d*\n", *q, ((int)q)%2); int *r = (int*)p; *r = 515559; printf("*%d*\n", *r); long long *t = (long long*)p; *t = 42949672960; printf("*%Ld*\n", *t); return 0; } ''' try: self.do_run(src, '*300:1*\n*515559*\n*42949672960*\n') except Exception, e: assert 'must be aligned' in str(e), e # expected to fail without emulation def test_align64(self): src = r''' #include // inspired by poppler enum Type { A = 10, B = 20 }; struct Object { Type type; union { int intg; double real; char *name; }; }; struct Principal { double x; Object a; double y; }; int main(int argc, char **argv) { int base = argc-1; Object *o = NULL; printf("%d,%d\n", sizeof(Object), sizeof(Principal)); printf("%d,%d,%d,%d\n", (int)&o[base].type, (int)&o[base].intg, (int)&o[base].real, (int)&o[base].name); printf("%d,%d,%d,%d\n", (int)&o[base+1].type, (int)&o[base+1].intg, (int)&o[base+1].real, (int)&o[base+1].name); Principal p, q; p.x = p.y = q.x = q.y = 0; p.a.type = A; p.a.real = 123.456; *(&q.a) = p.a; printf("%.2f,%d,%.2f,%.2f : %.2f,%d,%.2f,%.2f\n", p.x, p.a.type, p.a.real, p.y, q.x, q.a.type, q.a.real, q.y); return 0; } ''' if self.is_le32(): self.do_run(src, '''16,32 0,8,8,8 16,24,24,24 0.00,10,123.46,0.00 : 0.00,10,123.46,0.00 ''') else: self.do_run(src, '''12,28 0,4,4,4 12,16,16,16 0.00,10,123.46,0.00 : 0.00,10,123.46,0.00 ''') def test_unsigned(self): Settings.CORRECT_SIGNS = 1 # We test for exactly this sort of thing here Settings.CHECK_SIGNS = 0 src = ''' #include const signed char cvals[2] = { -1, -2 }; // compiler can store this is a string, so -1 becomes \FF, and needs re-signing int main() { { unsigned char x = 200; printf("*%d*\\n", x); unsigned char y = -22; printf("*%d*\\n", y); } int varey = 100; unsigned int MAXEY = -1, MAXEY2 = -77; printf("*%u,%d,%u*\\n", MAXEY, varey >= MAXEY, MAXEY2); // 100 >= -1? not in unsigned! int y = cvals[0]; printf("*%d,%d,%d,%d*\\n", cvals[0], cvals[0] < 0, y, y < 0); y = cvals[1]; printf("*%d,%d,%d,%d*\\n", cvals[1], cvals[1] < 0, y, y < 0); // zext issue - see mathop in jsifier unsigned char x8 = -10; unsigned long hold = 0; hold += x8; int y32 = hold+50; printf("*%u,%u*\\n", hold, y32); // Comparisons x8 = 0; for (int i = 0; i < 254; i++) x8++; // make it an actual 254 in JS - not a -2 printf("*%d,%d*\\n", x8+1 == 0xff, x8+1 != 0xff); // 0xff may be '-1' in the bitcode return 0; } ''' self.do_run(src, '*4294967295,0,4294967219*\n*-1,1,-1,1*\n*-2,1,-2,1*\n*246,296*\n*1,0*') # Now let's see some code that should just work in USE_TYPED_ARRAYS == 2, but requires # corrections otherwise if Settings.USE_TYPED_ARRAYS == 2: Settings.CORRECT_SIGNS = 0 Settings.CHECK_SIGNS = 1 if not Settings.ASM_JS else 0 else: Settings.CORRECT_SIGNS = 1 Settings.CHECK_SIGNS = 0 src = ''' #include int main() { { unsigned char x; unsigned char *y = &x; *y = -1; printf("*%d*\\n", x); } { unsigned short x; unsigned short *y = &x; *y = -1; printf("*%d*\\n", x); } /*{ // This case is not checked. The hint for unsignedness is just the %u in printf, and we do not analyze that unsigned int x; unsigned int *y = &x; *y = -1; printf("*%u*\\n", x); }*/ { char x; char *y = &x; *y = 255; printf("*%d*\\n", x); } { char x; char *y = &x; *y = 65535; printf("*%d*\\n", x); } { char x; char *y = &x; *y = 0xffffffff; printf("*%d*\\n", x); } return 0; } ''' self.do_run(src, '*255*\n*65535*\n*-1*\n*-1*\n*-1*') def test_bitfields(self): if self.emcc_args is None: Settings.SAFE_HEAP = 0 # bitfields do loads on invalid areas, by design src = ''' #include struct bitty { unsigned x : 1; unsigned y : 1; unsigned z : 1; }; int main() { bitty b; printf("*"); for (int i = 0; i <= 1; i++) for (int j = 0; j <= 1; j++) for (int k = 0; k <= 1; k++) { b.x = i; b.y = j; b.z = k; printf("%d,%d,%d,", b.x, b.y, b.z); } printf("*\\n"); return 0; } ''' self.do_run(src, '*0,0,0,0,0,1,0,1,0,0,1,1,1,0,0,1,0,1,1,1,0,1,1,1,*') def test_floatvars(self): src = ''' #include // headers test, see issue #1013 #include #include int main(int argc, char **argv) { float x = 1.234, y = 3.5, q = 0.00000001; y *= 3; int z = x < y; printf("*%d,%d,%.1f,%d,%.4f,%.2f*\\n", z, int(y), y, (int)x, x, q); printf("%.2f, %.2f, %.2f, %.2f\\n", fmin(0.5, 3.3), fmin(NAN, 3.3), fmax(0.5, 3.3), fmax(NAN, 3.3)); printf("small: %.10f\\n", argc * 0.000001); /* // Rounding behavior float fs[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; double ds[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; for (int i = 0; i < 6; i++) printf("*int(%.2f)=%d,%d*\\n", fs[i], int(fs[i]), int(ds[i])); */ return 0; } ''' self.do_run(src, '*1,10,10.5,1,1.2340,0.00*\n0.50, 3.30, 3.30, 3.30\nsmall: 0.0000010000\n') def test_zerodiv(self): self.do_run(r''' #include int main(int argc, const char* argv[]) { float f1 = 1.0f; float f2 = 0.0f; float f_zero = 0.0f; float f3 = 0.0f / f2; float f4 = f2 / 0.0f; float f5 = f2 / f2; float f6 = f2 / f_zero; printf("f3: %f\n", f3); printf("f4: %f\n", f4); printf("f5: %f\n", f5); printf("f6: %f\n", f6); return 0; } ''', '''f3: nan f4: nan f5: nan f6: nan ''') def test_zero_multiplication(self): src = ''' #include int main(int argc, char * argv[]) { int one = argc; printf("%d ", 0 * one); printf("%d ", 0 * -one); printf("%d ", -one * 0); printf("%g ", 0.0 * one); printf("%g ", 0.0 * -one); printf("%g", -one * 0.0); return 0; } ''' self.do_run(src, '0 0 0 0 -0 -0') def test_isnan(self): src = r''' #include int IsNaN(double x){ int rc; /* The value return */ volatile double y = x; volatile double z = y; rc = (y!=z); return rc; } int main() { double tests[] = { 1.0, 3.333, 1.0/0.0, 0.0/0.0, -1.0/0.0, -0, 0, -123123123, 12.0E200 }; for (int i = 0; i < sizeof(tests)/sizeof(double); i++) printf("%d - %f - %d\n", i, tests[i], IsNaN(tests[i])); } ''' self.do_run(src, '''0 - 1.000000 - 0 1 - 3.333000 - 0 2 - inf - 0 3 - nan - 1 4 - -inf - 0 5 - 0.000000 - 0 6 - 0.000000 - 0 7 - -123123123.000000 - 0 8 - 1.2e+201 - 0 ''') def test_globaldoubles(self): src = r''' #include #include double testVu, testVv, testWu, testWv; void Test(double _testVu, double _testVv, double _testWu, double _testWv) { testVu = _testVu; testVv = _testVv; testWu = _testWu; testWv = _testWv; printf("BUG?\n"); printf("Display: Vu=%f Vv=%f Wu=%f Wv=%f\n", testVu, testVv, testWu, testWv); } int main(void) { double v1 = 465.1; double v2 = 465.2; double v3 = 160.3; double v4 = 111.4; Test(v1, v2, v3, v4); return 0; } ''' self.do_run(src, 'BUG?\nDisplay: Vu=465.100000 Vv=465.200000 Wu=160.300000 Wv=111.400000') def test_math(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') src = ''' #include #include #include int main(int argc, char **argv) { printf("*%.2f,%.2f,%d", M_PI, -M_PI, (1/0.0) > 1e300); // could end up as infinity, or just a very very big number printf(",%d", isfinite(NAN) != 0); printf(",%d", isfinite(INFINITY) != 0); printf(",%d", isfinite(-INFINITY) != 0); printf(",%d", isfinite(12.3) != 0); printf(",%d", isinf(NAN) != 0); printf(",%d", isinf(INFINITY) != 0); printf(",%d", isinf(-INFINITY) != 0); printf(",%d", isinf(12.3) != 0); div_t div_result = div(23, 10); printf(",%d", div_result.quot); printf(",%d", div_result.rem); double sine = -1.0, cosine = -1.0; sincos(0.0, &sine, &cosine); printf(",%1.1lf", sine); printf(",%1.1lf", cosine); float fsine = -1.0f, fcosine = -1.0f; sincosf(0.0, &fsine, &fcosine); printf(",%1.1f", fsine); printf(",%1.1f", fcosine); fsine = sinf(1.1 + argc - 1); fcosine = cosf(1.1 + argc - 1); printf(",%1.1f", fsine); printf(",%1.1f", fcosine); printf("*\\n"); return 0; } ''' self.do_run(src, '*3.14,-3.14,1,0,0,0,1,0,1,1,0,2,3,0.0,1.0,0.0,1.0,0.9,0.5*') def test_erf(self): src = ''' #include #include int main() { printf("%1.6f, %1.6f, %1.6f, %1.6f, %1.6f, %1.6f\\n", erf(1.0), erf(3.0), erf(-1.0), erfc(1.0), erfc(3.0), erfc(-1.5)); return 0; } ''' self.do_run(src, '0.842701, 0.999978, -0.842701, 0.157299, 0.000022, 1.966105') def test_math_hyperbolic(self): src = open(path_from_root('tests', 'hyperbolic', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'hyperbolic', 'output.txt'), 'r').read() self.do_run(src, expected) def test_frexp(self): src = ''' #include #include #include static const double tol=1e-16; void test_value(double value) { int exponent; double x=frexp(value, &exponent); double expected=x*pow(2.0, exponent); printf("%f=%f*2^%d\\n", value, x, exponent); assert(fabs(expected-value)=5e-1 && fabs(x)<1)); // x has a magnitude in the interval [1/2, 1) } int main() { test_value(0); test_value(100.1); test_value(-100.1); test_value(.5); test_value(-.5); test_value(1-1e-16); test_value(-(1-1e-16)); return 0; } ''' self.do_run(src, '''0.000000=0.000000*2^0 100.100000=0.782031*2^7 -100.100000=-0.782031*2^7 0.500000=0.500000*2^0 -0.500000=-0.500000*2^0 1.000000=1.000000*2^0 -1.000000=-1.000000*2^0''') def test_rounding(self): src = ''' #include #include int main() { printf("%.1f ", round(1.4)); printf("%.1f ", round(1.6)); printf("%.1f ", round(-1.4)); printf("%.1f ", round(-1.6)); printf("%.1f ", round(1.5)); printf("%.1f ", round(2.5)); printf("%.1f ", round(-1.5)); printf("%.1f ", round(-2.5)); printf("%ld ", lrint(1.4)); printf("%ld ", lrint(1.6)); printf("%ld ", lrint(-1.4)); printf("%ld ", lrint(-1.6)); printf("%ld ", lrint(1.5)); printf("%ld ", lrint(2.5)); printf("%ld ", lrint(-1.5)); printf("%ld ", lrint(-2.5)); return 0; } ''' self.do_run(src, "1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 " "1 2 -1 -2 2 2 -2 -2") # This example borrowed from MSDN documentation def test_fcvt(self): if self.emcc_args is None: return self.skip('requires emcc') src = ''' #include #include int main() { int decimal, sign; char *buffer; double source = 3.1415926535; buffer = fcvt(source, 7, &decimal, &sign); printf("source: %2.10f buffer: '%s' decimal: %d sign: %d\\n", source, buffer, decimal, sign); } ''' self.do_run(src, "source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0"); def test_llrint(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') src = r''' #include #include int main() { printf("%lld\n%lld\n%lld\n%lld\n", llrint(0.1), llrint(0.6), llrint(1.25), llrint(1099511627776.667)); return 0; } ''' self.do_run(src, '0\n1\n1\n1099511627777\n') def test_getgep(self): # Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP src = ''' #include struct { int y[10]; int z[10]; } commonblock; int main() { for (int i = 0; i < 10; ++i) { commonblock.y[i] = 1; commonblock.z[i] = 2; } printf("*%d %d*\\n", commonblock.y[0], commonblock.z[0]); return 0; } ''' self.do_run(src, '*1 2*') def test_multiply_defined_symbols(self): a1 = "int f() { return 1; }" a1_name = os.path.join(self.get_dir(), 'a1.c') open(a1_name, 'w').write(a1) a2 = "void x() {}" a2_name = os.path.join(self.get_dir(), 'a2.c') open(a2_name, 'w').write(a2) b1 = "int f() { return 2; }" b1_name = os.path.join(self.get_dir(), 'b1.c') open(b1_name, 'w').write(b1) b2 = "void y() {}" b2_name = os.path.join(self.get_dir(), 'b2.c') open(b2_name, 'w').write(b2) main = r''' #include int f(); int main() { printf("result: %d\n", f()); return 0; } ''' main_name = os.path.join(self.get_dir(), 'main.c') open(main_name, 'w').write(main) Building.emcc(a1_name) Building.emcc(a2_name) Building.emcc(b1_name) Building.emcc(b2_name) Building.emcc(main_name) liba_name = os.path.join(self.get_dir(), 'liba.a') Building.emar('cr', liba_name, [a1_name + '.o', a2_name + '.o']) libb_name = os.path.join(self.get_dir(), 'libb.a') Building.emar('cr', libb_name, [b1_name + '.o', b2_name + '.o']) all_name = os.path.join(self.get_dir(), 'all.bc') Building.link([main_name + '.o', liba_name, libb_name], all_name) self.do_ll_run(all_name, 'result: 1') def test_if(self): src = ''' #include int main() { int x = 5; if (x > 3) { printf("*yes*\\n"); } return 0; } ''' self.do_run(src, '*yes*') def test_if_else(self): src = ''' #include int main() { int x = 5; if (x > 10) { printf("*yes*\\n"); } else { printf("*no*\\n"); } return 0; } ''' self.do_run(src, '*no*') def test_loop(self): src = ''' #include int main() { int x = 5; for (int i = 0; i < 6; i++) { x += x*i; if (x > 1000) { if (x % 7 == 0) printf("cheez\\n"); x /= 2; break; } } printf("*%d*\\n", x); return 0; } ''' self.do_run(src, '*1800*') generated = open('src.cpp.o.js', 'r').read() def test_stack(self): Settings.INLINING_LIMIT = 50 src = ''' #include int test(int i) { int x = 10; if (i > 0) { return test(i-1); } return int(&x); // both for the number, and forces x to not be nativized } int main(int argc, char **argv) { // We should get the same value for the first and last - stack has unwound int x1 = test(argc - 2); int x2 = test(100); int x3 = test((argc - 2) / 4); printf("*%d,%d*\\n", x3-x1, x2 != x1); return 0; } ''' self.do_run(src, '*0,1*') def test_strings(self): src = ''' #include #include #include int main(int argc, char **argv) { int x = 5, y = 9, magic = 7; // fool compiler with magic memmove(&x, &y, magic-7); // 0 should not crash us int xx, yy, zz; char s[32]; int cc = sscanf("abc_10.b1_xyz9_543_defg", "abc_%d.%2x_xyz9_%3d_%3s", &xx, &yy, &zz, s); printf("%d:%d,%d,%d,%s\\n", cc, xx, yy, zz, s); printf("%d\\n", argc); puts(argv[1]); puts(argv[2]); printf("%d\\n", atoi(argv[3])+2); const char *foolingthecompiler = "\\rabcd"; printf("%d\\n", strlen(foolingthecompiler)); // Tests parsing /0D in llvm - should not be a 0 (end string) then a D! printf("%s\\n", NULL); // Should print '(null)', not the string at address 0, which is a real address for us! printf("/* a comment */\\n"); // Should not break the generated code! printf("// another\\n"); // Should not break the generated code! char* strdup_val = strdup("test"); printf("%s\\n", strdup_val); free(strdup_val); { char *one = "one 1 ONE !"; char *two = "two 2 TWO ?"; char three[1024]; memset(three, '.', 1024); three[50] = 0; strncpy(three + argc, one + (argc/2), argc+1); strncpy(three + argc*3, two + (argc/3), argc+2); printf("waka %s\\n", three); } { char *one = "string number one top notch"; char *two = "fa la sa ho fi FI FO FUM WHEN WHERE WHY HOW WHO"; char three[1000]; strcpy(three, &one[argc*2]); char *four = strcat(three, &two[argc*3]); printf("cat |%s|\\n", three); printf("returned |%s|\\n", four); } return 0; } ''' for named in (0, 1): print named Settings.NAMED_GLOBALS = named self.do_run(src, '''4:10,177,543,def\n4\nwowie\ntoo\n76\n5\n(null)\n/* a comment */\n// another\ntest\nwaka ....e 1 O...wo 2 T................................ cat |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO| returned |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO|''', ['wowie', 'too', '74']) if self.emcc_args == []: gen = open(self.in_dir('src.cpp.o.js')).read() assert ('var __str1;' in gen) == named def test_strcmp_uni(self): src = ''' #include #include int main() { #define TEST(func) \ { \ char *word = "WORD"; \ char wordEntry[2] = { -61,-126 }; /* "Â"; */ \ int cmp = func(word, wordEntry, 2); \ printf("Compare value " #func " is %d\\n", cmp); \ } TEST(strncmp); TEST(strncasecmp); TEST(memcmp); } ''' self.do_run(src, 'Compare value strncmp is -1\nCompare value strncasecmp is -1\nCompare value memcmp is -1\n') def test_strndup(self): src = ''' //--------------- //- http://pubs.opengroup.org/onlinepubs/9699919799/functions/strndup.html //--------------- #include #include #include int main(int argc, char **argv) { const char* source = "strndup - duplicate a specific number of bytes from a string"; char* strdup_val = strndup(source, 0); printf("1:%s\\n", strdup_val); free(strdup_val); strdup_val = strndup(source, 7); printf("2:%s\\n", strdup_val); free(strdup_val); strdup_val = strndup(source, 1000); printf("3:%s\\n", strdup_val); free(strdup_val); strdup_val = strndup(source, 60); printf("4:%s\\n", strdup_val); free(strdup_val); strdup_val = strndup(source, 19); printf("5:%s\\n", strdup_val); free(strdup_val); strdup_val = strndup(source, -1); printf("6:%s\\n", strdup_val); free(strdup_val); return 0; } ''' self.do_run(src, '1:\n2:strndup\n3:strndup - duplicate a specific number of bytes from a string\n4:strndup - duplicate a specific number of bytes from a string\n5:strndup - duplicate\n6:\n') def test_errar(self): src = r''' #include #include #include int main() { char* err; char buffer[200]; err = strerror(EDOM); strerror_r(EWOULDBLOCK, buffer, 200); printf("<%s>\n", err); printf("<%s>\n", buffer); printf("<%d>\n", strerror_r(EWOULDBLOCK, buffer, 0)); errno = 123; printf("<%d>\n", errno); return 0; } ''' expected = ''' <34> <123> ''' self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) def test_mainenv(self): src = ''' #include int main(int argc, char **argv, char **envp) { printf("*%p*\\n", envp); return 0; } ''' self.do_run(src, '*(nil)*') def test_funcs(self): src = ''' #include int funcy(int x) { return x*9; } int main() { printf("*%d,%d*\\n", funcy(8), funcy(10)); return 0; } ''' self.do_run(src, '*72,90*') def test_structs(self): src = ''' #include struct S { int x, y; }; int main() { S a, b; a.x = 5; a.y = 6; b.x = 101; b.y = 7009; S *c, *d; c = &a; c->x *= 2; c = &b; c->y -= 1; d = c; d->y += 10; printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n", a.x, a.y, b.x, b.y, c->x, c->y, d->x, d->y); return 0; } ''' self.do_run(src, '*10,6,101,7018,101,7018,101,7018*') gen_struct_src = ''' #include #include #include "emscripten.h" struct S { int x, y; }; int main() { S* a = {{gen_struct}}; a->x = 51; a->y = 62; printf("*%d,%d*\\n", a->x, a->y); {{del_struct}}(a); return 0; } ''' def test_mallocstruct(self): self.do_run(self.gen_struct_src.replace('{{gen_struct}}', '(S*)malloc(sizeof(S))').replace('{{del_struct}}', 'free'), '*51,62*') def test_newstruct(self): if self.emcc_args is None: return self.skip('requires emcc') self.do_run(self.gen_struct_src.replace('{{gen_struct}}', 'new S').replace('{{del_struct}}', 'delete'), '*51,62*') def test_addr_of_stacked(self): src = ''' #include void alter(int *y) { *y += 5; } int main() { int x = 2; alter(&x); printf("*%d*\\n", x); return 0; } ''' self.do_run(src, '*7*') def test_globals(self): src = ''' #include char cache[256], *next = cache; int main() { cache[10] = 25; next[20] = 51; printf("*%d,%d*\\n", next[10], cache[20]); return 0; } ''' self.do_run(src, '*25,51*') def test_linked_list(self): src = ''' #include struct worker_args { int value; struct worker_args *next; }; int main() { worker_args a; worker_args b; a.value = 60; a.next = &b; b.value = 900; b.next = NULL; worker_args* c = &a; int total = 0; while (c) { total += c->value; c = c->next; } // Chunk of em worker_args chunk[10]; for (int i = 0; i < 9; i++) { chunk[i].value = i*10; chunk[i].next = &chunk[i+1]; } chunk[9].value = 90; chunk[9].next = &chunk[0]; c = chunk; do { total += c->value; c = c->next; } while (c != chunk); printf("*%d,%d*\\n", total, b.next); // NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.) return 0; } ''' self.do_run(src, '*1410,0*') def test_sup(self): src = ''' #include struct S4 { int x; }; // size: 4 struct S4_2 { short x, y; }; // size: 4, but for alignment purposes, 2 struct S6 { short x, y, z; }; // size: 6 struct S6w { char x[6]; }; // size: 6 also struct S6z { int x; short y; }; // size: 8, since we align to a multiple of the biggest - 4 struct C___ { S6 a, b, c; int later; }; struct Carr { S6 a[3]; int later; }; // essentially the same, but differently defined struct C__w { S6 a; S6w b; S6 c; int later; }; // same size, different struct struct Cp1_ { int pre; short a; S6 b, c; int later; }; // fillers for a struct Cp2_ { int a; short pre; S6 b, c; int later; }; // fillers for a (get addr of the other filler) struct Cint { S6 a; int b; S6 c; int later; }; // An int (different size) for b struct C4__ { S6 a; S4 b; S6 c; int later; }; // Same size as int from before, but a struct struct C4_2 { S6 a; S4_2 b; S6 c; int later; }; // Same size as int from before, but a struct with max element size 2 struct C__z { S6 a; S6z b; S6 c; int later; }; // different size, 8 instead of 6 int main() { #define TEST(struc) \\ { \\ struc *s = 0; \\ printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a), (int)&(s->b), (int)&(s->c), (int)&(s->later), sizeof(struc)); \\ } #define TEST_ARR(struc) \\ { \\ struc *s = 0; \\ printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a[0]), (int)&(s->a[1]), (int)&(s->a[2]), (int)&(s->later), sizeof(struc)); \\ } printf("sizeofs:%d,%d\\n", sizeof(S6), sizeof(S6z)); TEST(C___); TEST_ARR(Carr); TEST(C__w); TEST(Cp1_); TEST(Cp2_); TEST(Cint); TEST(C4__); TEST(C4_2); TEST(C__z); return 0; } ''' if Settings.QUANTUM_SIZE == 1: self.do_run(src, 'sizeofs:6,8\n*C___: 0,3,6,9<24*\n*Carr: 0,3,6,9<24*\n*C__w: 0,3,9,12<24*\n*Cp1_: 1,2,5,8<24*\n*Cp2_: 0,2,5,8<24*\n*Cint: 0,3,4,7<24*\n*C4__: 0,3,4,7<24*\n*C4_2: 0,3,5,8<20*\n*C__z: 0,3,5,8<28*') else: self.do_run(src, 'sizeofs:6,8\n*C___: 0,6,12,20<24*\n*Carr: 0,6,12,20<24*\n*C__w: 0,6,12,20<24*\n*Cp1_: 4,6,12,20<24*\n*Cp2_: 0,6,12,20<24*\n*Cint: 0,8,12,20<24*\n*C4__: 0,8,12,20<24*\n*C4_2: 0,6,10,16<20*\n*C__z: 0,8,16,24<28*') def test_assert(self): src = ''' #include #include int main() { assert(1 == true); // pass assert(1 == false); // fail return 0; } ''' self.do_run(src, 'Assertion failed: 1 == false') def test_libcextra(self): if self.emcc_args is None: return self.skip('needs emcc for libcextra') src = r''' #include #include int main() { const wchar_t* wstr = L"Hello"; printf("wcslen: %d\n", wcslen(wstr)); return 0; } ''' self.do_run(src, 'wcslen: 5') def test_regex(self): # This is from http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Frtref%2Fregexec.htm if self.emcc_args is None: return self.skip('needs emcc for libcextra') src = r''' #include #include #include int main(void) { regex_t preg; const char *string = "a very simple simple simple string"; const char *pattern = "\\(sim[a-z]le\\) \\1"; int rc; size_t nmatch = 2; regmatch_t pmatch[2]; if (0 != (rc = regcomp(&preg, pattern, 0))) { printf("regcomp() failed, returning nonzero (%d)\n", rc); exit(EXIT_FAILURE); } if (0 != (rc = regexec(&preg, string, nmatch, pmatch, 0))) { printf("Failed to match '%s' with '%s',returning %d.\n", string, pattern, rc); } else { printf("With the whole expression, " "a matched substring \"%.*s\" is found at position %d to %d.\n", pmatch[0].rm_eo - pmatch[0].rm_so, &string[pmatch[0].rm_so], pmatch[0].rm_so, pmatch[0].rm_eo - 1); printf("With the sub-expression, " "a matched substring \"%.*s\" is found at position %d to %d.\n", pmatch[1].rm_eo - pmatch[1].rm_so, &string[pmatch[1].rm_so], pmatch[1].rm_so, pmatch[1].rm_eo - 1); } regfree(&preg); return 0; } ''' self.do_run(src, 'With the whole expression, a matched substring "simple simple" is found at position 7 to 19.\n' 'With the sub-expression, a matched substring "simple" is found at position 7 to 12.') def test_longjmp(self): src = r''' #include #include static jmp_buf buf; void second(void) { printf("second\n"); longjmp(buf,-1); } void first(void) { printf("first\n"); // prints longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 } int main() { volatile int x = 0; int jmpval = setjmp(buf); if (!jmpval) { x++; // should be properly restored once longjmp jumps back first(); // when executed, setjmp returns 1 printf("skipped\n"); // does not print } else if (jmpval == 1) { // when first() jumps back, setjmp returns 1 printf("result: %d %d\n", x, jmpval); // prints x++; second(); // when executed, setjmp returns -1 } else if (jmpval == -1) { // when second() jumps back, setjmp returns -1 printf("result: %d %d\n", x, jmpval); // prints } return 0; } ''' self.do_run(src, 'first\nresult: 1 1\nsecond\nresult: 2 -1') def test_longjmp2(self): src = r''' #include #include typedef struct { jmp_buf* jmp; } jmp_state; void stack_manipulate_func(jmp_state* s, int level) { jmp_buf buf; printf("Entering stack_manipulate_func, level: %d\n", level); if (level == 0) { s->jmp = &buf; if (setjmp(*(s->jmp)) == 0) { printf("Setjmp normal execution path, level: %d\n", level); stack_manipulate_func(s, level + 1); } else { printf("Setjmp error execution path, level: %d\n", level); } } else { printf("Perform longjmp at level %d\n", level); longjmp(*(s->jmp), 1); } printf("Exiting stack_manipulate_func, level: %d\n", level); } int main(int argc, char *argv[]) { jmp_state s; s.jmp = NULL; stack_manipulate_func(&s, 0); return 0; } ''' self.do_run(src, '''Entering stack_manipulate_func, level: 0 Setjmp normal execution path, level: 0 Entering stack_manipulate_func, level: 1 Perform longjmp at level 1 Setjmp error execution path, level: 0 Exiting stack_manipulate_func, level: 0 ''') def test_longjmp3(self): src = r''' #include #include typedef struct { jmp_buf* jmp; } jmp_state; void setjmp_func(jmp_state* s, int level) { jmp_buf* prev_jmp = s->jmp; jmp_buf c_jmp; if (level == 2) { printf("level is 2, perform longjmp!\n"); longjmp(*(s->jmp), 1); } if (setjmp(c_jmp) == 0) { printf("setjmp normal execution path, level: %d\n", level); s->jmp = &c_jmp; setjmp_func(s, level + 1); } else { printf("setjmp exception execution path, level: %d\n", level); if (prev_jmp) { printf("prev_jmp is not empty, continue with longjmp!\n"); s->jmp = prev_jmp; longjmp(*(s->jmp), 1); } } printf("Exiting setjmp function, level: %d\n", level); } int main(int argc, char *argv[]) { jmp_state s; s.jmp = NULL; setjmp_func(&s, 0); return 0; } ''' self.do_run(src, '''setjmp normal execution path, level: 0 setjmp normal execution path, level: 1 level is 2, perform longjmp! setjmp exception execution path, level: 1 prev_jmp is not empty, continue with longjmp! setjmp exception execution path, level: 0 Exiting setjmp function, level: 0 ''') def test_longjmp4(self): src = r''' #include #include typedef struct { jmp_buf* jmp; } jmp_state; void second_func(jmp_state* s); void first_func(jmp_state* s) { jmp_buf* prev_jmp = s->jmp; jmp_buf c_jmp; volatile int once = 0; if (setjmp(c_jmp) == 0) { printf("Normal execution path of first function!\n"); s->jmp = &c_jmp; second_func(s); } else { printf("Exception execution path of first function! %d\n", once); if (!once) { printf("Calling longjmp the second time!\n"); once = 1; longjmp(*(s->jmp), 1); } } } void second_func(jmp_state* s) { longjmp(*(s->jmp), 1); } int main(int argc, char *argv[]) { jmp_state s; s.jmp = NULL; first_func(&s); return 0; } ''' self.do_run(src, '''Normal execution path of first function! Exception execution path of first function! 0 Calling longjmp the second time! Exception execution path of first function! 1 ''') def test_longjmp_funcptr(self): src = r''' #include #include static jmp_buf buf; void (*fp)() = NULL; void second(void) { printf("second\n"); // prints longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 } void first(void) { fp(); printf("first\n"); // does not print } int main(int argc, char **argv) { fp = argc == 200 ? NULL : second; volatile int x = 0; if ( ! setjmp(buf) ) { x++; first(); // when executed, setjmp returns 0 } else { // when longjmp jumps back, setjmp returns 1 printf("main: %d\n", x); // prints } return 0; } ''' self.do_run(src, 'second\nmain: 1\n') def test_longjmp_repeat(self): Settings.MAX_SETJMPS = 1 src = r''' #include #include static jmp_buf buf; int main() { volatile int x = 0; printf("setjmp:%d\n", setjmp(buf)); x++; printf("x:%d\n", x); if (x < 4) longjmp(buf, x*2); return 0; } ''' self.do_run(src, '''setjmp:0 x:1 setjmp:2 x:2 setjmp:4 x:3 setjmp:6 x:4 ''') def test_longjmp_stacked(self): src = r''' #include #include #include #include int bottom, top; int run(int y) { // confuse stack char *s = (char*)alloca(100); memset(s, 1, 100); s[y] = y; s[y/2] = y*2; volatile int x = s[y]; top = (int)alloca(4); if (x <= 2) return x; jmp_buf buf; printf("setjmp of %d\n", x); if (setjmp(buf) == 0) { printf("going\n"); x += run(x/2); longjmp(buf, 1); } printf("back\n"); return x/2; } int main(int argc, char **argv) { int sum = 0; for (int i = 0; i < argc*2; i++) { bottom = (int)alloca(4); sum += run(10); // scorch the earth if (bottom < top) { memset((void*)bottom, 1, top - bottom); } else { memset((void*)top, 1, bottom - top); } } printf("%d\n", sum); return sum; } ''' self.do_run(src, '''setjmp of 10 going setjmp of 5 going back back setjmp of 10 going setjmp of 5 going back back 12 ''') def test_longjmp_exc(self): src = r''' #include #include #include #include jmp_buf abortframe; void dostuff(int a) { printf("pre\n"); if (a != 42) emscripten_run_script("waka_waka()"); // this should fail, and never reach "never" printf("never\n"); if (a == 100) { longjmp (abortframe, -1); } if (setjmp(abortframe)) { printf("got 100"); } } int main(int argc, char **argv) { dostuff(argc); exit(1); return 1; } ''' self.do_run(src, 'waka_waka'); def test_setjmp_many(self): src = r''' #include #include int main(int argc) { jmp_buf buf; for (int i = 0; i < NUM; i++) printf("%d\n", setjmp(buf)); if (argc-- == 1131) longjmp(buf, 11); return 0; } ''' for num in [Settings.MAX_SETJMPS, Settings.MAX_SETJMPS+1]: print num self.do_run(src.replace('NUM', str(num)), '0\n' * num if num <= Settings.MAX_SETJMPS or not Settings.ASM_JS else 'build with a higher value for MAX_SETJMPS') def test_exceptions(self): if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') Settings.EXCEPTION_DEBUG = 1 Settings.DISABLE_EXCEPTION_CATCHING = 0 if '-O2' in self.emcc_args: self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage src = ''' #include void thrower() { printf("infunc..."); throw(99); printf("FAIL"); } int main() { try { printf("*throw..."); throw(1); printf("FAIL"); } catch(...) { printf("caught!"); } try { thrower(); } catch(...) { printf("done!*\\n"); } return 0; } ''' self.do_run(src, '*throw...caught!infunc...done!*') Settings.DISABLE_EXCEPTION_CATCHING = 1 self.do_run(src, 'Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0') src = ''' #include class MyException { public: MyException(){ std::cout << "Construct..."; } MyException( const MyException & ) { std::cout << "Copy..."; } ~MyException(){ std::cout << "Destruct..."; } }; int function() { std::cout << "Throw..."; throw MyException(); } int function2() { return function(); } int main() { try { function2(); } catch (MyException & e) { std::cout << "Catched..."; } try { function2(); } catch (MyException e) { std::cout << "Catched..."; } return 0; } ''' Settings.DISABLE_EXCEPTION_CATCHING = 0 if '-O2' in self.emcc_args: self.emcc_args.pop() ; self.emcc_args.pop() # disable closure to work around a closure bug self.do_run(src, 'Throw...Construct...Catched...Destruct...Throw...Construct...Copy...Catched...Destruct...Destruct...') def test_exception_2(self): if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') Settings.DISABLE_EXCEPTION_CATCHING = 0 src = r''' #include #include typedef void (*FuncPtr)(); void ThrowException() { throw std::runtime_error("catch me!"); } FuncPtr ptr = ThrowException; int main() { try { ptr(); } catch(...) { printf("Exception caught successfully!\n"); } return 0; } ''' self.do_run(src, 'Exception caught successfully!') def test_white_list_exception(self): Settings.DISABLE_EXCEPTION_CATCHING = 2 Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"] Settings.INLINING_LIMIT = 50 # otherwise it is inlined and not identified src = ''' #include void thrower() { printf("infunc..."); throw(99); printf("FAIL"); } void somefunction() { try { thrower(); } catch(...) { printf("done!*\\n"); } } int main() { somefunction(); return 0; } ''' self.do_run(src, 'infunc...done!*') Settings.DISABLE_EXCEPTION_CATCHING = 0 Settings.EXCEPTION_CATCHING_WHITELIST = [] def test_uncaught_exception(self): if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') Settings.DISABLE_EXCEPTION_CATCHING = 0 src = r''' #include #include struct X { ~X() { printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); } }; int main() { printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); try { X x; throw 1; } catch(...) { printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); } printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); return 0; } ''' self.do_run(src, 'exception? no\nexception? yes\nexception? no\nexception? no\n') src = r''' #include #include int main() { std::ofstream os("test"); os << std::unitbuf << "foo"; // trigger a call to std::uncaught_exception from // std::basic_ostream::sentry::~sentry std::cout << "success"; } ''' self.do_run(src, 'success') def test_typed_exceptions(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read() expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read() self.do_run(src, expected) def test_multiexception(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 src = r''' #include static int current_exception_id = 0; typedef struct { int jmp; } jmp_state; void setjmp_func(jmp_state* s, int level) { int prev_jmp = s->jmp; int c_jmp; if (level == 2) { printf("level is 2, perform longjmp!\n"); throw 1; } c_jmp = current_exception_id++; try { printf("setjmp normal execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); s->jmp = c_jmp; setjmp_func(s, level + 1); } catch (int catched_eid) { printf("caught %d\n", catched_eid); if (catched_eid == c_jmp) { printf("setjmp exception execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); if (prev_jmp != -1) { printf("prev_jmp is not empty, continue with longjmp!\n"); s->jmp = prev_jmp; throw s->jmp; } } else { throw; } } printf("Exiting setjmp function, level: %d, prev_jmp: %d\n", level, prev_jmp); } int main(int argc, char *argv[]) { jmp_state s; s.jmp = -1; setjmp_func(&s, 0); return 0; } ''' self.do_run(src, '''setjmp normal execution path, level: 0, prev_jmp: -1 setjmp normal execution path, level: 1, prev_jmp: 0 level is 2, perform longjmp! caught 1 setjmp exception execution path, level: 1, prev_jmp: 0 prev_jmp is not empty, continue with longjmp! caught 0 setjmp exception execution path, level: 0, prev_jmp: -1 Exiting setjmp function, level: 0, prev_jmp: -1 ''') def test_std_exception(self): if self.emcc_args is None: return self.skip('requires emcc') Settings.DISABLE_EXCEPTION_CATCHING = 0 self.emcc_args += ['-s', 'SAFE_HEAP=0'] src = r''' #include #include int main() { std::exception e; try { throw e; } catch(std::exception e) { printf("caught std::exception\n"); } return 0; } ''' self.do_run(src, 'caught std::exception') def test_async_exit(self): open('main.c', 'w').write(r''' #include #include #include "emscripten.h" void main_loop() { exit(EXIT_SUCCESS); } int main() { emscripten_set_main_loop(main_loop, 60, 0); return 0; } ''') Popen([PYTHON, EMCC, 'main.c']).communicate() self.assertNotContained('Reached an unreachable!', run_js(self.in_dir('a.out.js'), stderr=STDOUT)) def test_exit_stack(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.ASM_JS: return self.skip('uses report_stack without exporting') Settings.INLINING_LIMIT = 50 src = r''' #include #include extern "C" { extern void report_stack(int x); } char moar() { char temp[125]; for (int i = 0; i < 125; i++) temp[i] = i*i; for (int i = 1; i < 125; i++) temp[i] += temp[i-1]/2; if (temp[100] != 99) exit(1); return temp[120]; } int main(int argc, char *argv[]) { report_stack((int)alloca(4)); printf("*%d*\n", moar()); return 0; } ''' open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' var initialStack = -1; var _report_stack = function(x) { Module.print('reported'); initialStack = x; } var Module = { postRun: function() { Module.print('Exit Status: ' + EXITSTATUS); Module.print('postRun'); assert(initialStack == STACKTOP, [initialStack, STACKTOP]); Module.print('ok.'); } }; ''') self.emcc_args += ['--pre-js', 'pre.js'] self.do_run(src, '''reported\nExit Status: 1\npostRun\nok.\n''') def test_class(self): src = ''' #include struct Random { enum { IM = 139968, IA = 3877, IC = 29573 }; Random() : last(42) {} float get( float max = 1.0f ) { last = ( last * IA + IC ) % IM; return max * last / IM; } protected: unsigned int last; } rng1; int main() { Random rng2; int count = 0; for (int i = 0; i < 100; i++) { float x1 = rng1.get(); float x2 = rng2.get(); printf("%f, %f\\n", x1, x2); if (x1 != x2) count += 1; } printf("*%d*\\n", count); return 0; } ''' self.do_run(src, '*0*') def test_inherit(self): src = ''' #include struct Parent { int x1, x2; }; struct Child : Parent { int y; }; int main() { Parent a; a.x1 = 50; a.x2 = 87; Child b; b.x1 = 78; b.x2 = 550; b.y = 101; Child* c = (Child*)&a; c->x1 ++; c = &b; c->y --; printf("*%d,%d,%d,%d,%d,%d,%d*\\n", a.x1, a.x2, b.x1, b.x2, b.y, c->x1, c->x2); return 0; } ''' self.do_run(src, '*51,87,78,550,100,78,550*') def test_isdigit_l(self): if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') src = ''' #include int main() { using namespace std; use_facet >(cout.getloc()).put(cout, cout, '0', 3.14159265); } ''' self.do_run(src, '3.14159') def test_iswdigit(self): if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') src = ''' #include #include #include int main() { using namespace std; printf("%d ", isdigit('0')); printf("%d ", iswdigit(L'0')); return 0; } ''' self.do_run(src, '1 1') def test_polymorph(self): if self.emcc_args is None: return self.skip('requires emcc') src = ''' #include struct Pure { virtual int implme() = 0; }; struct Parent : Pure { virtual int getit() { return 11; }; int implme() { return 32; } }; struct Child : Parent { int getit() { return 74; } int implme() { return 1012; } }; struct Other { int one() { return 11; } int two() { return 22; } }; int main() { Parent *x = new Parent(); Parent *y = new Child(); printf("*%d,%d,%d,%d*\\n", x->getit(), y->getit(), x->implme(), y->implme()); Other *o = new Other; int (Other::*Ls)() = &Other::one; printf("*%d*\\n", (o->*(Ls))()); Ls = &Other::two; printf("*%d*\\n", (o->*(Ls))()); return 0; } ''' self.do_run(src, '*11,74,32,1012*\n*11*\n*22*') def test_segfault(self): if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults') if Settings.ASM_JS: return self.skip('asm does not support safe heap') Settings.SAFE_HEAP = 1 for addr in ['0', 'new D2()']: print addr src = r''' #include struct Classey { virtual void doIt() = 0; }; struct D1 : Classey { virtual void doIt() { printf("fleefl\n"); } }; struct D2 : Classey { virtual void doIt() { printf("marfoosh\n"); } }; int main(int argc, char **argv) { Classey *p = argc == 100 ? new D1() : (Classey*)%s; p->doIt(); return 0; } ''' % addr self.do_run(src, 'segmentation fault' if addr.isdigit() else 'marfoosh') def test_safe_dyncalls(self): if Settings.ASM_JS: return self.skip('asm does not support missing function stack traces') if Settings.SAFE_HEAP: return self.skip('safe heap warning will appear instead') if self.emcc_args is None: return self.skip('need libc') Settings.SAFE_DYNCALLS = 1 for cond, body, work in [(True, True, False), (True, False, False), (False, True, True), (False, False, False)]: print cond, body, work src = r''' #include struct Classey { virtual void doIt() = 0; }; struct D1 : Classey { virtual void doIt() BODY; }; int main(int argc, char **argv) { Classey *p = argc COND 100 ? new D1() : NULL; printf("%p\n", p); p->doIt(); return 0; } '''.replace('COND', '==' if cond else '!=').replace('BODY', r'{ printf("all good\n"); }' if body else '') self.do_run(src, 'dyncall error: vi' if not work else 'all good') def test_dynamic_cast(self): if self.emcc_args is None: return self.skip('need libcxxabi') src = r''' #include struct Support { virtual void f() { printf("f()\n"); } }; struct Derived : Support { }; int main() { Support * p = new Derived; dynamic_cast(p)->f(); } ''' self.do_run(src, 'f()\n') def test_dynamic_cast_b(self): if self.emcc_args is None: return self.skip('need libcxxabi') src = ''' #include class CBase { virtual void dummy() {} }; class CDerived : public CBase { int a; }; class CDerivedest : public CDerived { float b; }; int main () { CBase *pa = new CBase; CBase *pb = new CDerived; CBase *pc = new CDerivedest; printf("a1: %d\\n", dynamic_cast(pa) != NULL); printf("a2: %d\\n", dynamic_cast(pa) != NULL); printf("a3: %d\\n", dynamic_cast(pa) != NULL); printf("b1: %d\\n", dynamic_cast(pb) != NULL); printf("b2: %d\\n", dynamic_cast(pb) != NULL); printf("b3: %d\\n", dynamic_cast(pb) != NULL); printf("c1: %d\\n", dynamic_cast(pc) != NULL); printf("c2: %d\\n", dynamic_cast(pc) != NULL); printf("c3: %d\\n", dynamic_cast(pc) != NULL); return 0; } ''' self.do_run(src, 'a1: 0\na2: 0\na3: 1\nb1: 0\nb2: 1\nb3: 1\nc1: 1\nc2: 1\nc3: 1\n') def test_dynamic_cast_2(self): if self.emcc_args is None: return self.skip('need libcxxabi') src = r''' #include #include class Class {}; int main() { const Class* dp = dynamic_cast(&typeid(Class)); // should return dp == NULL, printf("pointer: %p\n", dp); } ''' self.do_run(src, "pointer: (nil)") def test_funcptr(self): src = ''' #include int calc1() { return 26; } int calc2() { return 90; } typedef int (*fp_t)(); fp_t globally1 = calc1; fp_t globally2 = calc2; int nothing(const char *str) { return 0; } int main() { fp_t fp = calc1; void *vp = (void*)fp; fp_t fpb = (fp_t)vp; fp_t fp2 = calc2; void *vp2 = (void*)fp2; fp_t fpb2 = (fp_t)vp2; printf("*%d,%d,%d,%d,%d,%d*\\n", fp(), fpb(), fp2(), fpb2(), globally1(), globally2()); fp_t t = calc1; printf("*%d,%d", t == calc1, t == calc2); t = calc2; printf(",%d,%d*\\n", t == calc1, t == calc2); int (*other)(const char *str); other = nothing; other("*hello!*"); other = puts; other("*goodbye!*"); return 0; } ''' self.do_run(src, '*26,26,90,90,26,90*\n*1,0,0,1*\n*goodbye!*') def test_mathfuncptr(self): src = ''' #include #include int main(int argc, char **argv) { float (*fn)(float) = argc != 12 ? &sqrtf : &fabsf; float (*fn2)(float) = argc != 13 ? &fabsf : &sqrtf; float (*fn3)(float) = argc != 14 ? &erff : &fabsf; printf("fn2(-5) = %d, fn(10) = %.2f, erf(10) = %.2f\\n", (int)fn2(-5), fn(10), fn3(10)); return 0; } ''' self.do_run(src, 'fn2(-5) = 5, fn(10) = 3.16, erf(10) = 1.00') def test_funcptrfunc(self): src = r''' #include typedef void (*funcptr)(int, int); typedef funcptr (*funcptrfunc)(int); funcptr __attribute__ ((noinline)) getIt(int x) { return (funcptr)x; } int main(int argc, char **argv) { funcptrfunc fpf = argc < 100 ? getIt : NULL; printf("*%p*\n", fpf(argc)); return 0; } ''' self.do_run(src, '*0x1*') def test_funcptr_namecollide(self): src = r''' #include void do_call(void (*puts)(const char *), const char *str); void do_print(const char *str) { if (!str) do_call(NULL, "delusion"); if ((int)str == -1) do_print(str+10); puts("===="); puts(str); puts("===="); } void do_call(void (*puts)(const char *), const char *str) { if (!str) do_print("confusion"); if ((int)str == -1) do_call(NULL, str-10); (*puts)(str); } int main(int argc, char **argv) { for (int i = 0; i < argc; i++) { do_call(i != 10 ? do_print : NULL, i != 15 ? "waka waka" : NULL); } return 0; } ''' self.do_run(src, 'waka', force_c=True) def test_emptyclass(self): if self.emcc_args is None: return self.skip('requires emcc') src = ''' #include struct Randomized { Randomized(int x) { printf("*zzcheezzz*\\n"); } }; int main( int argc, const char *argv[] ) { new Randomized(55); return 0; } ''' self.do_run(src, '*zzcheezzz*') def test_alloca(self): src = ''' #include #include int main() { char *pc; pc = (char *)alloca(5); printf("z:%d*%d*\\n", pc > 0, (int)pc); return 0; } ''' self.do_run(src, 'z:1*', force_c=True) def test_rename(self): src = open(path_from_root('tests', 'stdio', 'test_rename.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_alloca_stack(self): if self.emcc_args is None: return # too slow in other modes # We should not blow up the stack with numerous allocas src = ''' #include #include func(int i) { char *pc = (char *)alloca(100); *pc = i; (*pc)++; return (*pc) % 10; } int main() { int total = 0; for (int i = 0; i < 1024*1024; i++) total += func(i); printf("ok:%d*\\n", total); return 0; } ''' self.do_run(src, 'ok:-32768*', force_c=True) def test_stack_byval(self): if self.emcc_args is None: return # too slow in other modes # We should also not blow up the stack with byval arguments src = r''' #include struct vec { int x, y, z; vec(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {} static vec add(vec a, vec b) { return vec(a.x+b.x, a.y+b.y, a.z+b.z); } }; int main() { int total = 0; for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { vec c(i+i%10, j*2, i%255); vec d(j*2, j%255, i%120); vec f = vec::add(c, d); total += (f.x + f.y + f.z) % 100; total %= 10240; } } printf("sum:%d*\n", total); return 0; } ''' self.do_run(src, 'sum:9780*') def test_stack_varargs(self): if self.emcc_args is None: return # too slow in other modes Settings.INLINING_LIMIT = 50 # We should not blow up the stack with numerous varargs src = r''' #include #include void func(int i) { printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i); } int main() { for (int i = 0; i < 1024; i++) func(i); printf("ok!\n"); return 0; } ''' Settings.TOTAL_STACK = 1024 self.do_run(src, 'ok!') def test_stack_varargs2(self): if self.emcc_args is None: return # too slow in other modes Settings.TOTAL_STACK = 1024 src = r''' #include #include void func(int i) { } int main() { for (int i = 0; i < 1024; i++) { printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i); } printf("ok!\n"); return 0; } ''' self.do_run(src, 'ok!') print 'with return' src = r''' #include #include int main() { for (int i = 0; i < 1024; i++) { int j = printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i); printf(" (%d)\n", j); } printf("ok!\n"); return 0; } ''' self.do_run(src, 'ok!') print 'with definitely no return' src = r''' #include #include #include void vary(const char *s, ...) { va_list v; va_start(v, s); char d[20]; vsnprintf(d, 20, s, v); puts(d); // Try it with copying va_list tempva; va_copy(tempva, v); vsnprintf(d, 20, s, tempva); puts(d); va_end(v); } int main() { for (int i = 0; i < 1024; i++) { int j = printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i); printf(" (%d)\n", j); vary("*cheez: %d+%d*", 99, 24); vary("*albeit*"); } printf("ok!\n"); return 0; } ''' self.do_run(src, 'ok!') def test_stack_void(self): Settings.INLINING_LIMIT = 50 src = r''' #include static char s[100]="aaaaa"; static int func(void) { if(s[0]!='a') return 0; printf("iso open %s\n", s, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001); return 0; } int main(){ int i; for(i=0;i<5000;i++) func(); printf(".ok.\n"); } ''' self.do_run(src, '.ok.\n') def test_life(self): if self.emcc_args is None: return self.skip('need c99') self.emcc_args += ['-std=c99'] src = open(path_from_root('tests', 'life.c'), 'r').read() self.do_run(src, '''-------------------------------- [] [] [][][] [] [] [] [][] [] [] [] [] [][] [][] [][][] [] [] [] [] [] [][] [] [] [] [][] [] [] [] [] [][][][] [][] [][] [] [][][] [] [] [] [][] [][] [][] [][][] [][] [][][] [] [] [][] [][] [] [][][] [] [][][] [] [][] [][] [][] [] [][] [][] [][] [][] [] [][] [][] [] [] [][] [] [][][] [] [] [][] [] [] [] [] [] [] [] [][][] [] [][][] [] -------------------------------- ''', ['2'], force_c=True) def test_array2(self): src = ''' #include static const double grid[4][2] = { {-3/3.,-1/3.},{+1/3.,-3/3.}, {-1/3.,+3/3.},{+3/3.,+1/3.} }; int main() { for (int i = 0; i < 4; i++) printf("%d:%.2f,%.2f ", i, grid[i][0], grid[i][1]); printf("\\n"); return 0; } ''' self.do_run(src, '0:-1.00,-0.33 1:0.33,-1.00 2:-0.33,1.00 3:1.00,0.33') def test_array2b(self): src = ''' #include static const struct { unsigned char left; unsigned char right; } prioritah[] = { {6, 6}, {6, 6}, {7, 95}, {7, 7} }; int main() { printf("*%d,%d\\n", prioritah[1].left, prioritah[1].right); printf("%d,%d*\\n", prioritah[2].left, prioritah[2].right); return 0; } ''' self.do_run(src, '*6,6\n7,95*') def test_constglobalstructs(self): src = ''' #include struct IUB { int c; double p; unsigned int pi; }; IUB iub[] = { { 'a', 0.27, 5 }, { 'c', 0.15, 4 }, { 'g', 0.12, 3 }, { 't', 0.27, 2 }, }; const unsigned char faceedgesidx[6][4] = { { 4, 5, 8, 10 }, { 6, 7, 9, 11 }, { 0, 2, 8, 9 }, { 1, 3, 10,11 }, { 0, 1, 4, 6 }, { 2, 3, 5, 7 }, }; int main( int argc, const char *argv[] ) { printf("*%d,%d,%d,%d*\\n", iub[0].c, int(iub[1].p*100), iub[2].pi, faceedgesidx[3][2]); return 0; } ''' self.do_run(src, '*97,15,3,10*') def test_conststructs(self): src = ''' #include struct IUB { int c; double p; unsigned int pi; }; int main( int argc, const char *argv[] ) { int before = 70; IUB iub[] = { { 'a', 0.3029549426680, 5 }, { 'c', 0.15, 4 }, { 'g', 0.12, 3 }, { 't', 0.27, 2 }, }; int after = 90; printf("*%d,%d,%d,%d,%d,%d*\\n", before, iub[0].c, int(iub[1].p*100), iub[2].pi, int(iub[0].p*10000), after); return 0; } ''' self.do_run(src, '*70,97,15,3,3029,90*') def test_bigarray(self): if self.emcc_args is None: return self.skip('need ta2 to compress type data on zeroinitializers') # avoid "array initializer too large" errors src = r''' #include #include #define SIZE (1024*100) struct Struct { char x; int y; }; Struct buffy[SIZE]; int main() { for (int i = 0; i < SIZE; i++) { assert(buffy[i].x == 0 && buffy[i].y == 0); } // we were zeroinitialized for (int i = 0; i < SIZE; i++) { buffy[i].x = i*i; buffy[i].y = i*i*i; } // we can save data printf("*%d*\n", buffy[SIZE/3].x); return 0; } ''' self.do_run(src, '*57*') def test_mod_globalstruct(self): src = ''' #include struct malloc_params { size_t magic, page_size; }; malloc_params mparams; #define SIZE_T_ONE ((size_t)1) #define page_align(S) (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) int main() { mparams.page_size = 4096; printf("*%d,%d,%d,%d*\\n", mparams.page_size, page_align(1000), page_align(6000), page_align(66474)); return 0; } ''' self.do_run(src, '*4096,4096,8192,69632*') def test_pystruct(self): src = ''' #include // Based on CPython code union PyGC_Head { struct { union PyGC_Head *gc_next; union PyGC_Head *gc_prev; size_t gc_refs; } gc; long double dummy; /* force worst-case alignment */ } ; struct gc_generation { PyGC_Head head; int threshold; /* collection threshold */ int count; /* count of allocations or collections of younger generations */ }; #define NUM_GENERATIONS 3 #define GEN_HEAD(n) (&generations[n].head) /* linked lists of container objects */ static struct gc_generation generations[NUM_GENERATIONS] = { /* PyGC_Head, threshold, count */ {{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0}, {{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0}, {{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0}, }; int main() { gc_generation *n = NULL; printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n", (int)(&n[0]), (int)(&n[0].head), (int)(&n[0].head.gc.gc_next), (int)(&n[0].head.gc.gc_prev), (int)(&n[0].head.gc.gc_refs), (int)(&n[0].threshold), (int)(&n[0].count), (int)(&n[1]) ); printf("*%d,%d,%d*\\n", (int)(&generations[0]) == (int)(&generations[0].head.gc.gc_next), (int)(&generations[0]) == (int)(&generations[0].head.gc.gc_prev), (int)(&generations[0]) == (int)(&generations[1]) ); int x1 = (int)(&generations[0]); int x2 = (int)(&generations[1]); printf("*%d*\\n", x1 == x2); for (int i = 0; i < NUM_GENERATIONS; i++) { PyGC_Head *list = GEN_HEAD(i); printf("%d:%d,%d\\n", i, (int)list == (int)(list->gc.gc_prev), (int)list ==(int)(list->gc.gc_next)); } printf("*%d,%d,%d*\\n", sizeof(PyGC_Head), sizeof(gc_generation), int(GEN_HEAD(2)) - int(GEN_HEAD(1))); } ''' if Settings.QUANTUM_SIZE == 1: # Compressed memory. Note that sizeof() does give the fat sizes, however! self.do_run(src, '*0,0,0,1,2,3,4,5*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,5*') else: if self.is_le32(): self.do_run(src, '*0,0,0,4,8,16,20,24*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*16,24,24*') else: self.do_run(src, '*0,0,0,4,8,12,16,20*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,20*') def test_ptrtoint(self): if self.emcc_args is None: return self.skip('requires emcc') src = ''' #include int main( int argc, const char *argv[] ) { char *a = new char[10]; char *a0 = a+0; char *a5 = a+5; int *b = new int[10]; int *b0 = b+0; int *b5 = b+5; int c = (int)b5-(int)b0; // Emscripten should warn! int d = (int)b5-(int)b0; // Emscripten should warn! printf("*%d*\\n", (int)a5-(int)a0); return 0; } ''' runner = self def check_warnings(output): runner.assertEquals(filter(lambda line: 'Warning' in line, output.split('\n')).__len__(), 4) self.do_run(src, '*5*', output_processor=check_warnings) def test_sizeof(self): if self.emcc_args is None: return self.skip('requires emcc') # Has invalid writes between printouts Settings.SAFE_HEAP = 0 src = ''' #include #include #include "emscripten.h" struct A { int x, y; }; int main( int argc, const char *argv[] ) { int *a = new int[10]; int *b = new int[1]; int *c = new int[10]; for (int i = 0; i < 10; i++) a[i] = 2; *b = 5; for (int i = 0; i < 10; i++) c[i] = 8; printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]); // Should overwrite a, but not touch b! memcpy(a, c, 10*sizeof(int)); printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]); // Part 2 A as[3] = { { 5, 12 }, { 6, 990 }, { 7, 2 } }; memcpy(&as[0], &as[2], sizeof(A)); printf("*%d,%d,%d,%d,%d,%d*\\n", as[0].x, as[0].y, as[1].x, as[1].y, as[2].x, as[2].y); return 0; } ''' self.do_run(src, '*2,2,5,8,8***8,8,5,8,8***7,2,6,990,7,2*', [], lambda x, err: x.replace('\n', '*')) def test_float_h(self): process = Popen([PYTHON, EMCC, path_from_root('tests', 'float+.c')], stdout=PIPE, stderr=PIPE) process.communicate() assert process.returncode is 0, 'float.h should agree with our system' def test_emscripten_api(self): #if Settings.MICRO_OPTS or Settings.RELOOP or Building.LLVM_OPTS: return self.skip('FIXME') src = r''' #include #include "emscripten.h" extern "C" { void save_me_aimee() { printf("mann\n"); } } int main() { // EMSCRIPTEN_COMMENT("hello from the source"); emscripten_run_script("Module.print('hello world' + '!')"); printf("*%d*\n", emscripten_run_script_int("5*20")); printf("*%s*\n", emscripten_run_script_string("'five'+'six'")); emscripten_run_script("Module['_save_me_aimee']()"); return 0; } ''' check = ''' def process(filename): src = open(filename, 'r').read() # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src ''' Settings.EXPORTED_FUNCTIONS = ['_main', '_save_me_aimee'] self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) # test EXPORT_ALL Settings.EXPORTED_FUNCTIONS = [] Settings.EXPORT_ALL = 1 self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) def test_emscripten_get_now(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') if self.run_name == 'o2': self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage self.do_run(open(path_from_root('tests', 'emscripten_get_now.cpp')).read(), 'Timer resolution is good.') def test_inlinejs(self): if not self.is_le32(): return self.skip('le32 needed for inline js') src = r''' #include double get() { double ret = 0; __asm __volatile__("Math.abs(-12/3.3)":"=r"(ret)); // write to a variable asm("#comment1"); asm volatile("#comment2"); asm volatile("#comment3\n" "#comment4\n"); return ret; } int main() { asm("Module.print('Inline JS is very cool')"); printf("%.2f\n", get()); // Test that passing multiple input and output variables works. int src1 = 1, src2 = 2, src3 = 3; int dst1 = 0, dst2 = 0, dst3 = 0; // TODO asm("Module.print(%3); Module.print(%4); Module.print(%5); %0 = %3; %1 = %4; %2 = %5;" : "=r"(dst1),"=r"(dst2),"=r"(dst3): "r"(src1),"r"(src2),"r"(src3)); // TODO printf("%d\n%d\n%d\n", dst1, dst2, dst3); return 0; } ''' self.do_run(src, 'Inline JS is very cool\n3.64\n') # TODO 1\n2\n3\n1\n2\n3\n') if self.emcc_args == []: # opts will eliminate the comments out = open('src.cpp.o.js').read() for i in range(1, 5): assert ('comment%d' % i) in out def test_inlinejs2(self): if not self.is_le32(): return self.skip('le32 needed for inline js') src = r''' #include int mix(int x, int y) { int ret; asm("Math.pow(2, %0+%1+1)" : "=r"(ret) : "r"(x), "r"(y)); // read and write return ret; } void mult() { asm("var $_$1 = Math.abs(-100); $_$1 *= 2; Module.print($_$1)"); // multiline asm __volatile__("Module.print('done')"); } int main(int argc, char **argv) { printf("%d\n", mix(argc, argc/2)); mult(); return 0; } ''' self.do_run(src, '4\n200\ndone\n') def test_inlinejs3(self): src = r''' #include #include int main() { EM_ASM(Module.print('hello dere1')); EM_ASM( Module.print('hello dere2'); ); for (int i = 0; i < 3; i++) { EM_ASM( Module.print('hello dere3'); Module.print('hello dere' + 4); ); } return 0; } ''' self.do_run(src, 'hello dere1\nhello dere2\nhello dere3\nhello dere4\nhello dere3\nhello dere4\nhello dere3\nhello dere4\n') def test_memorygrowth(self): if Settings.USE_TYPED_ARRAYS == 0: return self.skip('memory growth is only supported with typed arrays') if Settings.ASM_JS: return self.skip('asm does not support memory growth yet') # With typed arrays in particular, it is dangerous to use more memory than TOTAL_MEMORY, # since we then need to enlarge the heap(s). src = r''' #include #include #include #include #include "emscripten.h" int main(int argc, char **argv) { char *buf1 = (char*)malloc(100); char *data1 = "hello"; memcpy(buf1, data1, strlen(data1)+1); float *buf2 = (float*)malloc(100); float pie = 4.955; memcpy(buf2, &pie, sizeof(float)); printf("*pre: %s,%.3f*\n", buf1, buf2[0]); int totalMemory = emscripten_run_script_int("TOTAL_MEMORY"); char *buf3 = (char*)malloc(totalMemory+1); buf3[argc] = (int)buf2; if (argc % 7 == 6) printf("%d\n", memcpy(buf3, buf1, argc)); char *buf4 = (char*)malloc(100); float *buf5 = (float*)malloc(100); //printf("totalMemory: %d bufs: %d,%d,%d,%d,%d\n", totalMemory, buf1, buf2, buf3, buf4, buf5); assert((int)buf4 > (int)totalMemory && (int)buf5 > (int)totalMemory); printf("*%s,%.3f*\n", buf1, buf2[0]); // the old heap data should still be there memcpy(buf4, buf1, strlen(data1)+1); memcpy(buf5, buf2, sizeof(float)); printf("*%s,%.3f*\n", buf4, buf5[0]); // and the new heap space should work too return 0; } ''' # Fail without memory growth self.do_run(src, 'Cannot enlarge memory arrays.') fail = open('src.cpp.o.js').read() # Win with it Settings.ALLOW_MEMORY_GROWTH = 1 self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*') win = open('src.cpp.o.js').read() if self.emcc_args and '-O2' in self.emcc_args: # Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized) code_start = 'var TOTAL_MEMORY = ' fail = fail[fail.find(code_start):] win = win[win.find(code_start):] assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' def test_ssr(self): # struct self-ref src = ''' #include // see related things in openjpeg typedef struct opj_mqc_state { unsigned int qeval; int mps; struct opj_mqc_state *nmps; struct opj_mqc_state *nlps; } opj_mqc_state_t; static opj_mqc_state_t mqc_states[2] = { {0x5600, 0, &mqc_states[2], &mqc_states[3]}, {0x5602, 1, &mqc_states[3], &mqc_states[2]}, }; int main() { printf("*%d*\\n", (int)(mqc_states+1)-(int)mqc_states); for (int i = 0; i < 2; i++) printf("%d:%d,%d,%d,%d\\n", i, mqc_states[i].qeval, mqc_states[i].mps, (int)mqc_states[i].nmps-(int)mqc_states, (int)mqc_states[i].nlps-(int)mqc_states); return 0; } ''' if Settings.QUANTUM_SIZE == 1: self.do_run(src, '''*4*\n0:22016,0,8,12\n1:22018,1,12,8\n''') else: self.do_run(src, '''*16*\n0:22016,0,32,48\n1:22018,1,48,32\n''') def test_tinyfuncstr(self): if self.emcc_args is None: return self.skip('requires emcc') src = ''' #include struct Class { static char *name1() { return "nameA"; } char *name2() { return "nameB"; } }; int main() { printf("*%s,%s*\\n", Class::name1(), (new Class())->name2()); return 0; } ''' self.do_run(src, '*nameA,nameB*') def test_llvmswitch(self): Settings.CORRECT_SIGNS = 1 src = ''' #include #include int switcher(int p) { switch(p) { case 'a': case 'b': case 'c': return p-1; case -15: return p+1; } return p; } int main( int argc, const char *argv[] ) { unsigned int x = 0xfffffff1; x >>= (argc-1); // force it to be unsigned for purpose of checking our switch comparison in signed/unsigned printf("*%d,%d,%d,%d,%d,%d*\\n", switcher('a'), switcher('b'), switcher('c'), switcher(x), switcher(-15), switcher('e')); return 0; } ''' self.do_run(src, '*96,97,98,-14,-14,101*') # By default, when user has not specified a -std flag, Emscripten should always build .cpp files using the C++03 standard, # i.e. as if "-std=c++03" had been passed on the command line. On Linux with Clang 3.2 this is the case, but on Windows # with Clang 3.2 -std=c++11 has been chosen as default, because of # < jrose> clb: it's deliberate, with the idea that for people who don't care about the standard, they should be using the "best" thing we can offer on that platform def test_cxx03_do_run(self): src = ''' #include #if __cplusplus != 199711L #error By default, if no -std is specified, emscripten should be compiling with -std=c++03! #endif int main( int argc, const char *argv[] ) { printf("Hello world!\\n"); return 0; } ''' self.do_run(src, 'Hello world!') def test_bigswitch(self): if self.run_name != 'default': return self.skip('TODO: issue #781') src = open(path_from_root('tests', 'bigswitch.cpp')).read() self.do_run(src, '''34962: GL_ARRAY_BUFFER (0x8892) 26214: what? 35040: GL_STREAM_DRAW (0x88E0) ''', args=['34962', '26214', '35040']) def test_indirectbr(self): Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) src = ''' #include int main(void) { const void *addrs[2] = { &&FOO, &&BAR }; // confuse the optimizer so it doesn't hardcode the jump and avoid generating an |indirectbr| instruction int which = 0; for (int x = 0; x < 1000; x++) which = (which + x*x) % 7; which = (which % 2) + 1; goto *addrs[which]; FOO: printf("bad\\n"); return 0; BAR: printf("good\\n"); const void *addr = &&FOO; goto *addr; } ''' self.do_run(src, 'good\nbad') def test_indirectbr_many(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('blockaddr > 255 requires ta2') blocks = range(1500) init = ', '.join(['&&B%d' % b for b in blocks]) defs = '\n'.join(['B%d: printf("%d\\n"); return 0;' % (b,b) for b in blocks]) src = ''' #include int main(int argc, char **argv) { printf("\\n"); const void *addrs[] = { %s }; goto *addrs[argc*argc + 1000]; %s return 0; } ''' % (init, defs) self.do_run(src, '\n1001\n') def test_pack(self): src = ''' #include #include #pragma pack(push,1) typedef struct header { unsigned char id; unsigned short colour; unsigned char desc; } header; #pragma pack(pop) typedef struct fatheader { unsigned char id; unsigned short colour; unsigned char desc; } fatheader; int main( int argc, const char *argv[] ) { header h, *ph = 0; fatheader fh, *pfh = 0; printf("*%d,%d,%d*\\n", sizeof(header), (int)((int)&h.desc - (int)&h.id), (int)(&ph[1])-(int)(&ph[0])); printf("*%d,%d,%d*\\n", sizeof(fatheader), (int)((int)&fh.desc - (int)&fh.id), (int)(&pfh[1])-(int)(&pfh[0])); return 0; } ''' if Settings.QUANTUM_SIZE == 1: self.do_run(src, '*4,2,3*\n*6,2,3*') else: self.do_run(src, '*4,3,4*\n*6,4,6*') def test_varargs(self): if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this') if not self.is_le32(): return self.skip('we do not support all varargs stuff without le32') src = ''' #include #include void vary(const char *s, ...) { va_list v; va_start(v, s); char d[20]; vsnprintf(d, 20, s, v); puts(d); // Try it with copying va_list tempva; va_copy(tempva, v); vsnprintf(d, 20, s, tempva); puts(d); va_end(v); } void vary2(char color, const char *s, ...) { va_list v; va_start(v, s); char d[21]; d[0] = color; vsnprintf(d+1, 20, s, v); puts(d); va_end(v); } void varargs_listoffsets_list_evaluate(int count, va_list ap, int vaIteration) { while(count > 0) { const char* string = va_arg(ap, const char*); printf("%s", string); count--; } printf("\\n"); } void varags_listoffsets_list_copy(int count, va_list ap, int iteration) { va_list ap_copy; va_copy(ap_copy, ap); varargs_listoffsets_list_evaluate(count, ap_copy, iteration); va_end(ap_copy); } void varargs_listoffsets_args(int type, int count, ...) { va_list ap; va_start(ap, count); // evaluate a copied list varags_listoffsets_list_copy(count, ap, 1); varags_listoffsets_list_copy(count, ap, 2); varags_listoffsets_list_copy(count, ap, 3); varags_listoffsets_list_copy(count, ap, 4); varargs_listoffsets_list_evaluate(count, ap, 1); // NOTE: we expect this test to fail, so we will check the stdout for ..... varargs_listoffsets_list_evaluate(count, ap, 2); // NOTE: this test has to work again, as we restart the list va_end(ap); va_start(ap, count); varargs_listoffsets_list_evaluate(count, ap, 3); va_end(ap); } void varargs_listoffsets_main() { varargs_listoffsets_args(0, 5, "abc", "def", "ghi", "jkl", "mno", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""); } #define GETMAX(pref, type) \ type getMax##pref(int num, ...) \ { \ va_list vv; \ va_start(vv, num); \ type maxx = va_arg(vv, type); \ for (int i = 1; i < num; i++) \ { \ type curr = va_arg(vv, type); \ maxx = curr > maxx ? curr : maxx; \ } \ va_end(vv); \ return maxx; \ } GETMAX(i, int); GETMAX(D, double); int main(int argc, char **argv) { vary("*cheez: %d+%d*", 0, 24); // Also tests that '0' is not special as an array ender vary("*albeit*"); // Should not fail with no var args in vararg function vary2('Q', "%d*", 85); int maxxi = getMaxi(6, 2, 5, 21, 4, -10, 19); printf("maxxi:%d*\\n", maxxi); double maxxD = getMaxD(6, (double)2.1, (double)5.1, (double)22.1, (double)4.1, (double)-10.1, (double)19.1, (double)2); printf("maxxD:%.2f*\\n", (float)maxxD); // And, as a function pointer void (*vfp)(const char *s, ...) = argc == 1211 ? NULL : vary; vfp("*vfp:%d,%d*", 22, 199); // ensure lists work properly when copied, reinited etc. varargs_listoffsets_main(); return 0; } ''' self.do_run(src, '*cheez: 0+24*\n*cheez: 0+24*\n*albeit*\n*albeit*\nQ85*\nmaxxi:21*\nmaxxD:22.10*\n*vfp:22,199*\n*vfp:22,199*\n'+ 'abcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\n\nabcdefghijklmno\n') def test_varargs_byval(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('FIXME: Add support for this') if self.is_le32(): return self.skip('clang cannot compile this code with that target yet') src = r''' #include #include typedef struct type_a { union { double f; void *p; int i; short sym; } value; } type_a; enum mrb_vtype { MRB_TT_FALSE = 0, /* 0 */ MRB_TT_CLASS = 9 /* 9 */ }; typedef struct type_b { enum mrb_vtype tt:8; } type_b; void print_type_a(int argc, ...); void print_type_b(int argc, ...); int main(int argc, char *argv[]) { type_a a; type_b b; a.value.p = (void*) 0x12345678; b.tt = MRB_TT_CLASS; printf("The original address of a is: %p\n", a.value.p); printf("The original type of b is: %d\n", b.tt); print_type_a(1, a); print_type_b(1, b); return 0; } void print_type_a(int argc, ...) { va_list ap; type_a a; va_start(ap, argc); a = va_arg(ap, type_a); va_end(ap); printf("The current address of a is: %p\n", a.value.p); } void print_type_b(int argc, ...) { va_list ap; type_b b; va_start(ap, argc); b = va_arg(ap, type_b); va_end(ap); printf("The current type of b is: %d\n", b.tt); } ''' self.do_run(src, '''The original address of a is: 0x12345678 The original type of b is: 9 The current address of a is: 0x12345678 The current type of b is: 9 ''') def test_functionpointer_libfunc_varargs(self): src = r''' #include #include typedef int (*fp_t)(int, int, ...); int main(int argc, char **argv) { fp_t fp = &fcntl; if (argc == 1337) fp = (fp_t)&main; (*fp)(0, 10); (*fp)(0, 10, 5); printf("waka\n"); return 0; } ''' self.do_run(src, '''waka''') def test_structbyval(self): Settings.INLINING_LIMIT = 50 # part 1: make sure that normally, passing structs by value works src = r''' #include struct point { int x, y; }; void dump(struct point p) { p.x++; // should not modify p.y++; // anything in the caller! printf("dump: %d,%d\n", p.x, p.y); } void dumpmod(struct point *p) { p->x++; // should not modify p->y++; // anything in the caller! printf("dump: %d,%d\n", p->x, p->y); } int main( int argc, const char *argv[] ) { point p = { 54, 2 }; printf("pre: %d,%d\n", p.x, p.y); dump(p); void (*dp)(point p) = dump; // And, as a function pointer dp(p); printf("post: %d,%d\n", p.x, p.y); dumpmod(&p); dumpmod(&p); printf("last: %d,%d\n", p.x, p.y); return 0; } ''' self.do_run(src, 'pre: 54,2\ndump: 55,3\ndump: 55,3\npost: 54,2\ndump: 55,3\ndump: 56,4\nlast: 56,4') # Check for lack of warning in the generated code (they should appear in part 2) generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() assert 'Casting a function pointer type to another with a different number of arguments.' not in generated, 'Unexpected warning' # part 2: make sure we warn about mixing c and c++ calling conventions here if not (self.emcc_args is None or self.emcc_args == []): return # Optimized code is missing the warning comments header = r''' struct point { int x, y; }; ''' open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header) supp = r''' #include #include "header.h" void dump(struct point p) { p.x++; // should not modify p.y++; // anything in the caller! printf("dump: %d,%d\n", p.x, p.y); } ''' supp_name = os.path.join(self.get_dir(), 'supp.c') open(supp_name, 'w').write(supp) main = r''' #include #include "header.h" #ifdef __cplusplus extern "C" { #endif void dump(struct point p); #ifdef __cplusplus } #endif int main( int argc, const char *argv[] ) { struct point p = { 54, 2 }; printf("pre: %d,%d\n", p.x, p.y); dump(p); void (*dp)(struct point p) = dump; // And, as a function pointer dp(p); printf("post: %d,%d\n", p.x, p.y); return 0; } ''' main_name = os.path.join(self.get_dir(), 'main.cpp') open(main_name, 'w').write(main) Building.emcc(supp_name) Building.emcc(main_name) all_name = os.path.join(self.get_dir(), 'all.bc') Building.link([supp_name + '.o', main_name + '.o'], all_name) # This will fail! See explanation near the warning we check for, in the compiler source code output = Popen([PYTHON, EMCC, all_name], stderr=PIPE).communicate() # Check for warning in the generated code generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() if 'i386-pc-linux-gnu' in COMPILER_OPTS: assert 'Casting a function pointer type to a potentially incompatible one' in output[1], 'Missing expected warning' else: print >> sys.stderr, 'skipping C/C++ conventions warning check, since not i386-pc-linux-gnu' def test_stdlibs(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.USE_TYPED_ARRAYS == 2: # Typed arrays = 2 + safe heap prints a warning that messes up our output. Settings.SAFE_HEAP = 0 src = ''' #include #include #include void clean() { printf("*cleaned*\\n"); } int comparer(const void *a, const void *b) { int aa = *((int*)a); int bb = *((int*)b); return aa - bb; } int main() { // timeofday timeval t; gettimeofday(&t, NULL); printf("*%d,%d\\n", int(t.tv_sec), int(t.tv_usec)); // should not crash // atexit atexit(clean); // qsort int values[6] = { 3, 2, 5, 1, 5, 6 }; qsort(values, 5, sizeof(int), comparer); printf("*%d,%d,%d,%d,%d,%d*\\n", values[0], values[1], values[2], values[3], values[4], values[5]); printf("*stdin==0:%d*\\n", stdin == 0); // check that external values are at least not NULL printf("*%%*\\n"); printf("*%.1ld*\\n", 5); printf("*%.1f*\\n", strtod("66", NULL)); // checks dependency system, as our strtod needs _isspace etc. printf("*%ld*\\n", strtol("10", NULL, 0)); printf("*%ld*\\n", strtol("0", NULL, 0)); printf("*%ld*\\n", strtol("-10", NULL, 0)); printf("*%ld*\\n", strtol("12", NULL, 16)); printf("*%lu*\\n", strtoul("10", NULL, 0)); printf("*%lu*\\n", strtoul("0", NULL, 0)); printf("*%lu*\\n", strtoul("-10", NULL, 0)); printf("*malloc(0)!=0:%d*\\n", malloc(0) != 0); // We should not fail horribly return 0; } ''' self.do_run(src, '*1,2,3,5,5,6*\n*stdin==0:0*\n*%*\n*5*\n*66.0*\n*10*\n*0*\n*-10*\n*18*\n*10*\n*0*\n*4294967286*\n*malloc(0)!=0:1*\n*cleaned*') src = r''' #include #include int main() { bool x = true; bool y = false; printf("*%d*\n", x != y); return 0; } ''' self.do_run(src, '*1*', force_c=True) def test_strtoll_hex(self): if self.emcc_args is None: return self.skip('requires emcc') # tests strtoll for hex strings (0x...) src = r''' #include #include int main() { const char *STRING = "0x4 -0x3A +0xDEADBEEF"; char *end_char; // undefined base long long int l1 = strtoll(STRING, &end_char, 0); long long int l2 = strtoll(end_char, &end_char, 0); long long int l3 = strtoll(end_char, NULL, 0); // defined base long long int l4 = strtoll(STRING, &end_char, 16); long long int l5 = strtoll(end_char, &end_char, 16); long long int l6 = strtoll(end_char, NULL, 16); printf("%d%d%d%d%d%d\n", l1==0x4, l2==-0x3a, l3==0xdeadbeef, l4==0x4, l5==-0x3a, l6==0xdeadbeef); return 0; } ''' self.do_run(src, '111111') def test_strtoll_dec(self): if self.emcc_args is None: return self.skip('requires emcc') # tests strtoll for decimal strings (0x...) src = r''' #include #include int main() { const char *STRING = "4 -38 +4711"; char *end_char; // undefined base long long int l1 = strtoll(STRING, &end_char, 0); long long int l2 = strtoll(end_char, &end_char, 0); long long int l3 = strtoll(end_char, NULL, 0); // defined base long long int l4 = strtoll(STRING, &end_char, 10); long long int l5 = strtoll(end_char, &end_char, 10); long long int l6 = strtoll(end_char, NULL, 10); printf("%d%d%d%d%d%d\n", l1==4, l2==-38, l3==4711, l4==4, l5==-38, l6==4711); return 0; } ''' self.do_run(src, '111111') def test_strtoll_bin(self): if self.emcc_args is None: return self.skip('requires emcc') # tests strtoll for binary strings (0x...) src = r''' #include #include int main() { const char *STRING = "1 -101 +1011"; char *end_char; // defined base long long int l4 = strtoll(STRING, &end_char, 2); long long int l5 = strtoll(end_char, &end_char, 2); long long int l6 = strtoll(end_char, NULL, 2); printf("%d%d%d\n", l4==1, l5==-5, l6==11); return 0; } ''' self.do_run(src, '111') def test_strtoll_oct(self): if self.emcc_args is None: return self.skip('requires emcc') # tests strtoll for decimal strings (0x...) src = r''' #include #include int main() { const char *STRING = "0 -035 +04711"; char *end_char; // undefined base long long int l1 = strtoll(STRING, &end_char, 0); long long int l2 = strtoll(end_char, &end_char, 0); long long int l3 = strtoll(end_char, NULL, 0); // defined base long long int l4 = strtoll(STRING, &end_char, 8); long long int l5 = strtoll(end_char, &end_char, 8); long long int l6 = strtoll(end_char, NULL, 8); printf("%d%d%d%d%d%d\n", l1==0, l2==-29, l3==2505, l4==0, l5==-29, l6==2505); return 0; } ''' self.do_run(src, '111111') def test_strtol_hex(self): # tests strtoll for hex strings (0x...) src = r''' #include #include int main() { const char *STRING = "0x4 -0x3A +0xDEAD"; char *end_char; // undefined base long l1 = strtol(STRING, &end_char, 0); long l2 = strtol(end_char, &end_char, 0); long l3 = strtol(end_char, NULL, 0); // defined base long l4 = strtol(STRING, &end_char, 16); long l5 = strtol(end_char, &end_char, 16); long l6 = strtol(end_char, NULL, 16); printf("%d%d%d%d%d%d\n", l1==0x4, l2==-0x3a, l3==0xdead, l4==0x4, l5==-0x3a, l6==0xdead); return 0; } ''' self.do_run(src, '111111') def test_strtol_dec(self): # tests strtoll for decimal strings (0x...) src = r''' #include #include int main() { const char *STRING = "4 -38 +4711"; char *end_char; // undefined base long l1 = strtol(STRING, &end_char, 0); long l2 = strtol(end_char, &end_char, 0); long l3 = strtol(end_char, NULL, 0); // defined base long l4 = strtol(STRING, &end_char, 10); long l5 = strtol(end_char, &end_char, 10); long l6 = strtol(end_char, NULL, 10); printf("%d%d%d%d%d%d\n", l1==4, l2==-38, l3==4711, l4==4, l5==-38, l6==4711); return 0; } ''' self.do_run(src, '111111') def test_strtol_bin(self): # tests strtoll for binary strings (0x...) src = r''' #include #include int main() { const char *STRING = "1 -101 +1011"; char *end_char; // defined base long l4 = strtol(STRING, &end_char, 2); long l5 = strtol(end_char, &end_char, 2); long l6 = strtol(end_char, NULL, 2); printf("%d%d%d\n", l4==1, l5==-5, l6==11); return 0; } ''' self.do_run(src, '111') def test_strtol_oct(self): # tests strtoll for decimal strings (0x...) src = r''' #include #include int main() { const char *STRING = "0 -035 +04711"; char *end_char; // undefined base long l1 = strtol(STRING, &end_char, 0); long l2 = strtol(end_char, &end_char, 0); long l3 = strtol(end_char, NULL, 0); // defined base long l4 = strtol(STRING, &end_char, 8); long l5 = strtol(end_char, &end_char, 8); long l6 = strtol(end_char, NULL, 8); printf("%d%d%d%d%d%d\n", l1==0, l2==-29, l3==2505, l4==0, l5==-29, l6==2505); return 0; } ''' self.do_run(src, '111111') def test_atexit(self): # Confirms they are called in reverse order src = r''' #include #include static void cleanA() { printf("A"); } static void cleanB() { printf("B"); } int main() { atexit(cleanA); atexit(cleanB); return 0; } ''' self.do_run(src, 'BA') def test_pthread_specific(self): if self.emcc_args is None: return self.skip('requires emcc') src = open(path_from_root('tests', 'pthread', 'specific.c'), 'r').read() expected = open(path_from_root('tests', 'pthread', 'specific.c.txt'), 'r').read() self.do_run(src, expected, force_c=True) def test_tcgetattr(self): src = open(path_from_root('tests', 'termios', 'test_tcgetattr.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_time(self): # XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again. src = open(path_from_root('tests', 'time', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'time', 'output.txt'), 'r').read() expected2 = open(path_from_root('tests', 'time', 'output2.txt'), 'r').read() self.do_run(src, [expected, expected2], extra_emscripten_args=['-H', 'libc/time.h']) #extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) def test_timeb(self): # Confirms they are called in reverse order src = r''' #include #include #include int main() { timeb tb; tb.timezone = 1; printf("*%d\n", ftime(&tb)); assert(tb.time > 10000); assert(tb.timezone == 0); assert(tb.dstflag == 0); return 0; } ''' self.do_run(src, '*0\n') def test_time_c(self): src = r''' #include #include int main() { time_t t = time(0); printf("time: %s\n", ctime(&t)); } ''' self.do_run(src, 'time: ') # compilation check, mainly def test_gmtime(self): src = r''' #include #include #include #include int main(void) { time_t t=time(NULL); struct tm *ptm=gmtime(&t); struct tm tmCurrent=*ptm; int hour=tmCurrent.tm_hour; t-=hour*3600; // back to midnight int yday = -1; for(hour=0;hour<24;hour++) { ptm=gmtime(&t); // tm_yday must be constant all day... printf("yday: %d, hour: %d\n", ptm->tm_yday, hour); if (yday == -1) yday = ptm->tm_yday; else assert(yday == ptm->tm_yday); t+=3600; // add one hour } printf("ok!\n"); return(0); } ''' self.do_run(src, '''ok!''') def test_strptime_tm(self): src=r''' #include #include #include int main() { struct tm tm; char *ptr = strptime("17410105012000", "%H%M%S%d%m%Y", &tm); printf("%s: %s, %d/%d/%d %d:%d:%d", (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", tm.tm_wday == 0 ? "Sun" : (tm.tm_wday == 1 ? "Mon" : (tm.tm_wday == 2 ? "Tue" : (tm.tm_wday == 3 ? "Wed" : (tm.tm_wday == 4 ? "Thu" : (tm.tm_wday == 5 ? "Fri" : (tm.tm_wday == 6 ? "Sat" : "ERR")))))), tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec ); } ''' self.do_run(src, 'OK: Wed, 1/5/2000 17:41:1') def test_strptime_days(self): src = r''' #include #include #include static const struct { const char *input; const char *format; } day_tests[] = { { "2000-01-01", "%Y-%m-%d"}, { "03/03/00", "%D"}, { "9/9/99", "%x"}, { "19990502123412", "%Y%m%d%H%M%S"}, { "2001 20 Mon", "%Y %U %a"}, { "2006 4 Fri", "%Y %U %a"}, { "2001 21 Mon", "%Y %W %a"}, { "2013 29 Wed", "%Y %W %a"}, { "2000-01-01 08:12:21 AM", "%Y-%m-%d %I:%M:%S %p"}, { "2000-01-01 08:12:21 PM", "%Y-%m-%d %I:%M:%S %p"}, { "2001 17 Tue", "%Y %U %a"}, { "2001 8 Thursday", "%Y %W %a"}, }; int main() { struct tm tm; for (int i = 0; i < sizeof (day_tests) / sizeof (day_tests[0]); ++i) { memset (&tm, '\0', sizeof (tm)); char *ptr = strptime(day_tests[i].input, day_tests[i].format, &tm); printf("%s: %d/%d/%d (%dth DoW, %dth DoY)\n", (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", tm.tm_mon+1, tm.tm_mday, 1900+tm.tm_year, tm.tm_wday, tm.tm_yday); } } ''' self.do_run(src, 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ 'OK: 3/3/2000 (5th DoW, 62th DoY)\n'\ 'OK: 9/9/1999 (4th DoW, 251th DoY)\n'\ 'OK: 5/2/1999 (0th DoW, 121th DoY)\n'\ 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\ 'OK: 1/27/2006 (5th DoW, 26th DoY)\n'\ 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\ 'OK: 7/24/2013 (3th DoW, 204th DoY)\n'\ 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ 'OK: 5/1/2001 (2th DoW, 120th DoY)\n'\ 'OK: 2/22/2001 (4th DoW, 52th DoY)\n'\ ) def test_strptime_reentrant(self): src=r''' #include #include #include #include int main () { int result = 0; struct tm tm; memset (&tm, 0xaa, sizeof (tm)); /* Test we don't crash on uninitialized struct tm. Some fields might contain bogus values until everything needed is initialized, but we shouldn't crash. */ if (strptime ("2007", "%Y", &tm) == NULL || strptime ("12", "%d", &tm) == NULL || strptime ("Feb", "%b", &tm) == NULL || strptime ("13", "%M", &tm) == NULL || strptime ("21", "%S", &tm) == NULL || strptime ("16", "%H", &tm) == NULL) { printf("ERR: returned NULL"); exit(EXIT_FAILURE); } if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 || tm.tm_mday != 12 || tm.tm_mon != 1 || tm.tm_year != 107 || tm.tm_wday != 1 || tm.tm_yday != 42) { printf("ERR: unexpected tm content (1) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); exit(EXIT_FAILURE); } if (strptime ("8", "%d", &tm) == NULL) { printf("ERR: strptime failed"); exit(EXIT_FAILURE); } if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 || tm.tm_mday != 8 || tm.tm_mon != 1 || tm.tm_year != 107 || tm.tm_wday != 4 || tm.tm_yday != 38) { printf("ERR: unexpected tm content (2) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); exit(EXIT_FAILURE); } printf("OK"); } ''' self.do_run(src, 'OK') def test_strftime(self): src=r''' #include #include #include #include void test(int result, const char* comment, const char* parsed = "") { printf("%d",result); if (!result) { printf("\nERROR: %s (\"%s\")\n", comment, parsed); } } int cmp(const char *s1, const char *s2) { for ( ; *s1 == *s2 ; s1++,s2++ ) { if ( *s1 == '\0' ) break; } return (*s1 - *s2); } int main() { struct tm tm; char s[1000]; size_t size; tm.tm_sec = 4; tm.tm_min = 23; tm.tm_hour = 20; tm.tm_mday = 21; tm.tm_mon = 1; tm.tm_year = 74; tm.tm_wday = 4; tm.tm_yday = 51; tm.tm_isdst = 0; size = strftime(s, 1000, "", &tm); test((size==0) && (*s=='\0'), "strftime test #1", s); size = strftime(s, 1000, "%a", &tm); test((size==3) && !cmp(s, "Thu"), "strftime test #2", s); size = strftime(s, 1000, "%A", &tm); test((size==8) && !cmp(s, "Thursday"), "strftime test #3", s); size = strftime(s, 1000, "%b", &tm); test((size==3) && !cmp(s, "Feb"), "strftime test #4", s); size = strftime(s, 1000, "%B", &tm); test((size==8) && !cmp(s, "February"), "strftime test #5", s); size = strftime(s, 1000, "%d", &tm); test((size==2) && !cmp(s, "21"), "strftime test #6", s); size = strftime(s, 1000, "%H", &tm); test((size==2) && !cmp(s, "20"), "strftime test #7", s); size = strftime(s, 1000, "%I", &tm); test((size==2) && !cmp(s, "08"), "strftime test #8", s); size = strftime(s, 1000, "%j", &tm); test((size==3) && !cmp(s, "052"), "strftime test #9", s); size = strftime(s, 1000, "%m", &tm); test((size==2) && !cmp(s, "02"), "strftime test #10", s); size = strftime(s, 1000, "%M", &tm); test((size==2) && !cmp(s, "23"), "strftime test #11", s); size = strftime(s, 1000, "%p", &tm); test((size==2) && !cmp(s, "PM"), "strftime test #12", s); size = strftime(s, 1000, "%S", &tm); test((size==2) && !cmp(s, "04"), "strftime test #13", s); size = strftime(s, 1000, "%U", &tm); test((size==2) && !cmp(s, "07"), "strftime test #14", s); size = strftime(s, 1000, "%w", &tm); test((size==1) && !cmp(s, "4"), "strftime test #15", s); size = strftime(s, 1000, "%W", &tm); test((size==2) && !cmp(s, "07"), "strftime test #16", s); size = strftime(s, 1000, "%y", &tm); test((size==2) && !cmp(s, "74"), "strftime test #17", s); size = strftime(s, 1000, "%Y", &tm); test((size==4) && !cmp(s, "1974"), "strftime test #18", s); size = strftime(s, 1000, "%%", &tm); test((size==1) && !cmp(s, "%"), "strftime test #19", s); size = strftime(s, 5, "%Y", &tm); test((size==4) && !cmp(s, "1974"), "strftime test #20", s); size = strftime(s, 4, "%Y", &tm); test((size==0), "strftime test #21", s); tm.tm_mon = 0; tm.tm_mday = 1; size = strftime(s, 10, "%U", &tm); test((size==2) && !cmp(s, "00"), "strftime test #22", s); size = strftime(s, 10, "%W", &tm); test((size==2) && !cmp(s, "00"), "strftime test #23", s); // 1/1/1973 was a Sunday and is in CW 1 tm.tm_year = 73; size = strftime(s, 10, "%W", &tm); test((size==2) && !cmp(s, "01"), "strftime test #24", s); // 1/1/1978 was a Monday and is in CW 1 tm.tm_year = 78; size = strftime(s, 10, "%U", &tm); test((size==2) && !cmp(s, "01"), "strftime test #25", s); // 2/1/1999 tm.tm_year = 99; tm.tm_yday = 1; size = strftime(s, 10, "%G (%V)", &tm); test((size==9) && !cmp(s, "1998 (53)"), "strftime test #26", s); size = strftime(s, 10, "%g", &tm); test((size==2) && !cmp(s, "98"), "strftime test #27", s); // 30/12/1997 tm.tm_year = 97; tm.tm_yday = 363; size = strftime(s, 10, "%G (%V)", &tm); test((size==9) && !cmp(s, "1998 (01)"), "strftime test #28", s); size = strftime(s, 10, "%g", &tm); test((size==2) && !cmp(s, "98"), "strftime test #29", s); } ''' self.do_run(src, '11111111111111111111111111111') def test_intentional_fault(self): # Some programs intentionally segfault themselves, we should compile that into a throw src = r''' int main () { *(volatile char *)0 = 0; return *(volatile char *)0; } ''' self.do_run(src, 'fault on write to 0' if not Settings.ASM_JS else 'abort()') def test_trickystring(self): src = r''' #include typedef struct { int (*f)(void *); void *d; char s[16]; } LMEXFunctionStruct; int f(void *user) { return 0; } static LMEXFunctionStruct const a[] = { {f, (void *)(int)'a', "aa"} }; int main() { printf("ok\n"); return a[0].f(a[0].d); } ''' self.do_run(src, 'ok\n') def test_statics(self): # static initializers save i16 but load i8 for some reason (or i64 and load i8) if Settings.SAFE_HEAP: Settings.SAFE_HEAP = 3 Settings.SAFE_HEAP_LINES = ['src.cpp:19', 'src.cpp:26', 'src.cpp:28'] src = ''' #include #include #define CONSTRLEN 32 char * (*func)(char *, const char *) = NULL; void conoutfv(const char *fmt) { static char buf[CONSTRLEN]; func(buf, fmt); // call by function pointer to make sure we test strcpy here puts(buf); } struct XYZ { float x, y, z; XYZ(float a, float b, float c) : x(a), y(b), z(c) { } static const XYZ& getIdentity() { static XYZ iT(1,2,3); return iT; } }; struct S { static const XYZ& getIdentity() { static const XYZ iT(XYZ::getIdentity()); return iT; } }; int main() { func = &strcpy; conoutfv("*staticccz*"); printf("*%.2f,%.2f,%.2f*\\n", S::getIdentity().x, S::getIdentity().y, S::getIdentity().z); return 0; } ''' self.do_run(src, '*staticccz*\n*1.00,2.00,3.00*') def test_copyop(self): if self.emcc_args is None: return self.skip('requires emcc') # clang generated code is vulnerable to this, as it uses # memcpy for assignments, with hardcoded numbers of bytes # (llvm-gcc copies items one by one). See QUANTUM_SIZE in # settings.js. src = ''' #include #include #include struct vec { double x,y,z; vec() : x(0), y(0), z(0) { }; vec(const double a, const double b, const double c) : x(a), y(b), z(c) { }; }; struct basis { vec a, b, c; basis(const vec& v) { a=v; // should not touch b! printf("*%.2f,%.2f,%.2f*\\n", b.x, b.y, b.z); } }; int main() { basis B(vec(1,0,0)); // Part 2: similar problem with memset and memmove int x = 1, y = 77, z = 2; memset((void*)&x, 0, sizeof(int)); memset((void*)&z, 0, sizeof(int)); printf("*%d,%d,%d*\\n", x, y, z); memcpy((void*)&x, (void*)&z, sizeof(int)); memcpy((void*)&z, (void*)&x, sizeof(int)); printf("*%d,%d,%d*\\n", x, y, z); memmove((void*)&x, (void*)&z, sizeof(int)); memmove((void*)&z, (void*)&x, sizeof(int)); printf("*%d,%d,%d*\\n", x, y, z); return 0; } ''' self.do_run(src, '*0.00,0.00,0.00*\n*0,77,0*\n*0,77,0*\n*0,77,0*') def test_memcpy_memcmp(self): src = ''' #include #include #include #define MAXX 48 void reset(unsigned char *buffer) { for (int i = 0; i < MAXX; i++) buffer[i] = i+1; } void dump(unsigned char *buffer) { for (int i = 0; i < MAXX-1; i++) printf("%2d,", buffer[i]); printf("%d\\n", buffer[MAXX-1]); } int main() { unsigned char buffer[MAXX]; for (int i = MAXX/4; i < MAXX-MAXX/4; i++) { for (int j = MAXX/4; j < MAXX-MAXX/4; j++) { for (int k = 1; k < MAXX/4; k++) { if (i == j) continue; if (i < j && i+k > j) continue; if (j < i && j+k > i) continue; printf("[%d,%d,%d] ", i, j, k); reset(buffer); memcpy(buffer+i, buffer+j, k); dump(buffer); assert(memcmp(buffer+i, buffer+j, k) == 0); buffer[i + k/2]++; if (buffer[i + k/2] != 0) { assert(memcmp(buffer+i, buffer+j, k) > 0); } else { assert(memcmp(buffer+i, buffer+j, k) < 0); } buffer[i + k/2]--; buffer[j + k/2]++; if (buffer[j + k/2] != 0) { assert(memcmp(buffer+i, buffer+j, k) < 0); } else { assert(memcmp(buffer+i, buffer+j, k) > 0); } } } } return 0; } ''' def check(result, err): return hashlib.sha1(result).hexdigest() self.do_run(src, '6c9cdfe937383b79e52ca7a2cce83a21d9f5422c', output_nicerizer = check) def test_memcpy2(self): src = r''' #include #include #include int main() { char buffer[256]; for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { for (int k = 0; k < 35; k++) { for (int t = 0; t < 256; t++) buffer[t] = t; char *dest = buffer + i + 128; char *src = buffer+j; //printf("%d, %d, %d\n", i, j, k); assert(memcpy(dest, src, k) == dest); assert(memcmp(dest, src, k) == 0); } } } printf("ok.\n"); return 1; } ''' self.do_run(src, 'ok.'); def test_getopt(self): if self.emcc_args is None: return self.skip('needs emcc for libc') src = ''' #pragma clang diagnostic ignored "-Winvalid-pp-token" #include #include #include int main(int argc, char *argv[]) { int flags, opt; int nsecs, tfnd; nsecs = 0; tfnd = 0; flags = 0; while ((opt = getopt(argc, argv, "nt:")) != -1) { switch (opt) { case 'n': flags = 1; break; case 't': nsecs = atoi(optarg); tfnd = 1; break; default: /* '?' */ fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\\n", argv[0]); exit(EXIT_FAILURE); } } printf("flags=%d; tfnd=%d; optind=%d\\n", flags, tfnd, optind); if (optind >= argc) { fprintf(stderr, "Expected argument after options\\n"); exit(EXIT_FAILURE); } printf("name argument = %s\\n", argv[optind]); /* Other code omitted */ exit(EXIT_SUCCESS); } ''' self.do_run(src, 'flags=1; tfnd=1; optind=4\nname argument = foobar', args=['-t', '12', '-n', 'foobar']) def test_getopt_long(self): if self.emcc_args is None: return self.skip('needs emcc for libc') src = ''' #pragma clang diagnostic ignored "-Winvalid-pp-token" #pragma clang diagnostic ignored "-Wdeprecated-writable-strings" #include /* for printf */ #include /* for exit */ #include int main(int argc, char **argv) { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", required_argument, 0, 0 }, {"append", no_argument, 0, 0 }, {"delete", required_argument, 0, 0 }, {"verbose", no_argument, 0, 0 }, {"create", required_argument, 0, 'c'}, {"file", required_argument, 0, 0 }, {0, 0, 0, 0 } }; c = getopt_long(argc, argv, "abc:d:012", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\\n"); break; case '0': case '1': case '2': if (digit_optind != 0 && digit_optind != this_option_optind) printf("digits occur in two different argv-elements.\\n"); digit_optind = this_option_optind; printf("option %c\\n", c); break; case 'a': printf("option a\\n"); break; case 'b': printf("option b\\n"); break; case 'c': printf("option c with value '%s'\\n", optarg); break; case 'd': printf("option d with value '%s'\\n", optarg); break; case '?': break; default: printf("?? getopt returned character code 0%o ??\\n", c); } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\\n"); } exit(EXIT_SUCCESS); } ''' self.do_run(src, 'option file with arg foobar\noption b', args=['--file', 'foobar', '-b']) def test_memmove(self): src = ''' #include #include int main() { char str[] = "memmove can be very useful....!"; memmove (str+20, str+15, 11); puts(str); return 0; } ''' self.do_run(src, 'memmove can be very very useful') def test_memmove2(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('need ta2') src = r''' #include #include #include int main() { int sum = 0; char buffer[256]; for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { for (int k = 0; k < 35; k++) { for (int t = 0; t < 256; t++) buffer[t] = t; char *dest = buffer + i; char *src = buffer + j; if (dest == src) continue; //printf("%d, %d, %d\n", i, j, k); assert(memmove(dest, src, k) == dest); for (int t = 0; t < 256; t++) sum += buffer[t]; } } } printf("final: %d.\n", sum); return 1; } ''' self.do_run(src, 'final: -403200.'); def test_memmove3(self): src = ''' #include #include int main() { char str[] = "memmove can be vvery useful....!"; memmove(str+15, str+16, 17); puts(str); return 0; } ''' self.do_run(src, 'memmove can be very useful....!') def test_flexarray_struct(self): src = r''' #include #include #include typedef struct { uint16_t length; struct { int32_t int32; } value[]; } Tuple; int main() { Tuple T[10]; Tuple *t = &T[0]; t->length = 4; t->value->int32 = 100; printf("(%d, %d)\n", t->length, t->value->int32); return 0; } ''' self.do_run(src, '(4, 100)') def test_bsearch(self): if Settings.QUANTUM_SIZE == 1: return self.skip('Test cannot work with q1') src = ''' #include #include int cmp(const void* key, const void* member) { return *(int *)key - *(int *)member; } void printResult(int* needle, int* haystack, unsigned int len) { void *result = bsearch(needle, haystack, len, sizeof(unsigned int), cmp); if (result == NULL) { printf("null\\n"); } else { printf("%d\\n", *(unsigned int *)result); } } int main() { int a[] = { -2, -1, 0, 6, 7, 9 }; int b[] = { 0, 1 }; /* Find all keys that exist. */ for(int i = 0; i < 6; i++) { int val = a[i]; printResult(&val, a, 6); } /* Keys that are covered by the range of the array but aren't in * the array cannot be found. */ int v1 = 3; int v2 = 8; printResult(&v1, a, 6); printResult(&v2, a, 6); /* Keys outside the range of the array cannot be found. */ int v3 = -1; int v4 = 2; printResult(&v3, b, 2); printResult(&v4, b, 2); return 0; } ''' self.do_run(src, '-2\n-1\n0\n6\n7\n9\nnull\nnull\nnull\nnull') def test_nestedstructs(self): src = ''' #include #include "emscripten.h" struct base { int x; float y; union { int a; float b; }; char c; }; struct hashtableentry { int key; base data; }; struct hashset { typedef hashtableentry entry; struct chain { entry elem; chain *next; }; // struct chainchunk { chain chains[100]; chainchunk *next; }; }; struct hashtable : hashset { hashtable() { base *b = NULL; entry *e = NULL; chain *c = NULL; printf("*%d,%d,%d,%d,%d,%d|%d,%d,%d,%d,%d,%d,%d,%d|%d,%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", sizeof(base), int(&(b->x)), int(&(b->y)), int(&(b->a)), int(&(b->b)), int(&(b->c)), sizeof(hashtableentry), int(&(e->key)), int(&(e->data)), int(&(e->data.x)), int(&(e->data.y)), int(&(e->data.a)), int(&(e->data.b)), int(&(e->data.c)), sizeof(hashset::chain), int(&(c->elem)), int(&(c->next)), int(&(c->elem.key)), int(&(c->elem.data)), int(&(c->elem.data.x)), int(&(c->elem.data.y)), int(&(c->elem.data.a)), int(&(c->elem.data.b)), int(&(c->elem.data.c)) ); } }; struct B { char buffer[62]; int last; char laster; char laster2; }; struct Bits { unsigned short A : 1; unsigned short B : 1; unsigned short C : 1; unsigned short D : 1; unsigned short x1 : 1; unsigned short x2 : 1; unsigned short x3 : 1; unsigned short x4 : 1; }; int main() { hashtable t; // Part 2 - the char[] should be compressed, BUT have a padding space at the end so the next // one is aligned properly. Also handle char; char; etc. properly. B *b = NULL; printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", int(b), int(&(b->buffer)), int(&(b->buffer[0])), int(&(b->buffer[1])), int(&(b->buffer[2])), int(&(b->last)), int(&(b->laster)), int(&(b->laster2)), sizeof(B)); // Part 3 - bitfields, and small structures Bits *b2 = NULL; printf("*%d*\\n", sizeof(Bits)); return 0; } ''' if Settings.QUANTUM_SIZE == 1: # Compressed memory. Note that sizeof() does give the fat sizes, however! self.do_run(src, '*16,0,1,2,2,3|20,0,1,1,2,3,3,4|24,0,5,0,1,1,2,3,3,4*\n*0,0,0,1,2,62,63,64,72*\n*2*') else: # Bloated memory; same layout as C/C++ self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*') def test_runtimelink(self): return self.skip('BUILD_AS_SHARED_LIB=2 is deprecated') if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize printf into puts in the parent, and the child will still look for puts') if Settings.ASM_JS: return self.skip('asm does not support runtime linking') main, supp = self.setup_runtimelink_test() self.banned_js_engines = [NODE_JS] # node's global scope behaves differently than everything else, needs investigation FIXME Settings.LINKABLE = 1 Settings.BUILD_AS_SHARED_LIB = 2 Settings.NAMED_GLOBALS = 1 self.build(supp, self.get_dir(), self.in_dir('supp.cpp')) shutil.move(self.in_dir('supp.cpp.o.js'), self.in_dir('liblib.so')) Settings.BUILD_AS_SHARED_LIB = 0 Settings.RUNTIME_LINKED_LIBS = ['liblib.so']; self.do_run(main, 'supp: 54,2\nmain: 56\nsupp see: 543\nmain see: 76\nok.') def can_dlfcn(self): if self.emcc_args and '--memory-init-file' in self.emcc_args: for i in range(len(self.emcc_args)): if self.emcc_args[i] == '--memory-init-file': self.emcc_args = self.emcc_args[:i] + self.emcc_args[i+2:] break if Settings.ASM_JS: Settings.DLOPEN_SUPPORT = 1 else: Settings.NAMED_GLOBALS = 1 if not self.is_le32(): self.skip('need le32 for dlfcn support') return False else: return True def prep_dlfcn_lib(self): if Settings.ASM_JS: Settings.MAIN_MODULE = 0 Settings.SIDE_MODULE = 1 else: Settings.BUILD_AS_SHARED_LIB = 1 Settings.INCLUDE_FULL_LIBRARY = 0 def prep_dlfcn_main(self): if Settings.ASM_JS: Settings.MAIN_MODULE = 1 Settings.SIDE_MODULE = 0 else: Settings.BUILD_AS_SHARED_LIB = 0 Settings.INCLUDE_FULL_LIBRARY = 1 dlfcn_post_build = ''' def process(filename): src = open(filename, 'r').read().replace( '// {{PRE_RUN_ADDITIONS}}', "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" ) open(filename, 'w').write(src) ''' def test_dlfcn_basic(self): if not self.can_dlfcn(): return self.prep_dlfcn_lib() lib_src = ''' #include class Foo { public: Foo() { printf("Constructing lib object.\\n"); } }; Foo global; ''' dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.cpp') self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() src = ''' #include #include class Bar { public: Bar() { printf("Constructing main object.\\n"); } }; Bar global; int main() { dlopen("liblib.so", RTLD_NOW); return 0; } ''' self.do_run(src, 'Constructing main object.\nConstructing lib object.\n', post_build=self.dlfcn_post_build) def test_dlfcn_qsort(self): if not self.can_dlfcn(): return if Settings.USE_TYPED_ARRAYS == 2: Settings.CORRECT_SIGNS = 1 # Needed for unsafe optimizations self.prep_dlfcn_lib() Settings.EXPORTED_FUNCTIONS = ['_get_cmp'] lib_src = ''' int lib_cmp(const void* left, const void* right) { const int* a = (const int*) left; const int* b = (const int*) right; if(*a > *b) return 1; else if(*a == *b) return 0; else return -1; } typedef int (*CMP_TYPE)(const void*, const void*); extern "C" CMP_TYPE get_cmp() { return lib_cmp; } ''' dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.cpp') self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc'] src = ''' #include #include #include typedef int (*CMP_TYPE)(const void*, const void*); int main_cmp(const void* left, const void* right) { const int* a = (const int*) left; const int* b = (const int*) right; if(*a < *b) return 1; else if(*a == *b) return 0; else return -1; } int main() { void* lib_handle; CMP_TYPE (*getter_ptr)(); CMP_TYPE lib_cmp_ptr; int arr[5] = {4, 2, 5, 1, 3}; qsort((void*)arr, 5, sizeof(int), main_cmp); printf("Sort with main comparison: "); for (int i = 0; i < 5; i++) { printf("%d ", arr[i]); } printf("\\n"); lib_handle = dlopen("liblib.so", RTLD_NOW); if (lib_handle == NULL) { printf("Could not load lib.\\n"); return 1; } getter_ptr = (CMP_TYPE (*)()) dlsym(lib_handle, "get_cmp"); if (getter_ptr == NULL) { printf("Could not find func.\\n"); return 1; } lib_cmp_ptr = getter_ptr(); qsort((void*)arr, 5, sizeof(int), lib_cmp_ptr); printf("Sort with lib comparison: "); for (int i = 0; i < 5; i++) { printf("%d ", arr[i]); } printf("\\n"); return 0; } ''' self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *', output_nicerizer=lambda x, err: x.replace('\n', '*'), post_build=self.dlfcn_post_build) if Settings.ASM_JS and os.path.exists(SPIDERMONKEY_ENGINE[0]): out = run_js('liblib.so', engine=SPIDERMONKEY_ENGINE, full_output=True, stderr=STDOUT) if 'asm' in out: self.validate_asmjs(out) def test_dlfcn_data_and_fptr(self): if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly') if not self.can_dlfcn(): return if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func') self.prep_dlfcn_lib() lib_src = ''' #include int global = 42; extern void parent_func(); // a function that is defined in the parent void lib_fptr() { printf("Second calling lib_fptr from main.\\n"); parent_func(); // call it also through a pointer, to check indexizing void (*p_f)(); p_f = parent_func; p_f(); } extern "C" void (*func(int x, void(*fptr)()))() { printf("In func: %d\\n", x); fptr(); return lib_fptr; } ''' dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.cpp') Settings.EXPORTED_FUNCTIONS = ['_func'] Settings.EXPORTED_GLOBALS = ['_global'] self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() Settings.LINKABLE = 1 src = ''' #include #include #include typedef void (*FUNCTYPE(int, void(*)()))(); FUNCTYPE func; void EMSCRIPTEN_KEEPALIVE parent_func() { printf("parent_func called from child\\n"); } void main_fptr() { printf("First calling main_fptr from lib.\\n"); } int main() { void* lib_handle; FUNCTYPE* func_fptr; // Test basic lib loading. lib_handle = dlopen("liblib.so", RTLD_NOW); if (lib_handle == NULL) { printf("Could not load lib.\\n"); return 1; } // Test looked up function. func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func"); // Load twice to test cache. func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func"); if (func_fptr == NULL) { printf("Could not find func.\\n"); return 1; } // Test passing function pointers across module bounds. void (*fptr)() = func_fptr(13, main_fptr); fptr(); // Test global data. int* global = (int*) dlsym(lib_handle, "global"); if (global == NULL) { printf("Could not find global.\\n"); return 1; } printf("Var: %d\\n", *global); return 0; } ''' Settings.EXPORTED_FUNCTIONS = ['_main'] Settings.EXPORTED_GLOBALS = [] self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*', output_nicerizer=lambda x, err: x.replace('\n', '*'), post_build=self.dlfcn_post_build) def test_dlfcn_alias(self): if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly') Settings.LINKABLE = 1 Settings.NAMED_GLOBALS = 1 if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize away stuff we expect from the shared library') lib_src = r''' #include extern int parent_global; extern "C" void func() { printf("Parent global: %d.\n", parent_global); } ''' dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.cpp') Settings.BUILD_AS_SHARED_LIB = 1 Settings.EXPORTED_FUNCTIONS = ['_func'] self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) src = r''' #include int parent_global = 123; int main() { void* lib_handle; void (*fptr)(); lib_handle = dlopen("liblib.so", RTLD_NOW); fptr = (void (*)())dlsym(lib_handle, "func"); fptr(); parent_global = 456; fptr(); return 0; } ''' Settings.BUILD_AS_SHARED_LIB = 0 Settings.INCLUDE_FULL_LIBRARY = 1 Settings.EXPORTED_FUNCTIONS = ['_main'] self.do_run(src, 'Parent global: 123.*Parent global: 456.*', output_nicerizer=lambda x, err: x.replace('\n', '*'), post_build=self.dlfcn_post_build, extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h']) Settings.INCLUDE_FULL_LIBRARY = 0 def test_dlfcn_varargs(self): if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly') if not self.can_dlfcn(): return Settings.LINKABLE = 1 if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize things that prevent shared objects from working') if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this') self.prep_dlfcn_lib() lib_src = r''' void print_ints(int n, ...); extern "C" void func() { print_ints(2, 13, 42); } ''' dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.cpp') Settings.EXPORTED_FUNCTIONS = ['_func'] self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() src = r''' #include #include #include #include void print_ints(int n, ...) { va_list args; va_start(args, n); for (int i = 0; i < n; i++) { printf("%d\n", va_arg(args, int)); } va_end(args); } int main() { void* lib_handle; void (*fptr)(); print_ints(2, 100, 200); lib_handle = dlopen("liblib.so", RTLD_NOW); assert(lib_handle); fptr = (void (*)())dlsym(lib_handle, "func"); fptr(); return 0; } ''' Settings.EXPORTED_FUNCTIONS = ['_main'] self.do_run(src, '100\n200\n13\n42\n', post_build=self.dlfcn_post_build) def test_dlfcn_self(self): if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1') Settings.DLOPEN_SUPPORT = 1 src = r''' #include #include #include int EMSCRIPTEN_KEEPALIVE global = 123; extern "C" EMSCRIPTEN_KEEPALIVE void foo(int x) { printf("%d\n", x); } extern "C" EMSCRIPTEN_KEEPALIVE void repeatable() { void* self = dlopen(NULL, RTLD_LAZY); int* global_ptr = (int*)dlsym(self, "global"); void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo"); foo_ptr(*global_ptr); dlclose(self); } int main() { repeatable(); repeatable(); return 0; }''' def post(filename): with open(filename) as f: for line in f: if 'var SYMBOL_TABLE' in line: table = line break else: raise Exception('Could not find symbol table!') table = table[table.find('{'):table.rfind('}')+1] # ensure there aren't too many globals; we don't want unnamed_addr assert table.count(',') <= 4 self.do_run(src, '123\n123', post_build=(None, post)) def test_dlfcn_unique_sig(self): if not self.can_dlfcn(): return self.prep_dlfcn_lib() lib_src = ''' #include int myfunc(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m) { return 13; } ''' Settings.EXPORTED_FUNCTIONS = ['_myfunc'] dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.c') self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() src = ''' #include #include #include typedef int (*FUNCTYPE)(int, int, int, int, int, int, int, int, int, int, int, int, int); int main() { void *lib_handle; FUNCTYPE func_ptr; lib_handle = dlopen("liblib.so", RTLD_NOW); assert(lib_handle != NULL); func_ptr = (FUNCTYPE)dlsym(lib_handle, "myfunc"); assert(func_ptr != NULL); assert(func_ptr(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == 13); puts("success"); return 0; } ''' Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc'] self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build) def test_dlfcn_stacks(self): if not self.can_dlfcn(): return self.prep_dlfcn_lib() lib_src = ''' #include #include #include int myfunc(const char *input) { char bigstack[1024] = { 0 }; // make sure we didn't just trample the stack! assert(!strcmp(input, "foobar")); snprintf(bigstack, sizeof(bigstack), input); return strlen(bigstack); } ''' Settings.EXPORTED_FUNCTIONS = ['_myfunc'] dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.c') self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() src = ''' #include #include #include typedef int (*FUNCTYPE)(const char *); int main() { void *lib_handle; FUNCTYPE func_ptr; char str[128]; snprintf(str, sizeof(str), "foobar"); lib_handle = dlopen("liblib.so", RTLD_NOW); assert(lib_handle != NULL); func_ptr = (FUNCTYPE)dlsym(lib_handle, "myfunc"); assert(func_ptr != NULL); assert(func_ptr(str) == 6); puts("success"); return 0; } ''' Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc'] self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build) def test_dlfcn_funcs(self): if not self.can_dlfcn(): return self.prep_dlfcn_lib() lib_src = r''' #include #include #include typedef void (*voidfunc)(); typedef void (*intfunc)(int); void callvoid(voidfunc f) { f(); } void callint(voidfunc f, int x) { f(x); } void void_0() { printf("void 0\n"); } void void_1() { printf("void 1\n"); } voidfunc getvoid(int i) { switch(i) { case 0: return void_0; case 1: return void_1; default: return NULL; } } void int_0(int x) { printf("int 0 %d\n", x); } void int_1(int x) { printf("int 1 %d\n", x); } intfunc getint(int i) { switch(i) { case 0: return int_0; case 1: return int_1; default: return NULL; } } ''' Settings.EXPORTED_FUNCTIONS = ['_callvoid', '_callint', '_getvoid', '_getint'] dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.c') self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() src = r''' #include #include #include typedef void (*voidfunc)(); typedef void (*intfunc)(int); typedef void (*voidcaller)(voidfunc); typedef void (*intcaller)(intfunc, int); typedef voidfunc (*voidgetter)(int); typedef intfunc (*intgetter)(int); void void_main() { printf("main.\n"); } void int_main(int x) { printf("main %d\n", x); } int main() { printf("go\n"); void *lib_handle; lib_handle = dlopen("liblib.so", RTLD_NOW); assert(lib_handle != NULL); voidcaller callvoid = (voidcaller)dlsym(lib_handle, "callvoid"); assert(callvoid != NULL); callvoid(void_main); intcaller callint = (intcaller)dlsym(lib_handle, "callint"); assert(callint != NULL); callint(int_main, 201); voidgetter getvoid = (voidgetter)dlsym(lib_handle, "getvoid"); assert(getvoid != NULL); callvoid(getvoid(0)); callvoid(getvoid(1)); intgetter getint = (intgetter)dlsym(lib_handle, "getint"); assert(getint != NULL); callint(getint(0), 54); callint(getint(1), 9000); assert(getint(1000) == NULL); puts("ok"); return 0; } ''' Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc'] self.do_run(src, '''go main. main 201 void 0 void 1 int 0 54 int 1 9000 ok ''', force_c=True, post_build=self.dlfcn_post_build) def test_dlfcn_mallocs(self): if not Settings.ASM_JS: return self.skip('needs asm') if not self.can_dlfcn(): return Settings.TOTAL_MEMORY = 64*1024*1024 # will be exhausted without functional malloc/free self.prep_dlfcn_lib() lib_src = r''' #include #include #include #include void *mallocproxy(int n) { return malloc(n); } void freeproxy(void *p) { free(p); } ''' Settings.EXPORTED_FUNCTIONS = ['_mallocproxy', '_freeproxy'] dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.c') self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() src = open(path_from_root('tests', 'dlmalloc_proxy.c')).read() Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free'] self.do_run(src, '''*294,153*''', force_c=True, post_build=self.dlfcn_post_build) def test_dlfcn_longjmp(self): if not self.can_dlfcn(): return self.prep_dlfcn_lib() lib_src = r''' #include void jumpy(jmp_buf buf) { static int i = 0; i++; if (i == 10) longjmp(buf, i); printf("pre %d\n", i); } ''' Settings.EXPORTED_FUNCTIONS = ['_jumpy'] dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.c') self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() src = r''' #include #include #include #include typedef void (*jumpfunc)(jmp_buf); int main() { printf("go!\n"); void *lib_handle; lib_handle = dlopen("liblib.so", RTLD_NOW); assert(lib_handle != NULL); jumpfunc jumpy = (jumpfunc)dlsym(lib_handle, "jumpy"); assert(jumpy); jmp_buf buf; int jmpval = setjmp(buf); if (jmpval == 0) { while (1) jumpy(buf); } else { printf("out!\n"); } return 0; } ''' Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free'] self.do_run(src, '''go! pre 1 pre 2 pre 3 pre 4 pre 5 pre 6 pre 7 pre 8 pre 9 out! ''', post_build=self.dlfcn_post_build, force_c=True) def zzztest_dlfcn_exceptions(self): # TODO: make this work. need to forward tempRet0 across modules if not self.can_dlfcn(): return Settings.DISABLE_EXCEPTION_CATCHING = 0 self.prep_dlfcn_lib() lib_src = r''' extern "C" { int ok() { return 65; } int fail() { throw 123; } } ''' Settings.EXPORTED_FUNCTIONS = ['_ok', '_fail'] dirname = self.get_dir() filename = os.path.join(dirname, 'liblib.cpp') self.build(lib_src, dirname, filename) shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) self.prep_dlfcn_main() src = r''' #include #include #include typedef int (*intfunc)(); int main() { printf("go!\n"); void *lib_handle; lib_handle = dlopen("liblib.so", RTLD_NOW); assert(lib_handle != NULL); intfunc okk = (intfunc)dlsym(lib_handle, "ok"); intfunc faill = (intfunc)dlsym(lib_handle, "fail"); assert(okk && faill); try { printf("ok: %d\n", okk()); } catch(...) { printf("wha\n"); } try { printf("fail: %d\n", faill()); } catch(int x) { printf("int %d\n", x); } try { printf("fail: %d\n", faill()); } catch(double x) { printf("caught %f\n", x); } return 0; } ''' Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free'] self.do_run(src, '''go! ok: 65 int 123 ok ''', post_build=self.dlfcn_post_build) def test_rand(self): return self.skip('rand() is now random') # FIXME src = r''' #include #include int main() { printf("%d\n", rand()); printf("%d\n", rand()); srand(123); printf("%d\n", rand()); printf("%d\n", rand()); srand(123); printf("%d\n", rand()); printf("%d\n", rand()); unsigned state = 0; int r; r = rand_r(&state); printf("%d, %u\n", r, state); r = rand_r(&state); printf("%d, %u\n", r, state); state = 0; r = rand_r(&state); printf("%d, %u\n", r, state); return 0; } ''' expected = ''' 1250496027 1116302336 440917656 1476150784 440917656 1476150784 12345, 12345 1406932606, 3554416254 12345, 12345 ''' self.do_run(src, re.sub(r'(^|\n)\s+', r'\1', expected)) def test_strtod(self): if self.emcc_args is None: return self.skip('needs emcc for libc') src = r''' #include #include int main() { char* endptr; printf("\n"); printf("%g\n", strtod("0", &endptr)); printf("%g\n", strtod("0.", &endptr)); printf("%g\n", strtod("0.0", &endptr)); printf("%g\n", strtod("-0.0", &endptr)); printf("%g\n", strtod("1", &endptr)); printf("%g\n", strtod("1.", &endptr)); printf("%g\n", strtod("1.0", &endptr)); printf("%g\n", strtod("z1.0", &endptr)); printf("%g\n", strtod("0.5", &endptr)); printf("%g\n", strtod(".5", &endptr)); printf("%g\n", strtod(".a5", &endptr)); printf("%g\n", strtod("123", &endptr)); printf("%g\n", strtod("123.456", &endptr)); printf("%g\n", strtod("-123.456", &endptr)); printf("%g\n", strtod("1234567891234567890", &endptr)); printf("%g\n", strtod("1234567891234567890e+50", &endptr)); printf("%g\n", strtod("84e+220", &endptr)); printf("%g\n", strtod("123e-50", &endptr)); printf("%g\n", strtod("123e-250", &endptr)); printf("%g\n", strtod("123e-450", &endptr)); char str[] = " 12.34e56end"; printf("%g\n", strtod(str, &endptr)); printf("%d\n", endptr - str); printf("%g\n", strtod("84e+420", &endptr)); printf("%.12f\n", strtod("1.2345678900000000e+08", NULL)); return 0; } ''' expected = ''' 0 0 0 -0 1 1 1 0 0.5 0.5 0 123 123.456 -123.456 1.23457e+18 1.23457e+68 8.4e+221 1.23e-48 1.23e-248 0 1.234e+57 10 inf 123456789.000000000000 ''' self.do_run(src, re.sub(r'\n\s+', '\n', expected)) self.do_run(src.replace('strtod', 'strtold'), re.sub(r'\n\s+', '\n', expected)) # XXX add real support for long double def test_strtok(self): src = r''' #include #include int main() { char test[80], blah[80]; char *sep = "\\/:;=-"; char *word, *phrase, *brkt, *brkb; strcpy(test, "This;is.a:test:of=the/string\\tokenizer-function."); for (word = strtok_r(test, sep, &brkt); word; word = strtok_r(NULL, sep, &brkt)) { strcpy(blah, "blah:blat:blab:blag"); for (phrase = strtok_r(blah, sep, &brkb); phrase; phrase = strtok_r(NULL, sep, &brkb)) { printf("at %s:%s\n", word, phrase); } } return 0; } ''' expected = '''at This:blah at This:blat at This:blab at This:blag at is.a:blah at is.a:blat at is.a:blab at is.a:blag at test:blah at test:blat at test:blab at test:blag at of:blah at of:blat at of:blab at of:blag at the:blah at the:blat at the:blab at the:blag at string:blah at string:blat at string:blab at string:blag at tokenizer:blah at tokenizer:blat at tokenizer:blab at tokenizer:blag at function.:blah at function.:blat at function.:blab at function.:blag ''' self.do_run(src, expected) def test_parseInt(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') if Settings.QUANTUM_SIZE == 1: return self.skip('Q1 and I64_1 do not mix well yet') src = open(path_from_root('tests', 'parseInt', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'parseInt', 'output.txt'), 'r').read() self.do_run(src, expected) def test_transtrcase(self): src = ''' #include #include int main() { char szToupr[] = "hello, "; char szTolwr[] = "EMSCRIPTEN"; strupr(szToupr); strlwr(szTolwr); printf(szToupr); printf(szTolwr); return 0; } ''' self.do_run(src, 'HELLO, emscripten') def test_printf(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') self.banned_js_engines = [NODE_JS, V8_ENGINE] # SpiderMonkey and V8 do different things to float64 typed arrays, un-NaNing, etc. src = open(path_from_root('tests', 'printf', 'test.c'), 'r').read() expected = [open(path_from_root('tests', 'printf', 'output.txt'), 'r').read(), open(path_from_root('tests', 'printf', 'output_i64_1.txt'), 'r').read()] self.do_run(src, expected) def test_printf_2(self): src = r''' #include int main() { char c = '1'; short s = 2; int i = 3; long long l = 4; float f = 5.5; double d = 6.6; printf("%c,%hd,%d,%lld,%.1f,%.1llf\n", c, s, i, l, f, d); printf("%#x,%#x\n", 1, 0); return 0; } ''' self.do_run(src, '1,2,3,4,5.5,6.6\n0x1,0\n') def test_vprintf(self): src = r''' #include #include void print(char* format, ...) { va_list args; va_start (args, format); vprintf (format, args); va_end (args); } int main () { print("Call with %d variable argument.\n", 1); print("Call with %d variable %s.\n", 2, "arguments"); return 0; } ''' expected = ''' Call with 1 variable argument. Call with 2 variable arguments. ''' self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) def test_vsnprintf(self): if self.emcc_args is None: return self.skip('needs i64 math') src = r''' #include #include #include void printy(const char *f, ...) { char buffer[256]; va_list args; va_start(args, f); vsnprintf(buffer, 256, f, args); puts(buffer); va_end(args); } int main(int argc, char **argv) { int64_t x = argc - 1; int64_t y = argc - 1 + 0x400000; if (x % 3 == 2) y *= 2; printy("0x%llx_0x%llx", x, y); printy("0x%llx_0x%llx", x, x); printy("0x%llx_0x%llx", y, x); printy("0x%llx_0x%llx", y, y); { uint64_t A = 0x800000; uint64_t B = 0x800000000000ULL; printy("0x%llx_0x%llx", A, B); } { uint64_t A = 0x800; uint64_t B = 0x12340000000000ULL; printy("0x%llx_0x%llx", A, B); } { uint64_t A = 0x000009182746756; uint64_t B = 0x192837465631ACBDULL; printy("0x%llx_0x%llx", A, B); } return 0; } ''' self.do_run(src, '''0x0_0x400000 0x0_0x0 0x400000_0x0 0x400000_0x400000 0x800000_0x800000000000 0x800_0x12340000000000 0x9182746756_0x192837465631acbd ''') def test_printf_more(self): src = r''' #include int main() { int size = snprintf(NULL, 0, "%s %d %.2f\n", "me and myself", 25, 1.345); char buf[size]; snprintf(buf, size, "%s %d %.2f\n", "me and myself", 25, 1.345); printf("%d : %s\n", size, buf); char *buff = NULL; asprintf(&buff, "%d waka %d\n", 21, 95); puts(buff); return 0; } ''' self.do_run(src, '22 : me and myself 25 1.34\n21 waka 95\n') def test_perrar(self): src = r''' #include #include #include #include int main( int argc, char** argv ){ int retval = open( "NonExistingFile", O_RDONLY ); if( retval == -1 ) perror( "Cannot open NonExistingFile" ); return 0; } ''' self.do_run(src, 'Cannot open NonExistingFile: No such file or directory\n') def test_atoX(self): if self.emcc_args is None: return self.skip('requires ta2') src = r''' #include #include int main () { printf("%d*", atoi("")); printf("%d*", atoi("a")); printf("%d*", atoi(" b")); printf("%d*", atoi(" c ")); printf("%d*", atoi("6")); printf("%d*", atoi(" 5")); printf("%d*", atoi("4 ")); printf("%d*", atoi("3 6")); printf("%d*", atoi(" 3 7")); printf("%d*", atoi("9 d")); printf("%d\n", atoi(" 8 e")); printf("%d*", atol("")); printf("%d*", atol("a")); printf("%d*", atol(" b")); printf("%d*", atol(" c ")); printf("%d*", atol("6")); printf("%d*", atol(" 5")); printf("%d*", atol("4 ")); printf("%d*", atol("3 6")); printf("%d*", atol(" 3 7")); printf("%d*", atol("9 d")); printf("%d\n", atol(" 8 e")); printf("%lld*", atoll("6294967296")); printf("%lld*", atoll("")); printf("%lld*", atoll("a")); printf("%lld*", atoll(" b")); printf("%lld*", atoll(" c ")); printf("%lld*", atoll("6")); printf("%lld*", atoll(" 5")); printf("%lld*", atoll("4 ")); printf("%lld*", atoll("3 6")); printf("%lld*", atoll(" 3 7")); printf("%lld*", atoll("9 d")); printf("%lld\n", atoll(" 8 e")); return 0; } ''' self.do_run(src, '0*0*0*0*6*5*4*3*3*9*8\n0*0*0*0*6*5*4*3*3*9*8\n6294967296*0*0*0*0*6*5*4*3*3*9*8\n') def test_strstr(self): src = r''' #include #include int main() { printf("%d\n", !!strstr("\\n", "\\n")); printf("%d\n", !!strstr("cheezy", "ez")); printf("%d\n", !!strstr("cheeezy", "ez")); printf("%d\n", !!strstr("cheeeeeeeeeezy", "ez")); printf("%d\n", !!strstr("cheeeeeeeeee1zy", "ez")); printf("%d\n", !!strstr("che1ezy", "ez")); printf("%d\n", !!strstr("che1ezy", "che")); printf("%d\n", !!strstr("ce1ezy", "che")); printf("%d\n", !!strstr("ce1ezy", "ezy")); printf("%d\n", !!strstr("ce1ezyt", "ezy")); printf("%d\n", !!strstr("ce1ez1y", "ezy")); printf("%d\n", !!strstr("cheezy", "a")); printf("%d\n", !!strstr("cheezy", "b")); printf("%d\n", !!strstr("cheezy", "c")); printf("%d\n", !!strstr("cheezy", "d")); printf("%d\n", !!strstr("cheezy", "g")); printf("%d\n", !!strstr("cheezy", "h")); printf("%d\n", !!strstr("cheezy", "i")); printf("%d\n", !!strstr("cheezy", "e")); printf("%d\n", !!strstr("cheezy", "x")); printf("%d\n", !!strstr("cheezy", "y")); printf("%d\n", !!strstr("cheezy", "z")); printf("%d\n", !!strstr("cheezy", "_")); const char *str = "a big string"; printf("%d\n", strstr(str, "big") - str); return 0; } ''' self.do_run(src, '''1 1 1 1 0 1 1 0 1 1 0 0 0 1 0 0 1 0 1 0 1 1 0 2 ''') def test_sscanf(self): if self.emcc_args is None: return self.skip('needs emcc for libc') src = r''' #include #include #include int main () { #define CHECK(str) \ { \ char name[1000]; \ memset(name, 0, 1000); \ int prio = 99; \ sscanf(str, "%s %d", name, &prio); \ printf("%s : %d\n", name, prio); \ } CHECK("en-us 2"); CHECK("en-r"); CHECK("en 3"); printf("%f, %f\n", atof("1.234567"), atof("cheez")); char float_formats[] = "fegE"; char format[] = "%_"; for(int i = 0; i < 4; ++i) { format[1] = float_formats[i]; float n = -1; sscanf(" 2.8208", format, &n); printf("%.4f\n", n); float a = -1; sscanf("-3.03", format, &a); printf("%.4f\n", a); } char buffy[100]; sscanf("cheez some thing moar 123\nyet more\n", "cheez %s", buffy); printf("|%s|\n", buffy); sscanf("cheez something\nmoar 123\nyet more\n", "cheez %s", buffy); printf("|%s|\n", buffy); sscanf("cheez somethingmoar\tyet more\n", "cheez %s", buffy); printf("|%s|\n", buffy); int numverts = -1; printf("%d\n", sscanf(" numverts 1499\n", " numverts %d", &numverts)); // white space is the same, even if tab vs space printf("%d\n", numverts); int index; float u, v; short start, count; printf("%d\n", sscanf(" vert 87 ( 0.481565 0.059481 ) 0 1\n", " vert %d ( %f %f ) %hu %hu", &index, &u, &v, &start, &count)); printf("%d,%.6f,%.6f,%hu,%hu\n", index, u, v, start, count); int neg, neg2, neg3 = 0; printf("%d\n", sscanf("-123 -765 -34-6", "%d %u %d", &neg, &neg2, &neg3)); printf("%d,%u,%d\n", neg, neg2, neg3); { int a = 0; sscanf("1", "%i", &a); printf("%i\n", a); } char buf1[100], buf2[100], buf3[100], buf4[100]; int numItems = sscanf("level=4:ref=3", "%255[^:=]=%255[^:]:%255[^=]=%255c", buf1, buf2, buf3, buf4); printf("%d, %s, %s, %s, %s\n", numItems, buf1, buf2, buf3, buf4); numItems = sscanf("def|456", "%[a-z]|%[0-9]", buf1, buf2); printf("%d, %s, %s\n", numItems, buf1, buf2); numItems = sscanf("3-4,-ab", "%[-0-9],%[ab-z-]", buf1, buf2); printf("%d, %s, %s\n", numItems, buf1, buf2); numItems = sscanf("Hello,World", "%[A-Za-z],%[^0-9]", buf1, buf2); printf("%d, %s, %s\n", numItems, buf1, buf2); numItems = sscanf("Hello4711", "%[^0-9],%[^0-9]", buf1, buf2); printf("%d, %s\n", numItems, buf1); numItems = sscanf("JavaScript", "%4[A-Za-z]", buf1); printf("%d, %s\n", numItems, buf1); numItems = sscanf("[]", "%1[[]%1[]]", buf1, buf2); printf("%d, %s, %s\n", numItems, buf1, buf2); return 0; } ''' self.do_run(src, 'en-us : 2\nen-r : 99\nen : 3\n1.234567, 0.000000\n2.8208\n-3.0300\n2.8208\n-3.0300\n2.8208\n-3.0300\n2.8208\n-3.0300\n|some|\n|something|\n|somethingmoar|\n' + '1\n1499\n' + '5\n87,0.481565,0.059481,0,1\n' + '3\n-123,4294966531,-34\n' + '1\n' + '4, level, 4, ref, 3\n' + '2, def, 456\n' + '2, 3-4, -ab\n' + '2, Hello, World\n' + '1, Hello\n' + '1, Java\n' + '2, [, ]') def test_sscanf_2(self): # doubles if Settings.USE_TYPED_ARRAYS == 2: for ftype in ['float', 'double']: src = r''' #include int main(){ char strval1[] = "1.2345678901"; char strval2[] = "1.23456789e5"; char strval3[] = "1.23456789E5"; char strval4[] = "1.2345678e-5"; char strval5[] = "1.2345678E-5"; double dblval = 1.2345678901; double tstval; sscanf(strval1, "%lf", &tstval); if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); else printf("Pass: %lf %lf\n", tstval, dblval); sscanf(strval2, "%lf", &tstval); dblval = 123456.789; if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); else printf("Pass: %lf %lf\n", tstval, dblval); sscanf(strval3, "%lf", &tstval); dblval = 123456.789; if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); else printf("Pass: %lf %lf\n", tstval, dblval); sscanf(strval4, "%lf", &tstval); dblval = 0.000012345678; if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); else printf("Pass: %lf %lf\n", tstval, dblval); sscanf(strval5, "%lf", &tstval); dblval = 0.000012345678; if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); else printf("Pass: %lf %lf\n", tstval, dblval); return 0; } ''' if ftype == 'float': self.do_run(src.replace('%lf', '%f').replace('double', 'float'), '''Pass: 1.234568 1.234568 Pass: 123456.789063 123456.789063 Pass: 123456.789063 123456.789063 Pass: 0.000012 0.000012 Pass: 0.000012 0.000012''') else: self.do_run(src, '''Pass: 1.234568 1.234568 Pass: 123456.789000 123456.789000 Pass: 123456.789000 123456.789000 Pass: 0.000012 0.000012 Pass: 0.000012 0.000012''') def test_sscanf_n(self): src = r''' #include int main() { char *line = "version 1.0"; int i, l, lineno; char word[80]; if (sscanf(line, "%s%n", word, &l) != 1) { printf("Header format error, line %d\n", lineno); } printf("[DEBUG] word 1: %s, l: %d\n", word, l); int x = sscanf("one %n two", "%s %n", word, &l); printf("%d,%s,%d\n", x, word, l); { int a, b, c, count; count = sscanf("12345 6789", "%d %n%d", &a, &b, &c); printf("%i %i %i %i\n", count, a, b, c); } return 0; } ''' self.do_run(src, '''[DEBUG] word 1: version, l: 7\n1,one,4\n2 12345 6 6789\n''') def test_sscanf_whitespace(self): src = r''' #include int main() { short int x; short int y; const char* buffer[] = { "173,16", " 16,173", "183, 173", " 17, 287", " 98, 123, " }; for (int i=0; i<5; ++i) { sscanf(buffer[i], "%hd,%hd", &x, &y); printf("%d:%d,%d ", i, x, y); } return 0; } ''' self.do_run(src, '''0:173,16 1:16,173 2:183,173 3:17,287 4:98,123''') def test_sscanf_other_whitespace(self): Settings.SAFE_HEAP = 0 # use i16s in printf src = r''' #include int main() { short int x; short int y; const char* buffer[] = { "\t2\t3\t", /* TAB - horizontal tab */ "\t\t5\t\t7\t\t", "\n11\n13\n", /* LF - line feed */ "\n\n17\n\n19\n\n", "\v23\v29\v", /* VT - vertical tab */ "\v\v31\v\v37\v\v", "\f41\f43\f", /* FF - form feed */ "\f\f47\f\f53\f\f", "\r59\r61\r", /* CR - carrage return */ "\r\r67\r\r71\r\r" }; for (int i=0; i<10; ++i) { x = 0; y = 0; sscanf(buffer[i], " %d %d ", &x, &y); printf("%d, %d, ", x, y); } return 0; } ''' self.do_run(src, '''2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, ''') def test_sscanf_3(self): # i64 if not Settings.USE_TYPED_ARRAYS == 2: return self.skip('64-bit sscanf only supported in ta2') src = r''' #include #include int main(){ int64_t s, m, l; printf("%d\n", sscanf("123 1073741823 1125899906842620", "%lld %lld %lld", &s, &m, &l)); printf("%lld,%lld,%lld\n", s, m, l); int64_t negS, negM, negL; printf("%d\n", sscanf("-123 -1073741823 -1125899906842620", "%lld %lld %lld", &negS, &negM, &negL)); printf("%lld,%lld,%lld\n", negS, negM, negL); return 0; } ''' self.do_run(src, '3\n123,1073741823,1125899906842620\n' + '3\n-123,-1073741823,-1125899906842620\n') def test_sscanf_4(self): src = r''' #include int main() { char pYear[16], pMonth[16], pDay[16], pDate[64]; printf("%d\n", sscanf("Nov 19 2012", "%s%s%s", pMonth, pDay, pYear)); printf("day %s, month %s, year %s \n", pDay, pMonth, pYear); return(0); } ''' self.do_run(src, '3\nday 19, month Nov, year 2012'); def test_sscanf_5(self): src = r''' #include "stdio.h" static const char *colors[] = { " c black", ". c #001100", "X c #111100" }; int main(){ unsigned char code; char color[32]; int rcode; for(int i = 0; i < 3; i++) { rcode = sscanf(colors[i], "%c c %s", &code, color); printf("%i, %c, %s\n", rcode, code, color); } } ''' self.do_run(src, '2, , black\n2, ., #001100\n2, X, #111100'); def test_sscanf_6(self): src = r''' #include #include int main() { char *date = "18.07.2013w"; char c[10]; memset(c, 0, 10); int y, m, d, i; i = sscanf(date, "%d.%d.%4d%c", &d, &m, &y, c); printf("date: %s; day %2d, month %2d, year %4d, extra: %c, %d\n", date, d, m, y, c[0], i); i = sscanf(date, "%d.%d.%3c", &d, &m, c); printf("date: %s; day %2d, month %2d, year %4d, extra: %s, %d\n", date, d, m, y, c, i); } ''' self.do_run(src, '''date: 18.07.2013w; day 18, month 7, year 2013, extra: w, 4 date: 18.07.2013w; day 18, month 7, year 2013, extra: 201, 3 '''); def test_sscanf_skip(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip("need ta2 for full i64") src = r''' #include #include int main(){ int val1; printf("%d\n", sscanf("10 20 30 40", "%*lld %*d %d", &val1)); printf("%d\n", val1); int64_t large, val2; printf("%d\n", sscanf("1000000 -1125899906842620 -123 -1073741823", "%lld %*lld %ld %*d", &large, &val2)); printf("%lld,%d\n", large, val2); return 0; } ''' self.do_run(src, '1\n30\n2\n1000000,-123\n') def test_sscanf_caps(self): src = r''' #include "stdio.h" int main(){ unsigned int a; float e, f, g; sscanf("a 1.1 1.1 1.1", "%X %E %F %G", &a, &e, &f, &g); printf("%d %.1F %.1F %.1F\n", a, e, f, g); } ''' self.do_run(src, '10 1.1 1.1 1.1'); def test_sscanf_hex(self): src = r''' #include "stdio.h" int main(){ unsigned int a, b; sscanf("0x12AB 12AB", "%x %x", &a, &b); printf("%d %d\n", a, b); } ''' self.do_run(src, '4779 4779') def test_sscanf_float(self): src = r''' #include "stdio.h" int main(){ float f1, f2, f3, f4, f5, f6, f7, f8, f9; sscanf("0.512 0.250x5.129_-9.98 1.12*+54.32E3 +54.32E3^87.5E-3 87.5E-3$", "%f %fx%f_%f %f*%f %f^%f %f$", &f1, &f2, &f3, &f4, &f5, &f6, &f7, &f8, &f9); printf("\n%f, %f, %f, %f, %f, %f, %f, %f, %f\n", f1, f2, f3, f4, f5, f6, f7, f8, f9); } ''' self.do_run(src, '\n0.512000, 0.250000, 5.129000, -9.980000, 1.120000, 54320.000000, 54320.000000, 0.087500, 0.087500\n') def test_langinfo(self): src = open(path_from_root('tests', 'langinfo', 'test.c'), 'r').read() expected = open(path_from_root('tests', 'langinfo', 'output.txt'), 'r').read() self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/langinfo.h']) def test_files(self): self.banned_js_engines = [SPIDERMONKEY_ENGINE] # closure can generate variables called 'gc', which pick up js shell stuff if self.emcc_args is not None and '-O2' in self.emcc_args: self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff self.emcc_args = filter(lambda x: x != '-g', self.emcc_args) # ensure we test --closure 1 --memory-init-file 1 (-g would disable closure) self.emcc_args += ["-s", "CHECK_HEAP_ALIGN=0"] # disable heap align check here, it mixes poorly with closure Settings.CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both. post = ''' def process(filename): src = \'\'\' var Module = { 'noFSInit': true, 'preRun': function() { FS.createLazyFile('/', 'test.file', 'test.file', true, false); // Test FS_* exporting Module['FS_createDataFile']('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory var test_files_input = 'hi there!'; var test_files_input_index = 0; FS.init(function() { return test_files_input.charCodeAt(test_files_input_index++) || null; }); } }; \'\'\' + open(filename, 'r').read() open(filename, 'w').write(src) ''' other = open(os.path.join(self.get_dir(), 'test.file'), 'w') other.write('some data'); other.close() src = open(path_from_root('tests', 'files.cpp'), 'r').read() mem_file = 'src.cpp.o.js.mem' try_delete(mem_file) self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'), post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h']) if self.emcc_args and '--memory-init-file' in self.emcc_args: assert os.path.exists(mem_file) def test_files_m(self): # Test for Module.stdin etc. Settings.CORRECT_SIGNS = 1 post = ''' def process(filename): src = \'\'\' var data = [10, 20, 40, 30]; var Module = { stdin: function() { return data.pop() || null }, stdout: function(x) { Module.print('got: ' + x) } }; \'\'\' + open(filename, 'r').read() open(filename, 'w').write(src) ''' src = r''' #include #include int main () { char c; fprintf(stderr, "isatty? %d,%d,%d\n", isatty(fileno(stdin)), isatty(fileno(stdout)), isatty(fileno(stderr))); while ((c = fgetc(stdin)) != EOF) { putc(c+5, stdout); } return 0; } ''' self.do_run(src, ('got: 35\ngot: 45\ngot: 25\ngot: 15\nisatty? 0,0,1\n', 'isatty? 0,0,1\ngot: 35\ngot: 45\ngot: 25\ngot: 15\n'), post_build=post) def test_fwrite_0(self): src = r''' #include #include int main () { FILE *fh; fh = fopen("a.txt", "wb"); if (!fh) exit(1); fclose(fh); fh = fopen("a.txt", "rb"); if (!fh) exit(1); char data[] = "foobar"; size_t written = fwrite(data, 1, sizeof(data), fh); printf("written=%zu\n", written); } ''' self.do_run(src, 'written=0') def test_fgetc_ungetc(self): src = open(path_from_root('tests', 'stdio', 'test_fgetc_ungetc.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_fgetc_unsigned(self): if self.emcc_args is None: return self.skip('requires emcc') src = r''' #include int main() { FILE *file = fopen("file_with_byte_234.txt", "rb"); int c = fgetc(file); printf("*%d\n", c); } ''' open('file_with_byte_234.txt', 'wb').write('\xea') self.emcc_args += ['--embed-file', 'file_with_byte_234.txt'] self.do_run(src, '*234\n') def test_fgets_eol(self): if self.emcc_args is None: return self.skip('requires emcc') src = r''' #include char buf[32]; int main() { char *r = "SUCCESS"; FILE *f = fopen("eol.txt", "r"); while (fgets(buf, 32, f) != NULL) { if (buf[0] == '\0') { r = "FAIL"; break; } } printf("%s\n", r); fclose(f); return 0; } ''' open('eol.txt', 'wb').write('\n') self.emcc_args += ['--embed-file', 'eol.txt'] self.do_run(src, 'SUCCESS\n') def test_fscanf(self): if self.emcc_args is None: return self.skip('requires emcc') open(os.path.join(self.get_dir(), 'three_numbers.txt'), 'w').write('''-1 0.1 -.1''') src = r''' #include #include #include int main() { float x = FLT_MAX, y = FLT_MAX, z = FLT_MAX; FILE* fp = fopen("three_numbers.txt", "r"); if (fp) { int match = fscanf(fp, " %f %f %f ", &x, &y, &z); printf("match = %d\n", match); printf("x = %0.1f, y = %0.1f, z = %0.1f\n", x, y, z); } else { printf("failed to open three_numbers.txt\n"); } return 0; } ''' self.emcc_args += ['--embed-file', 'three_numbers.txt'] self.do_run(src, 'match = 3\nx = -1.0, y = 0.1, z = -0.1\n') def test_readdir(self): src = open(path_from_root('tests', 'dirent', 'test_readdir.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_stat(self): src = open(path_from_root('tests', 'stat', 'test_stat.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_stat_chmod(self): src = open(path_from_root('tests', 'stat', 'test_chmod.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_stat_mknod(self): src = open(path_from_root('tests', 'stat', 'test_mknod.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_fcntl(self): add_pre_run = ''' def process(filename): src = open(filename, 'r').read().replace( '// {{PRE_RUN_ADDITIONS}}', "FS.createDataFile('/', 'test', 'abcdef', true, true);" ) open(filename, 'w').write(src) ''' src = open(path_from_root('tests', 'fcntl', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'fcntl', 'output.txt'), 'r').read() self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_fcntl_open(self): src = open(path_from_root('tests', 'fcntl-open', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'fcntl-open', 'output.txt'), 'r').read() self.do_run(src, expected, force_c=True, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_fcntl_misc(self): add_pre_run = ''' def process(filename): src = open(filename, 'r').read().replace( '// {{PRE_RUN_ADDITIONS}}', "FS.createDataFile('/', 'test', 'abcdef', true, true);" ) open(filename, 'w').write(src) ''' src = open(path_from_root('tests', 'fcntl-misc', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'fcntl-misc', 'output.txt'), 'r').read() self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_poll(self): add_pre_run = ''' def process(filename): src = open(filename, 'r').read().replace( '// {{PRE_RUN_ADDITIONS}}', \'\'\' var dummy_device = FS.makedev(64, 0); FS.registerDevice(dummy_device, {}); FS.createDataFile('/', 'file', 'abcdef', true, true); FS.mkdev('/device', dummy_device); \'\'\' ) open(filename, 'w').write(src) ''' src = r''' #include #include #include #include int main() { struct pollfd multi[5]; multi[0].fd = open("/file", O_RDONLY, 0777); multi[1].fd = open("/device", O_RDONLY, 0777); multi[2].fd = 123; multi[3].fd = open("/file", O_RDONLY, 0777); multi[4].fd = open("/file", O_RDONLY, 0777); multi[0].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; multi[1].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; multi[2].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; multi[3].events = 0x00; multi[4].events = POLLOUT | POLLNVAL | POLLERR; printf("ret: %d\n", poll(multi, 5, 123)); printf("errno: %d\n", errno); printf("multi[0].revents: %d\n", multi[0].revents == (POLLIN | POLLOUT)); printf("multi[1].revents: %d\n", multi[1].revents == (POLLIN | POLLOUT)); printf("multi[2].revents: %d\n", multi[2].revents == POLLNVAL); printf("multi[3].revents: %d\n", multi[3].revents == 0); printf("multi[4].revents: %d\n", multi[4].revents == POLLOUT); return 0; } ''' expected = r''' ret: 4 errno: 0 multi[0].revents: 1 multi[1].revents: 1 multi[2].revents: 1 multi[3].revents: 1 multi[4].revents: 1 ''' self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h,poll.h']) def test_statvfs(self): src = r''' #include #include #include int main() { struct statvfs s; printf("result: %d\n", statvfs("/test", &s)); printf("errno: %d\n", errno); printf("f_bsize: %lu\n", s.f_bsize); printf("f_frsize: %lu\n", s.f_frsize); printf("f_blocks: %lu\n", s.f_blocks); printf("f_bfree: %lu\n", s.f_bfree); printf("f_bavail: %lu\n", s.f_bavail); printf("f_files: %d\n", s.f_files > 5); printf("f_ffree: %lu\n", s.f_ffree); printf("f_favail: %lu\n", s.f_favail); printf("f_fsid: %lu\n", s.f_fsid); printf("f_flag: %lu\n", s.f_flag); printf("f_namemax: %lu\n", s.f_namemax); return 0; } ''' expected = r''' result: 0 errno: 0 f_bsize: 4096 f_frsize: 4096 f_blocks: 1000000 f_bfree: 500000 f_bavail: 500000 f_files: 1 f_ffree: 1000000 f_favail: 1000000 f_fsid: 42 f_flag: 2 f_namemax: 255 ''' self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) def test_libgen(self): src = r''' #include #include int main() { char p1[16] = "/usr/lib", p1x[16] = "/usr/lib"; printf("%s -> ", p1); printf("%s : %s\n", dirname(p1x), basename(p1)); char p2[16] = "/usr", p2x[16] = "/usr"; printf("%s -> ", p2); printf("%s : %s\n", dirname(p2x), basename(p2)); char p3[16] = "/usr/", p3x[16] = "/usr/"; printf("%s -> ", p3); printf("%s : %s\n", dirname(p3x), basename(p3)); char p4[16] = "/usr/lib///", p4x[16] = "/usr/lib///"; printf("%s -> ", p4); printf("%s : %s\n", dirname(p4x), basename(p4)); char p5[16] = "/", p5x[16] = "/"; printf("%s -> ", p5); printf("%s : %s\n", dirname(p5x), basename(p5)); char p6[16] = "///", p6x[16] = "///"; printf("%s -> ", p6); printf("%s : %s\n", dirname(p6x), basename(p6)); char p7[16] = "/usr/../lib/..", p7x[16] = "/usr/../lib/.."; printf("%s -> ", p7); printf("%s : %s\n", dirname(p7x), basename(p7)); char p8[16] = "", p8x[16] = ""; printf("(empty) -> %s : %s\n", dirname(p8x), basename(p8)); printf("(null) -> %s : %s\n", dirname(0), basename(0)); return 0; } ''' expected = ''' /usr/lib -> /usr : lib /usr -> / : usr /usr/ -> / : usr /usr/lib/// -> /usr : lib / -> / : / /// -> / : / /usr/../lib/.. -> /usr/../lib : .. (empty) -> . : . (null) -> . : . ''' self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) def test_utime(self): src = open(path_from_root('tests', 'utime', 'test_utime.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_utf(self): self.banned_js_engines = [SPIDERMONKEY_ENGINE] # only node handles utf well Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc'] src = r''' #include #include int main() { char *c = "μ†ℱ ╋ℯ╳╋ 😇"; printf("%d %d %d %d %s\n", c[0]&0xff, c[1]&0xff, c[2]&0xff, c[3]&0xff, c); emscripten_run_script( "cheez = _malloc(100);" "Module.writeStringToMemory(\"μ†ℱ ╋ℯ╳╋ 😇\", cheez);" "Module.print([Pointer_stringify(cheez), Module.getValue(cheez, 'i8')&0xff, Module.getValue(cheez+1, 'i8')&0xff, Module.getValue(cheez+2, 'i8')&0xff, Module.getValue(cheez+3, 'i8')&0xff, ]);"); } ''' self.do_run(src, '206 188 226 128 μ†ℱ ╋ℯ╳╋ 😇\nμ†ℱ ╋ℯ╳╋ 😇,206,188,226,128\n'); def test_utf32(self): if self.emcc_args is None: return self.skip('need libc for wcslen()') if not self.is_le32(): return self.skip('this test uses inline js, which requires le32') self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.') self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.', args=['-fshort-wchar']) def test_direct_string_constant_usage(self): if self.emcc_args is None: return self.skip('requires libcxx') src = ''' #include template void printText( const char (&text)[ i ] ) { std::cout << text; } int main() { printText( "some string constant" ); return 0; } ''' self.do_run(src, "some string constant") def test_std_cout_new(self): if self.emcc_args is None: return self.skip('requires emcc') src = ''' #include struct NodeInfo { //structure that we want to transmit to our shaders float x; float y; float s; float c; }; const int nbNodes = 100; NodeInfo * data = new NodeInfo[nbNodes]; //our data that will be transmitted using float texture. template void printText( const char (&text)[ i ] ) { std::cout << text << std::endl; } int main() { printText( "some string constant" ); return 0; } ''' self.do_run(src, "some string constant") def test_istream(self): if self.emcc_args is None: return self.skip('requires libcxx') src = ''' #include #include #include int main() { std::string mystring("1 2 3"); std::istringstream is(mystring); int one, two, three; is >> one >> two >> three; printf( "%i %i %i", one, two, three ); } ''' for linkable in [0]:#, 1]: print linkable Settings.LINKABLE = linkable # regression check for issue #273 self.do_run(src, "1 2 3") def test_fs_base(self): Settings.INCLUDE_FULL_LIBRARY = 1 try: addJS = ''' def process(filename): import tools.shared as shared src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours '// {{PRE_RUN_ADDITIONS}}', open(shared.path_from_root('tests', 'filesystem', 'src.js'), 'r').read()) open(filename, 'w').write(src) ''' src = 'int main() {return 0;}\n' expected = open(path_from_root('tests', 'filesystem', 'output.txt'), 'r').read() self.do_run(src, expected, post_build=addJS, extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) finally: Settings.INCLUDE_FULL_LIBRARY = 0 def test_fs_nodefs_rw(self): if self.emcc_args is None: return self.skip('requires emcc') if not self.is_le32(): return self.skip('le32 needed for inline js') src = open(path_from_root('tests', 'fs', 'test_nodefs_rw.c'), 'r').read() self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS]) def test_unistd_access(self): self.clear() if not self.is_le32(): return self.skip('le32 needed for inline js') for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read() Building.COMPILER_TEST_OPTS += ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_curdir(self): if not self.is_le32(): return self.skip('le32 needed for inline js') src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read() self.do_run(src, expected) def test_unistd_close(self): src = open(path_from_root('tests', 'unistd', 'close.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'close.out'), 'r').read() self.do_run(src, expected) def test_unistd_confstr(self): src = open(path_from_root('tests', 'unistd', 'confstr.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'confstr.out'), 'r').read() self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/unistd.h']) def test_unistd_ttyname(self): src = open(path_from_root('tests', 'unistd', 'ttyname.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_unistd_dup(self): src = open(path_from_root('tests', 'unistd', 'dup.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'dup.out'), 'r').read() self.do_run(src, expected) def test_unistd_pathconf(self): src = open(path_from_root('tests', 'unistd', 'pathconf.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'pathconf.out'), 'r').read() self.do_run(src, expected) def test_unistd_truncate(self): self.clear() if not self.is_le32(): return self.skip('le32 needed for inline js') for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read() Building.COMPILER_TEST_OPTS += ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_swab(self): src = open(path_from_root('tests', 'unistd', 'swab.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'swab.out'), 'r').read() self.do_run(src, expected) def test_unistd_isatty(self): src = open(path_from_root('tests', 'unistd', 'isatty.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_unistd_sysconf(self): src = open(path_from_root('tests', 'unistd', 'sysconf.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'sysconf.out'), 'r').read() self.do_run(src, expected) def test_unistd_login(self): src = open(path_from_root('tests', 'unistd', 'login.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'login.out'), 'r').read() self.do_run(src, expected) def test_unistd_unlink(self): self.clear() if self.emcc_args is None: return self.skip('requires emcc') if not self.is_le32(): return self.skip('le32 needed for inline js') for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read() Building.COMPILER_TEST_OPTS += ['-D' + fs] self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS]) def test_unistd_links(self): self.clear() if not self.is_le32(): return self.skip('le32 needed for inline js') for fs in ['MEMFS', 'NODEFS']: if WINDOWS and fs == 'NODEFS': print >> sys.stderr, 'Skipping NODEFS part of this test for test_unistd_links on Windows, since it would require administrative privileges.' # Also, other detected discrepancies if you do end up running this test on NODEFS: # test expects /, but Windows gives \ as path slashes. # Calling readlink() on a non-link gives error 22 EINVAL on Unix, but simply error 0 OK on Windows. continue src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read() Building.COMPILER_TEST_OPTS += ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_sleep(self): src = open(path_from_root('tests', 'unistd', 'sleep.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'sleep.out'), 'r').read() self.do_run(src, expected) def test_unistd_io(self): self.clear() if not self.is_le32(): return self.skip('le32 needed for inline js') if self.run_name == 'o2': return self.skip('non-asm optimized builds can fail with inline js') if self.emcc_args is None: return self.skip('requires emcc') for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read() Building.COMPILER_TEST_OPTS += ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_misc(self): if self.emcc_args is None: return self.skip('requires emcc') if not self.is_le32(): return self.skip('le32 needed for inline js') for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'misc.out'), 'r').read() Building.COMPILER_TEST_OPTS += ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_uname(self): src = r''' #include #include int main() { struct utsname u; printf("ret: %d\n", uname(&u)); printf("sysname: %s\n", u.sysname); printf("nodename: %s\n", u.nodename); printf("release: %s\n", u.release); printf("version: %s\n", u.version); printf("machine: %s\n", u.machine); printf("invalid: %d\n", uname(0)); return 0; } ''' expected = ''' ret: 0 sysname: Emscripten nodename: emscripten release: 1.0 version: #1 machine: x86-JS ''' self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) def test_env(self): src = open(path_from_root('tests', 'env', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'env', 'output.txt'), 'r').read() self.do_run(src, expected) def test_systypes(self): src = open(path_from_root('tests', 'systypes', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'systypes', 'output.txt'), 'r').read() self.do_run(src, expected) def test_getloadavg(self): src = r''' #include #include int main() { double load[5] = {42.13, 42.13, 42.13, 42.13, 42.13}; printf("ret: %d\n", getloadavg(load, 5)); printf("load[0]: %.3lf\n", load[0]); printf("load[1]: %.3lf\n", load[1]); printf("load[2]: %.3lf\n", load[2]); printf("load[3]: %.3lf\n", load[3]); printf("load[4]: %.3lf\n", load[4]); return 0; } ''' expected = ''' ret: 3 load[0]: 0.100 load[1]: 0.100 load[2]: 0.100 load[3]: 42.130 load[4]: 42.130 ''' self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) def test_799(self): src = open(path_from_root('tests', '799.cpp'), 'r').read() self.do_run(src, '''Set PORT family: 0, port: 3979 Get PORT family: 0 PORT: 3979 ''') def test_ctype(self): # The bit fiddling done by the macros using __ctype_b_loc requires this. Settings.CORRECT_SIGNS = 1 src = open(path_from_root('tests', 'ctype', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'ctype', 'output.txt'), 'r').read() self.do_run(src, expected) def test_strcasecmp(self): src = r''' #include #include int sign(int x) { if (x < 0) return -1; if (x > 0) return 1; return 0; } int main() { printf("*\n"); printf("%d\n", sign(strcasecmp("hello", "hello"))); printf("%d\n", sign(strcasecmp("hello1", "hello"))); printf("%d\n", sign(strcasecmp("hello", "hello1"))); printf("%d\n", sign(strcasecmp("hello1", "hello1"))); printf("%d\n", sign(strcasecmp("iello", "hello"))); printf("%d\n", sign(strcasecmp("hello", "iello"))); printf("%d\n", sign(strcasecmp("A", "hello"))); printf("%d\n", sign(strcasecmp("Z", "hello"))); printf("%d\n", sign(strcasecmp("a", "hello"))); printf("%d\n", sign(strcasecmp("z", "hello"))); printf("%d\n", sign(strcasecmp("hello", "a"))); printf("%d\n", sign(strcasecmp("hello", "z"))); printf("%d\n", sign(strcasecmp("Hello", "hello"))); printf("%d\n", sign(strcasecmp("Hello1", "hello"))); printf("%d\n", sign(strcasecmp("Hello", "hello1"))); printf("%d\n", sign(strcasecmp("Hello1", "hello1"))); printf("%d\n", sign(strcasecmp("Iello", "hello"))); printf("%d\n", sign(strcasecmp("Hello", "iello"))); printf("%d\n", sign(strcasecmp("A", "hello"))); printf("%d\n", sign(strcasecmp("Z", "hello"))); printf("%d\n", sign(strcasecmp("a", "hello"))); printf("%d\n", sign(strcasecmp("z", "hello"))); printf("%d\n", sign(strcasecmp("Hello", "a"))); printf("%d\n", sign(strcasecmp("Hello", "z"))); printf("%d\n", sign(strcasecmp("hello", "Hello"))); printf("%d\n", sign(strcasecmp("hello1", "Hello"))); printf("%d\n", sign(strcasecmp("hello", "Hello1"))); printf("%d\n", sign(strcasecmp("hello1", "Hello1"))); printf("%d\n", sign(strcasecmp("iello", "Hello"))); printf("%d\n", sign(strcasecmp("hello", "Iello"))); printf("%d\n", sign(strcasecmp("A", "Hello"))); printf("%d\n", sign(strcasecmp("Z", "Hello"))); printf("%d\n", sign(strcasecmp("a", "Hello"))); printf("%d\n", sign(strcasecmp("z", "Hello"))); printf("%d\n", sign(strcasecmp("hello", "a"))); printf("%d\n", sign(strcasecmp("hello", "z"))); printf("%d\n", sign(strcasecmp("Hello", "Hello"))); printf("%d\n", sign(strcasecmp("Hello1", "Hello"))); printf("%d\n", sign(strcasecmp("Hello", "Hello1"))); printf("%d\n", sign(strcasecmp("Hello1", "Hello1"))); printf("%d\n", sign(strcasecmp("Iello", "Hello"))); printf("%d\n", sign(strcasecmp("Hello", "Iello"))); printf("%d\n", sign(strcasecmp("A", "Hello"))); printf("%d\n", sign(strcasecmp("Z", "Hello"))); printf("%d\n", sign(strcasecmp("a", "Hello"))); printf("%d\n", sign(strcasecmp("z", "Hello"))); printf("%d\n", sign(strcasecmp("Hello", "a"))); printf("%d\n", sign(strcasecmp("Hello", "z"))); printf("%d\n", sign(strncasecmp("hello", "hello", 3))); printf("%d\n", sign(strncasecmp("hello1", "hello", 3))); printf("%d\n", sign(strncasecmp("hello", "hello1", 3))); printf("%d\n", sign(strncasecmp("hello1", "hello1", 3))); printf("%d\n", sign(strncasecmp("iello", "hello", 3))); printf("%d\n", sign(strncasecmp("hello", "iello", 3))); printf("%d\n", sign(strncasecmp("A", "hello", 3))); printf("%d\n", sign(strncasecmp("Z", "hello", 3))); printf("%d\n", sign(strncasecmp("a", "hello", 3))); printf("%d\n", sign(strncasecmp("z", "hello", 3))); printf("%d\n", sign(strncasecmp("hello", "a", 3))); printf("%d\n", sign(strncasecmp("hello", "z", 3))); printf("*\n"); return 0; } ''' self.do_run(src, '''*\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n0\n0\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n*\n''') def test_atomic(self): src = ''' #include int main() { int x = 10; int y = __sync_add_and_fetch(&x, 5); printf("*%d,%d*\\n", x, y); x = 10; y = __sync_fetch_and_add(&x, 5); printf("*%d,%d*\\n", x, y); x = 10; y = __sync_lock_test_and_set(&x, 6); printf("*%d,%d*\\n", x, y); x = 10; y = __sync_bool_compare_and_swap(&x, 9, 7); printf("*%d,%d*\\n", x, y); y = __sync_bool_compare_and_swap(&x, 10, 7); printf("*%d,%d*\\n", x, y); return 0; } ''' self.do_run(src, '*15,15*\n*15,10*\n*6,10*\n*10,0*\n*7,1*') def test_phiundef(self): src = r''' #include #include static int state; struct my_struct { union { struct { unsigned char a; unsigned char b; } c; unsigned int d; } e; unsigned int f; }; int main(int argc, char **argv) { struct my_struct r; state = 0; for (int i=0;i int main() { std::cout << "hello world" << std::endl << 77 << "." << std::endl; return 0; } ''' # FIXME: should not have so many newlines in output here self.do_run(src, 'hello world\n77.\n') def test_stdvec(self): if self.emcc_args is None: return self.skip('requires emcc') src = ''' #include #include struct S { int a; float b; }; void foo(int a, float b) { printf("%d:%.2f\\n", a, b); } int main ( int argc, char *argv[] ) { std::vector ar; S s; s.a = 789; s.b = 123.456f; ar.push_back(s); s.a = 0; s.b = 100.1f; ar.push_back(s); foo(ar[0].a, ar[0].b); foo(ar[1].a, ar[1].b); } ''' self.do_run(src, '789:123.46\n0:100.1') def test_reinterpreted_ptrs(self): if self.emcc_args is None: return self.skip('needs emcc and libc') src = r''' #include class Foo { private: float bar; public: int baz; Foo(): bar(0), baz(4711) {}; int getBar() const; }; int Foo::getBar() const { return this->bar; }; const Foo *magic1 = reinterpret_cast(0xDEAD111F); const Foo *magic2 = reinterpret_cast(0xDEAD888F); static void runTest() { const Foo *a = new Foo(); const Foo *b = a; if (a->getBar() == 0) { if (a->baz == 4712) b = magic1; else b = magic2; } printf("%s\n", (b == magic1 ? "magic1" : (b == magic2 ? "magic2" : "neither"))); }; extern "C" { int main(int argc, char **argv) { runTest(); } } ''' self.do_run(src, 'magic2') def test_jansson(self): return self.skip('currently broken') if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') if Settings.SAFE_HEAP: return self.skip('jansson is not safe-heap safe') src = ''' #include #include #include int main() { const char* jsonString = "{\\"key\\": \\"value\\",\\"array\\": [\\"array_item1\\",\\"array_item2\\",\\"array_item3\\"],\\"dict\\":{\\"number\\": 3,\\"float\\": 2.2}}"; json_error_t error; json_t *root = json_loadb(jsonString, strlen(jsonString), 0, &error); if(!root) { printf("Node `root` is `null`."); return 0; } if(!json_is_object(root)) { printf("Node `root` is no object."); return 0; } printf("%s\\n", json_string_value(json_object_get(root, "key"))); json_t *array = json_object_get(root, "array"); if(!array) { printf("Node `array` is `null`."); return 0; } if(!json_is_array(array)) { printf("Node `array` is no array."); return 0; } for(size_t i=0; i1) def test_raytrace(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.USE_TYPED_ARRAYS == 2: return self.skip('Relies on double value rounding, extremely sensitive') src = open(path_from_root('tests', 'raytrace.cpp'), 'r').read().replace('double', 'float') output = open(path_from_root('tests', 'raytrace.ppm'), 'r').read() self.do_run(src, output, ['3', '16'])#, build_ll_hook=self.do_autodebug) def test_fasta(self): if self.emcc_args is None: return self.skip('requires emcc') results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''), (50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ] for precision in [0, 1, 2]: Settings.PRECISE_F32 = precision for t in ['float', 'double']: print precision, t src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', t) for i, j in results: self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1) shutil.copyfile('src.cpp.o.js', '%d_%s.js' % (precision, t)) def test_whets(self): if not Settings.ASM_JS: return self.skip('mainly a test for asm validation here') self.do_run(open(path_from_root('tests', 'whets.cpp')).read(), 'Single Precision C Whetstone Benchmark') def test_dlmalloc(self): if self.emcc_args is None: self.emcc_args = [] # dlmalloc auto-inclusion is only done if we use emcc self.banned_js_engines = [NODE_JS] # slower, and fail on 64-bit Settings.CORRECT_SIGNS = 2 Settings.CORRECT_SIGNS_LINES = ['src.cpp:' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]] Settings.TOTAL_MEMORY = 128*1024*1024 # needed with typed arrays src = open(path_from_root('system', 'lib', 'dlmalloc.c'), 'r').read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read() self.do_run(src, '*1,0*', ['200', '1']) self.do_run(src, '*400,0*', ['400', '400'], no_build=True) # Linked version src = open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read() self.do_run(src, '*1,0*', ['200', '1'], extra_emscripten_args=['-m']) self.do_run(src, '*400,0*', ['400', '400'], extra_emscripten_args=['-m'], no_build=True) if self.emcc_args == []: # TODO: do this in other passes too, passing their opts into emcc # emcc should build in dlmalloc automatically, and do all the sign correction etc. for it try_delete(os.path.join(self.get_dir(), 'src.cpp.o.js')) output = Popen([PYTHON, EMCC, path_from_root('tests', 'dlmalloc_test.c'), '-s', 'TOTAL_MEMORY=' + str(128*1024*1024), '-o', os.path.join(self.get_dir(), 'src.cpp.o.js')], stdout=PIPE, stderr=self.stderr_redirect).communicate() self.do_run('x', '*1,0*', ['200', '1'], no_build=True) self.do_run('x', '*400,0*', ['400', '400'], no_build=True) # The same for new and all its variants src = open(path_from_root('tests', 'new.cpp')).read() for new, delete in [ ('malloc(100)', 'free'), ('new char[100]', 'delete[]'), ('new Structy', 'delete'), ('new int', 'delete'), ('new Structy[10]', 'delete[]'), ]: self.do_run(src.replace('{{{ NEW }}}', new).replace('{{{ DELETE }}}', delete), '*1,0*') def test_dlmalloc_partial(self): if self.emcc_args is None: return self.skip('only emcc will link in dlmalloc') # present part of the symbols of dlmalloc, not all src = open(path_from_root('tests', 'new.cpp')).read().replace('{{{ NEW }}}', 'new int').replace('{{{ DELETE }}}', 'delete') + ''' void * operator new(size_t size) { printf("new %d!\\n", size); return malloc(size); } ''' self.do_run(src, 'new 4!\n*1,0*') def test_dlmalloc_partial_2(self): if self.emcc_args is None or 'SAFE_HEAP' in str(self.emcc_args) or 'CHECK_HEAP_ALIGN' in str(self.emcc_args): return self.skip('only emcc will link in dlmalloc, and we do unsafe stuff') # present part of the symbols of dlmalloc, not all. malloc is harder to link than new which is weak. src = r''' #include #include void *malloc(size_t size) { return (void*)123; } int main() { void *x = malloc(10); printf("got %p\n", x); free(x); printf("freed the faker\n"); return 1; } ''' self.do_run(src, 'got 0x7b\nfreed') def test_libcxx(self): if self.emcc_args is None: return self.skip('requires emcc') self.do_run(open(path_from_root('tests', 'hashtest.cpp')).read(), 'june -> 30\nPrevious (in alphabetical order) is july\nNext (in alphabetical order) is march') self.do_run(''' #include #include int main() { std::set *fetchOriginatorNums = new std::set(); fetchOriginatorNums->insert(171); printf("hello world\\n"); return 0; } ''', 'hello world'); def test_typeid(self): self.do_run(r''' #include #include #include int main() { printf("*\n"); #define MAX 100 int ptrs[MAX]; int groups[MAX]; memset(ptrs, 0, MAX*sizeof(int)); memset(groups, 0, MAX*sizeof(int)); int next_group = 1; #define TEST(X) { \ int ptr = (int)&typeid(X); \ int group = 0; \ int i; \ for (i = 0; i < MAX; i++) { \ if (!groups[i]) break; \ if (ptrs[i] == ptr) { \ group = groups[i]; \ break; \ } \ } \ if (!group) { \ groups[i] = group = next_group++; \ ptrs[i] = ptr; \ } \ printf("%s:%d\n", #X, group); \ } TEST(int); TEST(unsigned int); TEST(unsigned); TEST(signed int); TEST(long); TEST(unsigned long); TEST(signed long); TEST(long long); TEST(unsigned long long); TEST(signed long long); TEST(short); TEST(unsigned short); TEST(signed short); TEST(char); TEST(unsigned char); TEST(signed char); TEST(float); TEST(double); TEST(long double); TEST(void); TEST(void*); printf("*\n"); } ''', '''* int:1 unsigned int:2 unsigned:2 signed int:1 long:3 unsigned long:4 signed long:3 long long:5 unsigned long long:6 signed long long:5 short:7 unsigned short:8 signed short:7 char:9 unsigned char:10 signed char:11 float:12 double:13 long double:14 void:15 void*:16 * '''); def test_static_variable(self): if self.emcc_args is None: Settings.SAFE_HEAP = 0 # LLVM mixes i64 and i8 in the guard check src = ''' #include struct DATA { int value; DATA() { value = 0; } }; DATA & GetData() { static DATA data; return data; } int main() { GetData().value = 10; printf( "value:%i", GetData().value ); } ''' self.do_run(src, 'value:10') def test_fakestat(self): src = r''' #include struct stat { int x, y; }; int main() { stat s; s.x = 10; s.y = 22; printf("*%d,%d*\n", s.x, s.y); } ''' self.do_run(src, '*10,22*') def test_mmap(self): if self.emcc_args is None: return self.skip('requires emcc') Settings.TOTAL_MEMORY = 128*1024*1024 src = ''' #include #include #include int main(int argc, char *argv[]) { for (int i = 0; i < 10; i++) { int* map = (int*)mmap(0, 5000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); /* TODO: Should we align to 4k? assert(((int)map) % 4096 == 0); // aligned */ assert(munmap(map, 5000) == 0); } const int NUM_BYTES = 8 * 1024 * 1024; const int NUM_INTS = NUM_BYTES / sizeof(int); int* map = (int*)mmap(0, NUM_BYTES, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); assert(map != MAP_FAILED); int i; for (i = 0; i < NUM_INTS; i++) { map[i] = i; } for (i = 0; i < NUM_INTS; i++) { assert(map[i] == i); } assert(munmap(map, NUM_BYTES) == 0); printf("hello,world"); return 0; } ''' self.do_run(src, 'hello,world') self.do_run(src, 'hello,world', force_c=True) def test_mmap_file(self): if self.emcc_args is None: return self.skip('requires emcc') for extra_args in [[], ['--no-heap-copy']]: self.emcc_args += ['--embed-file', 'data.dat'] + extra_args open(self.in_dir('data.dat'), 'w').write('data from the file ' + ('.' * 9000)) src = open(path_from_root('tests', 'mmap_file.c')).read() self.do_run(src, '*\ndata from the file .\nfrom the file ......\n*\n') def test_cubescript(self): if self.emcc_args is None: return self.skip('requires emcc') if self.run_name == 'o2': self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code... # Overflows happen in hash loop Settings.CORRECT_OVERFLOWS = 1 Settings.CHECK_OVERFLOWS = 0 if Settings.USE_TYPED_ARRAYS == 2: Settings.CORRECT_SIGNS = 1 self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') assert 'asm2g' in test_modes if self.run_name == 'asm2g': results = {} original = open('src.cpp.o.js').read() results[Settings.ALIASING_FUNCTION_POINTERS] = len(original) Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') final = open('src.cpp.o.js').read() results[Settings.ALIASING_FUNCTION_POINTERS] = len(final) open('original.js', 'w').write(original) print results assert results[1] < 0.99*results[0] assert ' & 3]()' in original, 'small function table exists' assert ' & 3]()' not in final, 'small function table does not exist' assert ' & 255]()' not in original, 'big function table does not exist' assert ' & 255]()' in final, 'big function table exists' assert 'asm1' in test_modes if self.run_name == 'asm1': generated = open('src.cpp.o.js').read() main = generated[generated.find('function runPostSets'):] main = main[:main.find('\n}')] assert main.count('\n') == 7, 'must not emit too many postSets: %d' % main.count('\n') def test_simd(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate src = r''' #include #include static inline float32x4 __attribute__((always_inline)) _mm_set_ps(const float __Z, const float __Y, const float __X, const float __W) { return (float32x4){ __W, __X, __Y, __Z }; } static __inline__ float32x4 __attribute__((__always_inline__)) _mm_setzero_ps(void) { return (float32x4){ 0.0, 0.0, 0.0, 0.0 }; } int main(int argc, char **argv) { float data[8]; for (int i = 0; i < 32; i++) data[i] = (1+i+argc)*(2+i+argc*argc); // confuse optimizer { float32x4 *a = (float32x4*)&data[0]; float32x4 *b = (float32x4*)&data[4]; float32x4 c, d; c = *a; d = *b; printf("1floats! %d, %d, %d, %d %d, %d, %d, %d\n", (int)c[0], (int)c[1], (int)c[2], (int)c[3], (int)d[0], (int)d[1], (int)d[2], (int)d[3]); c = c+d; printf("2floats! %d, %d, %d, %d %d, %d, %d, %d\n", (int)c[0], (int)c[1], (int)c[2], (int)c[3], (int)d[0], (int)d[1], (int)d[2], (int)d[3]); d = c*d; printf("3floats! %d, %d, %d, %d %d, %d, %d, %d\n", (int)c[0], (int)c[1], (int)c[2], (int)c[3], (int)d[0], (int)d[1], (int)d[2], (int)d[3]); c = _mm_setzero_ps(); printf("zeros %d, %d, %d, %d\n", (int)c[0], (int)c[1], (int)c[2], (int)c[3]); } { uint32x4 *a = (uint32x4*)&data[0]; uint32x4 *b = (uint32x4*)&data[4]; uint32x4 c, d, e, f; c = *a; d = *b; printf("4uints! %d, %d, %d, %d %d, %d, %d, %d\n", c[0], c[1], c[2], c[3], d[0], d[1], d[2], d[3]); e = c+d; f = c-d; printf("5uints! %d, %d, %d, %d %d, %d, %d, %d\n", e[0], e[1], e[2], e[3], f[0], f[1], f[2], f[3]); e = c&d; f = c|d; e = ~c&d; f = c^d; printf("5uintops! %d, %d, %d, %d %d, %d, %d, %d\n", e[0], e[1], e[2], e[3], f[0], f[1], f[2], f[3]); } { float32x4 c, d, e, f; c = _mm_set_ps(9.0, 4.0, 0, -9.0); d = _mm_set_ps(10.0, 14.0, -12, -2.0); printf("6floats! %d, %d, %d, %d %d, %d, %d, %d\n", (int)c[0], (int)c[1], (int)c[2], (int)c[3], (int)d[0], (int)d[1], (int)d[2], (int)d[3]); printf("7calcs: %d\n", emscripten_float32x4_signmask(c)); // TODO: just not just compilation but output as well } return 0; } ''' self.do_run(src, '''1floats! 6, 12, 20, 30 42, 56, 72, 90 2floats! 48, 68, 92, 120 42, 56, 72, 90 3floats! 48, 68, 92, 120 2016, 3808, 6624, 10800 zeros 0, 0, 0, 0 4uints! 1086324736, 1094713344, 1101004800, 1106247680 1109917696, 1113587712, 1116733440, 1119092736 5uints! -2098724864, -2086666240, -2077229056, -2069626880 -23592960, -18874368, -15728640, -12845056 5uintops! 36175872, 35651584, 34603008, 33816576 48758784, 52428800, 53477376, 54788096 6floats! -9, 0, 4, 9 -2, -12, 14, 10 ''') def test_simd2(self): if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate self.do_run(r''' #include typedef float __m128 __attribute__ ((__vector_size__ (16))); static inline __m128 __attribute__((always_inline)) _mm_set_ps(const float __Z, const float __Y, const float __X, const float __W) { return (__m128){ __W, __X, __Y, __Z }; } static inline void __attribute__((always_inline)) _mm_store_ps(float *__P, __m128 __A) { *(__m128 *)__P = __A; } static inline __m128 __attribute__((always_inline)) _mm_add_ps(__m128 __A, __m128 __B) { return __A + __B; } using namespace std; int main(int argc, char ** argv) { float __attribute__((__aligned__(16))) ar[4]; __m128 v1 = _mm_set_ps(9.0, 4.0, 0, -9.0); __m128 v2 = _mm_set_ps(7.0, 3.0, 2.5, 1.0); __m128 v3 = _mm_add_ps(v1, v2); _mm_store_ps(ar, v3); for (int i = 0; i < 4; i++) { printf("%f\n", ar[i]); } return 0; } ''', '''-8.000000 2.500000 7.000000 16.000000 ''') def test_gcc_unmangler(self): Settings.NAMED_GLOBALS = 1 # test coverage for this Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('third_party')] self.do_run(open(path_from_root('third_party', 'gcc_demangler.c')).read(), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj']) #### Code snippet that is helpful to search for nonportable optimizations #### #global LLVM_OPT_OPTS #for opt in ['-aa-eval', '-adce', '-always-inline', '-argpromotion', '-basicaa', '-basiccg', '-block-placement', '-break-crit-edges', '-codegenprepare', '-constmerge', '-constprop', '-correlated-propagation', '-count-aa', '-dce', '-deadargelim', '-deadtypeelim', '-debug-aa', '-die', '-domfrontier', '-domtree', '-dse', '-extract-blocks', '-functionattrs', '-globaldce', '-globalopt', '-globalsmodref-aa', '-gvn', '-indvars', '-inline', '-insert-edge-profiling', '-insert-optimal-edge-profiling', '-instcombine', '-instcount', '-instnamer', '-internalize', '-intervals', '-ipconstprop', '-ipsccp', '-iv-users', '-jump-threading', '-lazy-value-info', '-lcssa', '-lda', '-libcall-aa', '-licm', '-lint', '-live-values', '-loop-deletion', '-loop-extract', '-loop-extract-single', '-loop-index-split', '-loop-reduce', '-loop-rotate', '-loop-unroll', '-loop-unswitch', '-loops', '-loopsimplify', '-loweratomic', '-lowerinvoke', '-lowersetjmp', '-lowerswitch', '-mem2reg', '-memcpyopt', '-memdep', '-mergefunc', '-mergereturn', '-module-debuginfo', '-no-aa', '-no-profile', '-partial-inliner', '-partialspecialization', '-pointertracking', '-postdomfrontier', '-postdomtree', '-preverify', '-prune-eh', '-reassociate', '-reg2mem', '-regions', '-scalar-evolution', '-scalarrepl', '-sccp', '-scev-aa', '-simplify-libcalls', '-simplify-libcalls-halfpowr', '-simplifycfg', '-sink', '-split-geps', '-sretpromotion', '-strip', '-strip-dead-debug-info', '-strip-dead-prototypes', '-strip-debug-declare', '-strip-nondebug', '-tailcallelim', '-tailduplicate', '-targetdata', '-tbaa']: # LLVM_OPT_OPTS = [opt] # try: # self.do_run(path_from_root(['third_party']), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c') # print opt, "ok" # except: # print opt, "FAIL" def test_lua(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') self.do_run('', 'hello lua world!\n17\n1\n2\n3\n4\n7', args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''], libraries=self.get_library('lua', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None), includes=[path_from_root('tests', 'lua')], output_nicerizer=lambda string, err: (string + err).replace('\n\n', '\n').replace('\n\n', '\n')) def get_freetype(self): Settings.DEAD_FUNCTIONS += ['_inflateEnd', '_inflate', '_inflateReset', '_inflateInit2_'] return self.get_library('freetype', os.path.join('objs', '.libs', 'libfreetype.a')) def test_freetype(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix') assert 'asm2g' in test_modes if self.run_name == 'asm2g': Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS # flip for some more coverage here if Settings.CORRECT_SIGNS == 0: Settings.CORRECT_SIGNS = 1 # Not sure why, but needed post = ''' def process(filename): import tools.shared as shared # Embed the font into the document src = open(filename, 'r').read().replace( '// {{PRE_RUN_ADDITIONS}}', "FS.createDataFile('/', 'font.ttf', %s, true, false);" % str( map(ord, open(shared.path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read()) ) ) open(filename, 'w').write(src) ''' # Not needed for js, but useful for debugging shutil.copyfile(path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), os.path.join(self.get_dir(), 'font.ttf')) # Main for outlining in [0, 5000]: Settings.OUTLINING_LIMIT = outlining print >> sys.stderr, 'outlining:', outlining self.do_run(open(path_from_root('tests', 'freetype', 'main.c'), 'r').read(), open(path_from_root('tests', 'freetype', 'ref.txt'), 'r').read(), ['font.ttf', 'test!', '150', '120', '25'], libraries=self.get_freetype(), includes=[path_from_root('tests', 'freetype', 'include')], post_build=post) # github issue 324 print '[issue 324]' self.do_run(open(path_from_root('tests', 'freetype', 'main_2.c'), 'r').read(), open(path_from_root('tests', 'freetype', 'ref_2.txt'), 'r').read(), ['font.ttf', 'w', '32', '32', '25'], libraries=self.get_freetype(), includes=[path_from_root('tests', 'freetype', 'include')], post_build=post) print '[issue 324 case 2]' self.do_run(open(path_from_root('tests', 'freetype', 'main_3.c'), 'r').read(), open(path_from_root('tests', 'freetype', 'ref_3.txt'), 'r').read(), ['font.ttf', 'W', '32', '32', '0'], libraries=self.get_freetype(), includes=[path_from_root('tests', 'freetype', 'include')], post_build=post) print '[issue 324 case 3]' self.do_run('', open(path_from_root('tests', 'freetype', 'ref_4.txt'), 'r').read(), ['font.ttf', 'ea', '40', '32', '0'], no_build=True) def test_sqlite(self): # gcc -O3 -I/home/alon/Dev/emscripten/tests/sqlite -ldl src.c if self.emcc_args is None: return self.skip('Very slow without ta2, and we would also need to include dlmalloc manually without emcc') if not self.is_le32(): return self.skip('fails on x86 due to a legalization issue on llvm 3.3') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO FIXME') self.banned_js_engines = [NODE_JS] # OOM in older node Settings.CORRECT_SIGNS = 1 Settings.CORRECT_OVERFLOWS = 0 Settings.CORRECT_ROUNDINGS = 0 if self.emcc_args is None: Settings.SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff Settings.DISABLE_EXCEPTION_CATCHING = 1 Settings.FAST_MEMORY = 4*1024*1024 Settings.EXPORTED_FUNCTIONS += ['_sqlite3_open', '_sqlite3_close', '_sqlite3_exec', '_sqlite3_free', '_callback']; if Settings.ASM_JS == 1 and '-g' in self.emcc_args: print "disabling inlining" # without registerize (which -g disables), we generate huge amounts of code Settings.INLINING_LIMIT = 50 self.do_run(r''' #define SQLITE_DISABLE_LFS #define LONGDOUBLE_TYPE double #define SQLITE_INT64_TYPE long long int #define SQLITE_THREADSAFE 0 ''' + open(path_from_root('tests', 'sqlite', 'sqlite3.c'), 'r').read() + open(path_from_root('tests', 'sqlite', 'benchmark.c'), 'r').read(), open(path_from_root('tests', 'sqlite', 'benchmark.txt'), 'r').read(), includes=[path_from_root('tests', 'sqlite')], force_c=True) def test_zlib(self): if not Settings.USE_TYPED_ARRAYS == 2: return self.skip('works in general, but cached build will be optimized and fail, so disable this') if Settings.ASM_JS: self.banned_js_engines = [NODE_JS] # TODO investigate if self.emcc_args is not None and '-O2' in self.emcc_args and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage Settings.CORRECT_SIGNS = 1 self.do_run(open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(), open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), libraries=self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), includes=[path_from_root('tests', 'zlib')], force_c=True) def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long if self.emcc_args is None: return self.skip('requires emcc') if Building.LLVM_OPTS and self.emcc_args is None: Settings.SAFE_HEAP = 0 # Optimizations make it so we do not have debug info on the line we need to ignore Settings.DEAD_FUNCTIONS = ['__ZSt9terminatev'] # Note: this is also a good test of per-file and per-line changes (since we have multiple files, and correct specific lines) if Settings.SAFE_HEAP: # Ignore bitfield warnings Settings.SAFE_HEAP = 3 Settings.SAFE_HEAP_LINES = ['btVoronoiSimplexSolver.h:40', 'btVoronoiSimplexSolver.h:41', 'btVoronoiSimplexSolver.h:42', 'btVoronoiSimplexSolver.h:43'] configure_commands = [['sh', './configure'], ['cmake', '.']] configure_args = [['--disable-demos','--disable-dependency-tracking'], ['-DBUILD_DEMOS=OFF', '-DBUILD_EXTRAS=OFF']] for c in range(0,2): configure = configure_commands[c] # Windows cannot run configure sh scripts. if WINDOWS and configure[0] == 'sh': continue # Depending on whether 'configure' or 'cmake' is used to build, Bullet places output files in different directory structures. if configure[0] == 'sh': generated_libs = [os.path.join('src', '.libs', 'libBulletDynamics.a'), os.path.join('src', '.libs', 'libBulletCollision.a'), os.path.join('src', '.libs', 'libLinearMath.a')] else: generated_libs = [os.path.join('src', 'BulletDynamics', 'libBulletDynamics.a'), os.path.join('src', 'BulletCollision', 'libBulletCollision.a'), os.path.join('src', 'LinearMath', 'libLinearMath.a')] def test(): self.do_run(open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], libraries=self.get_library('bullet', generated_libs, configure=configure, configure_args=configure_args[c], cache_name_extra=configure[0]), includes=[path_from_root('tests', 'bullet', 'src')]) test() assert 'asm2g' in test_modes if self.run_name == 'asm2g' and configure[0] == 'sh': # Test forced alignment print >> sys.stderr, 'testing FORCE_ALIGNED_MEMORY' old = open('src.cpp.o.js').read() Settings.FORCE_ALIGNED_MEMORY = 1 test() new = open('src.cpp.o.js').read() print len(old), len(new), old.count('tempBigInt'), new.count('tempBigInt') assert len(old) > len(new) assert old.count('tempBigInt') > new.count('tempBigInt') def test_poppler(self): if self.emcc_args is None: return self.skip('very slow, we only do this in emcc runs') Settings.CORRECT_OVERFLOWS = 1 Settings.CORRECT_SIGNS = 1 Building.COMPILER_TEST_OPTS += [ '-I' + path_from_root('tests', 'freetype', 'include'), '-I' + path_from_root('tests', 'poppler', 'include'), ] Settings.INVOKE_RUN = 0 # We append code that does run() ourselves # See post(), below input_file = open(os.path.join(self.get_dir(), 'paper.pdf.js'), 'w') input_file.write(str(map(ord, open(path_from_root('tests', 'poppler', 'paper.pdf'), 'rb').read()))) input_file.close() post = ''' def process(filename): # To avoid loading this large file to memory and altering it, we simply append to the end src = open(filename, 'a') src.write( \'\'\' FS.createDataFile('/', 'paper.pdf', eval(Module.read('paper.pdf.js')), true, false); Module.callMain(Module.arguments); Module.print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) }))); \'\'\' ) src.close() ''' #fontconfig = self.get_library('fontconfig', [os.path.join('src', '.libs', 'libfontconfig.a')]) # Used in file, but not needed, mostly freetype = self.get_freetype() poppler = self.get_library('poppler', [os.path.join('utils', 'pdftoppm.o'), os.path.join('utils', 'parseargs.o'), os.path.join('poppler', '.libs', 'libpoppler.a')], env_init={ 'FONTCONFIG_CFLAGS': ' ', 'FONTCONFIG_LIBS': ' ' }, configure_args=['--disable-libjpeg', '--disable-libpng', '--disable-poppler-qt', '--disable-poppler-qt4', '--disable-cms', '--disable-cairo-output', '--disable-abiword-output', '--enable-shared=no']) # Combine libraries combined = os.path.join(self.get_dir(), 'poppler-combined.bc') Building.link(poppler + freetype, combined) self.do_ll_run(combined, map(ord, open(path_from_root('tests', 'poppler', 'ref.ppm'), 'r').read()).__str__().replace(' ', ''), args='-scale-to 512 paper.pdf filename'.split(' '), post_build=post) #, build_ll_hook=self.do_autodebug) def test_openjpeg(self): if self.emcc_args is None: return self.skip('needs libc for getopt') Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default if Settings.USE_TYPED_ARRAYS == 2: Settings.CORRECT_SIGNS = 1 else: Settings.CORRECT_SIGNS = 2 Settings.CORRECT_SIGNS_LINES = ["mqc.c:566", "mqc.c:317"] post = ''' def process(filename): import tools.shared as shared original_j2k = shared.path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.j2k') src = open(filename, 'r').read().replace( '// {{PRE_RUN_ADDITIONS}}', "FS.createDataFile('/', 'image.j2k', %s, true, false);" % shared.line_splitter(str( map(ord, open(original_j2k, 'rb').read()) )) ).replace( '// {{POST_RUN_ADDITIONS}}', "Module.print('Data: ' + JSON.stringify(FS.analyzePath('image.raw').object.contents));" ) open(filename, 'w').write(src) ''' shutil.copy(path_from_root('tests', 'openjpeg', 'opj_config.h'), self.get_dir()) lib = self.get_library('openjpeg', [os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/index.c.o'.split('/')), os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/convert.c.o'.split('/')), os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/__/common/color.c.o'.split('/')), os.path.join('bin', 'libopenjpeg.so.1.4.0')], configure=['cmake', '.'], #configure_args=['--enable-tiff=no', '--enable-jp3d=no', '--enable-png=no'], make_args=[]) # no -j 2, since parallel builds can fail # We use doubles in JS, so we get slightly different values than native code. So we # check our output by comparing the average pixel difference def image_compare(output, err): # Get the image generated by JS, from the JSON.stringify'd array m = re.search('\[[\d, -]*\]', output) try: js_data = eval(m.group(0)) except AttributeError: print 'Failed to find proper image output in: ' + output raise js_data = map(lambda x: x if x >= 0 else 256+x, js_data) # Our output may be signed, so unsign it # Get the correct output true_data = open(path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.raw'), 'rb').read() # Compare them assert(len(js_data) == len(true_data)) num = len(js_data) diff_total = js_total = true_total = 0 for i in range(num): js_total += js_data[i] true_total += ord(true_data[i]) diff_total += abs(js_data[i] - ord(true_data[i])) js_mean = js_total/float(num) true_mean = true_total/float(num) diff_mean = diff_total/float(num) image_mean = 83.265 #print '[image stats:', js_mean, image_mean, true_mean, diff_mean, num, ']' assert abs(js_mean - image_mean) < 0.01 assert abs(true_mean - image_mean) < 0.01 assert diff_mean < 0.01 return output self.emcc_args += ['--minify', '0'] # to compare the versions def do_test(): self.do_run(open(path_from_root('tests', 'openjpeg', 'codec', 'j2k_to_image.c'), 'r').read(), 'Successfully generated', # The real test for valid output is in image_compare '-i image.j2k -o image.raw'.split(' '), libraries=lib, includes=[path_from_root('tests', 'openjpeg', 'libopenjpeg'), path_from_root('tests', 'openjpeg', 'codec'), path_from_root('tests', 'openjpeg', 'common'), os.path.join(self.get_build_dir(), 'openjpeg')], force_c=True, post_build=post, output_nicerizer=image_compare)#, build_ll_hook=self.do_autodebug) do_test() # some test coverage for EMCC_DEBUG 1 and 2 if self.emcc_args and '-O2' in self.emcc_args and 'EMCC_DEBUG' not in os.environ and '-g' in self.emcc_args: shutil.copyfile('src.c.o.js', 'release.js') try: os.environ['EMCC_DEBUG'] = '1' print '2' do_test() shutil.copyfile('src.c.o.js', 'debug1.js') os.environ['EMCC_DEBUG'] = '2' print '3' do_test() shutil.copyfile('src.c.o.js', 'debug2.js') finally: del os.environ['EMCC_DEBUG'] for debug in [1,2]: def clean(text): text = text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\n}', '{}') return '\n'.join(sorted(text.split('\n'))) self.assertIdentical(clean(open('release.js').read()), clean(open('debug%d.js' % debug).read())) # EMCC_DEBUG=1 mode must not generate different code! print >> sys.stderr, 'debug check %d passed too' % debug try: os.environ['EMCC_FORCE_STDLIBS'] = '1' print 'EMCC_FORCE_STDLIBS' do_test() finally: del os.environ['EMCC_FORCE_STDLIBS'] print >> sys.stderr, 'EMCC_FORCE_STDLIBS ok' try_delete(CANONICAL_TEMP_DIR) else: print >> sys.stderr, 'not doing debug check' def test_python(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') if not self.is_le32(): return self.skip('fails on non-le32') # FIXME #Settings.EXPORTED_FUNCTIONS += ['_PyRun_SimpleStringFlags'] # for the demo if self.is_le32(): bitcode = path_from_root('tests', 'python', 'python.le32.bc') else: bitcode = path_from_root('tests', 'python', 'python.small.bc') self.do_ll_run(bitcode, 'hello python world!\n[0, 2, 4, 6]\n5\n22\n5.470000', args=['-S', '-c' '''print "hello python world!"; print [x*2 for x in range(4)]; t=2; print 10-3-t; print (lambda x: x*2)(11); print '%f' % 5.47''']) def test_lifetime(self): if self.emcc_args is None: return self.skip('test relies on emcc opts') self.do_ll_run(path_from_root('tests', 'lifetime.ll'), 'hello, world!\n') if '-O1' in self.emcc_args or '-O2' in self.emcc_args: assert 'a18' not in open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read(), 'lifetime stuff and their vars must be culled' # Test cases in separate files. Note that these files may contain invalid .ll! # They are only valid enough for us to read for test purposes, not for llvm-as # to process. def test_cases(self): if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") emcc_args = self.emcc_args try: os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' Settings.CHECK_OVERFLOWS = 0 for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): shortname = name.replace('.ll', '') if '' not in shortname: continue if '_ta2' in shortname and not Settings.USE_TYPED_ARRAYS == 2: print self.skip('case "%s" only relevant for ta2' % shortname) continue if '_noasm' in shortname and Settings.ASM_JS: print self.skip('case "%s" not relevant for asm.js' % shortname) continue if '_le32' in shortname and not self.is_le32(): print self.skip('case "%s" not relevant for non-le32 target' % shortname) continue self.emcc_args = emcc_args if os.path.exists(shortname + '.emcc'): if not self.emcc_args: continue self.emcc_args = self.emcc_args + json.loads(open(shortname + '.emcc').read()) print >> sys.stderr, "Testing case '%s'..." % shortname output_file = path_from_root('tests', 'cases', shortname + '.txt') if Settings.QUANTUM_SIZE == 1: q1_output_file = path_from_root('tests', 'cases', shortname + '_q1.txt') if os.path.exists(q1_output_file): output_file = q1_output_file if os.path.exists(output_file): output = open(output_file, 'r').read() else: output = 'hello, world!' if output.rstrip() != 'skip': self.do_ll_run(path_from_root('tests', 'cases', name), output) # Optional source checking, a python script that gets a global generated with the source src_checker = path_from_root('tests', 'cases', shortname + '.py') if os.path.exists(src_checker): generated = open('src.cpp.o.js').read() exec(open(src_checker).read()) finally: del os.environ['EMCC_LEAVE_INPUTS_RAW'] self.emcc_args = emcc_args def test_fuzz(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('tests', 'fuzz')] def run_all(x): print x for name in glob.glob(path_from_root('tests', 'fuzz', '*.c')): print name self.do_run(open(path_from_root('tests', 'fuzz', name)).read(), open(path_from_root('tests', 'fuzz', name + '.txt')).read(), force_c=True) run_all('normal') self.emcc_args += ['--llvm-lto', '1'] run_all('lto') # Autodebug the code def do_autodebug(self, filename): output = Popen([PYTHON, AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] assert 'Success.' in output, output self.prep_ll_run(filename, filename+'.o.ll.ll', force_recompile=True) # rebuild .bc # TODO: use code in do_autodebug_post for this # Autodebug the code, after LLVM opts. Will only work once! def do_autodebug_post(self, filename): if not hasattr(self, 'post'): print 'Asking for post re-call' self.post = True return True print 'Autodebugging during post time' delattr(self, 'post') output = Popen([PYTHON, AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] assert 'Success.' in output, output shutil.copyfile(filename + '.o.ll.ll', filename + '.o.ll') Building.llvm_as(filename) Building.llvm_dis(filename) def test_autodebug(self): if Building.LLVM_OPTS: return self.skip('LLVM opts mess us up') Building.COMPILER_TEST_OPTS += ['--llvm-opts', '0'] # Run a test that should work, generating some code self.test_structs() filename = os.path.join(self.get_dir(), 'src.cpp') self.do_autodebug(filename) # Compare to each other, and to expected output self.do_ll_run(path_from_root('tests', filename+'.o.ll.ll'), '''AD:-1,1''') assert open('stdout').read().startswith('AD:-1'), 'We must note when we enter functions' # Test using build_ll_hook src = ''' #include char cache[256], *next = cache; int main() { cache[10] = 25; next[20] = 51; int x = cache[10]; double y = 11.52; printf("*%d,%d,%.2f*\\n", x, cache[20], y); return 0; } ''' self.do_run(src, '''AD:-1,1''', build_ll_hook=self.do_autodebug) def test_corruption(self): if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') Settings.CORRUPTION_CHECK = 1 src = r''' #include #include #include int main(int argc, char **argv) { int size = 1024*argc; char *buffer = (char*)malloc(size); #if CORRUPT memset(buffer, argc, size+15); #else memset(buffer, argc, size); #endif for (int x = 0; x < size; x += argc*3) buffer[x] = x/3; int ret = 0; for (int x = 0; x < size; x++) ret += buffer[x]; free(buffer); printf("All ok, %d\n", ret); } ''' for corrupt in [1]: self.do_run(src.replace('CORRUPT', str(corrupt)), 'Heap corruption detected!' if corrupt else 'All ok, 4209') def test_corruption_2(self): if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') Settings.SAFE_HEAP = 1 Settings.CORRUPTION_CHECK = 1 # test for free(0), malloc(0), etc. src = r''' #include #include #include #include void bye() { printf("all ok\n"); } int main() { atexit(bye); std::string testPath = "/Script/WA-KA.txt"; std::fstream str(testPath.c_str(), std::ios::in | std::ios::binary); if (str.is_open()) { std::cout << "open!" << std::endl; } else { std::cout << "missing!" << std::endl; } return 1; } ''' self.do_run(src, 'missing!\nall ok\n') def test_corruption_3(self): if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') Settings.CORRUPTION_CHECK = 1 # realloc src = r''' #include #include #include void bye() { printf("all ok\n"); } int main(int argc, char **argv) { atexit(bye); char *buffer = (char*)malloc(100); for (int i = 0; i < 100; i++) buffer[i] = (i*i)%256; buffer = (char*)realloc(buffer, argc + 50); for (int i = 0; i < argc + 50; i++) { //printf("%d : %d : %d : %d\n", i, (int)(buffer + i), buffer[i], (char)((i*i)%256)); assert(buffer[i] == (char)((i*i)%256)); } return 1; } ''' self.do_run(src, 'all ok\n') ### Integration tests def test_ccall(self): if self.emcc_args is not None and '-O2' in self.emcc_args: self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right src = r''' #include #include extern "C" { int get_int() { return 5; } float get_float() { return 3.14; } char * get_string() { return "hello world"; } void print_int(int x) { printf("%d\n", x); } void print_float(float x) { printf("%.2f\n", x); } void print_string(char *x) { printf("%s\n", x); } int multi(int x, float y, int z, char *str) { if (x) puts(str); return (x+y)*z; } int * pointer(int *in) { printf("%d\n", *in); static int ret = 21; return &ret; } } int main(int argc, char **argv) { return 0; } ''' post = ''' def process(filename): src = \'\'\' var Module = { 'noInitialRun': true }; \'\'\' + open(filename, 'r').read() + \'\'\' Module.addOnExit(function () { Module.print('*'); var ret; ret = Module['ccall']('get_int', 'number'); Module.print([typeof ret, ret]); ret = ccall('get_float', 'number'); Module.print([typeof ret, ret.toFixed(2)]); ret = ccall('get_string', 'string'); Module.print([typeof ret, ret]); ret = ccall('print_int', null, ['number'], [12]); Module.print(typeof ret); ret = ccall('print_float', null, ['number'], [14.56]); Module.print(typeof ret); ret = ccall('print_string', null, ['string'], ["cheez"]); Module.print(typeof ret); ret = ccall('print_string', null, ['array'], [[97, 114, 114, 45, 97, 121, 0]]); Module.print(typeof ret); ret = ccall('multi', 'number', ['number', 'number', 'number', 'string'], [2, 1.4, 3, 'more']); Module.print([typeof ret, ret]); var p = ccall('malloc', 'pointer', ['number'], [4]); setValue(p, 650, 'i32'); ret = ccall('pointer', 'pointer', ['pointer'], [p]); Module.print([typeof ret, getValue(ret, 'i32')]); Module.print('*'); // part 2: cwrap var multi = Module['cwrap']('multi', 'number', ['number', 'number', 'number', 'string']); Module.print(multi(2, 1.4, 3, 'atr')); Module.print(multi(8, 5.4, 4, 'bret')); Module.print('*'); // part 3: avoid stack explosion for (var i = 0; i < TOTAL_STACK/60; i++) { ccall('multi', 'number', ['number', 'number', 'number', 'string'], [0, 0, 0, '123456789012345678901234567890123456789012345678901234567890']); } Module.print('stack is ok.'); }); Module.callMain(); \'\'\' open(filename, 'w').write(src) ''' Settings.EXPORTED_FUNCTIONS += ['_get_int', '_get_float', '_get_string', '_print_int', '_print_float', '_print_string', '_multi', '_pointer', '_malloc'] self.do_run(src, '*\nnumber,5\nnumber,3.14\nstring,hello world\n12\nundefined\n14.56\nundefined\ncheez\nundefined\narr-ay\nundefined\nmore\nnumber,10\n650\nnumber,21\n*\natr\n10\nbret\n53\n*\nstack is ok.\n', post_build=post) def test_pgo(self): if Settings.ASM_JS: return self.skip('PGO does not work in asm mode') def run_all(name, src): print name def test(expected, args=[], no_build=False): self.do_run(src, expected, args=args, no_build=no_build) return open(self.in_dir('src.cpp.o.js')).read() # Sanity check that it works and the dead function is emitted js = test('*9*') assert 'function _unused(' in js # Run with PGO, see that unused is true to its name Settings.PGO = 1 test("*9*\n-s DEAD_FUNCTIONS='[\"_unused\"]'") Settings.PGO = 0 # Kill off the dead function, still works and it is not emitted Settings.DEAD_FUNCTIONS = ['_unused'] js = test('*9*') assert 'function _unused($' not in js # no compiled code assert 'function _unused(' in js # lib-generated stub Settings.DEAD_FUNCTIONS = [] # Run the same code with argc that uses the dead function, see abort test(('dead function: unused'), args=['a', 'b'], no_build=True) # Normal stuff run_all('normal', r''' #include extern "C" { int used(int x) { if (x == 0) return -1; return used(x/3) + used(x/17) + x%5; } int unused(int x) { if (x == 0) return -1; return unused(x/4) + unused(x/23) + x%7; } } int main(int argc, char **argv) { printf("*%d*\n", argc == 3 ? unused(argv[0][0] + 1024) : used(argc + 1555)); return 0; } ''') # Call by function pointer run_all('function pointers', r''' #include extern "C" { int used(int x) { if (x == 0) return -1; return used(x/3) + used(x/17) + x%5; } int unused(int x) { if (x == 0) return -1; return unused(x/4) + unused(x/23) + x%7; } } typedef int (*ii)(int); int main(int argc, char **argv) { ii pointers[256]; for (int i = 0; i < 256; i++) { pointers[i] = (i == 3) ? unused : used; } printf("*%d*\n", pointers[argc](argc + 1555)); return 0; } ''') def test_asm_pgo(self): if not Settings.ASM_JS: return self.skip('this is a test for PGO for asm (NB: not *in* asm)') src = open(path_from_root('tests', 'hello_libcxx.cpp')).read() output = 'hello, world!' self.do_run(src, output) shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('normal.js')) Settings.ASM_JS = 0 Settings.PGO = 1 self.do_run(src, output) Settings.ASM_JS = 1 Settings.PGO = 0 shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgo.js')) pgo_output = run_js(self.in_dir('pgo.js')).split('\n')[1] open('pgo_data.rsp', 'w').write(pgo_output) # with response file self.emcc_args += ['@pgo_data.rsp'] self.do_run(src, output) self.emcc_args.pop() shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed.js')) before = len(open('normal.js').read()) after = len(open('pgoed.js').read()) assert after < 0.90 * before, [before, after] # expect a size reduction # with response in settings element itself open('dead_funcs', 'w').write(pgo_output[pgo_output.find('['):-1]) self.emcc_args += ['-s', 'DEAD_FUNCTIONS=@' + self.in_dir('dead_funcs')] self.do_run(src, output) self.emcc_args.pop() self.emcc_args.pop() shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed2.js')) assert open('pgoed.js').read() == open('pgoed2.js').read() # with relative response in settings element itself open('dead_funcs', 'w').write(pgo_output[pgo_output.find('['):-1]) self.emcc_args += ['-s', 'DEAD_FUNCTIONS=@dead_funcs'] self.do_run(src, output) self.emcc_args.pop() self.emcc_args.pop() shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed2.js')) assert open('pgoed.js').read() == open('pgoed2.js').read() def test_exported_response(self): if self.emcc_args is None: return self.skip('requires emcc') src = r''' #include #include extern "C" { int other_function() { return 5; } } int main() { printf("waka!\n"); return 0; } ''' open('exps', 'w').write('["_main","_other_function"]') self.emcc_args += ['-s', 'EXPORTED_FUNCTIONS=@exps'] self.do_run(src, '''waka!''') assert 'other_function' in open('src.cpp.o.js').read() def test_add_function(self): if self.emcc_args is None: return self.skip('requires emcc') Settings.INVOKE_RUN = 0 Settings.RESERVED_FUNCTION_POINTERS = 1 src = r''' #include #include int main(int argc, char **argv) { int fp = atoi(argv[1]); printf("fp: %d\n", fp); void (*f)(int) = reinterpret_cast(fp); f(7); return 0; } ''' open(os.path.join(self.get_dir(), 'post.js'), 'w').write(''' var newFuncPtr = Runtime.addFunction(function(num) { Module.print('Hello ' + num + ' from JS!'); }); Module.callMain([newFuncPtr.toString()]); ''') self.emcc_args += ['--post-js', 'post.js'] self.do_run(src, '''Hello 7 from JS!''') if Settings.ASM_JS: Settings.RESERVED_FUNCTION_POINTERS = 0 self.do_run(src, '''Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.''') generated = open('src.cpp.o.js').read() assert 'jsCall' not in generated Settings.RESERVED_FUNCTION_POINTERS = 1 Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS # flip the test self.do_run(src, '''Hello 7 from JS!''') def test_demangle_stacks(self): if Settings.ASM_JS: return self.skip('spidermonkey has stack trace issues') src = r''' #include #include namespace NameSpace { class Class { public: int Aborter(double x, char y, int *z) { int addr = x+y+(int)z; void *p = (void*)addr; for (int i = 0; i < 100; i++) free(p); // will abort, should show proper stack trace } }; } int main(int argc, char **argv) { NameSpace::Class c; c.Aborter(1.234, 'a', NULL); return 0; } ''' self.do_run(src, 'NameSpace::Class::Aborter(double, char, int*)'); def test_embind(self): if self.emcc_args is None: return self.skip('requires emcc') Building.COMPILER_TEST_OPTS += ['--bind'] src = r''' #include #include using namespace emscripten; int main() { val Math = val::global("Math"); // two ways to call Math.abs printf("abs(-10): %d\n", Math.call("abs", -10)); printf("abs(-11): %d\n", Math["abs"](-11).as()); return 0; } ''' self.do_run(src, 'abs(-10): 10\nabs(-11): 11'); def test_embind_2(self): if self.emcc_args is None: return self.skip('requires emcc') Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] open('post.js', 'w').write(''' Module.print('lerp ' + Module.lerp(1, 2, 0.66) + '.'); ''') src = r''' #include #include #include using namespace emscripten; float lerp(float a, float b, float t) { return (1 - t) * a + t * b; } EMSCRIPTEN_BINDINGS(my_module) { function("lerp", &lerp); } ''' self.do_run(src, 'lerp 1.66'); def test_scriptaclass(self): if self.emcc_args is None: return self.skip('requires emcc') Settings.EXPORT_BINDINGS = 1 header_filename = os.path.join(self.get_dir(), 'header.h') header = ''' struct ScriptMe { int value; ScriptMe(int val); int getVal(); // XXX Sadly, inlining these will result in LLVM not // producing any code for them (when just building // as a library) void mulVal(int mul); }; ''' h = open(header_filename, 'w') h.write(header) h.close() src = ''' #include "header.h" ScriptMe::ScriptMe(int val) : value(val) { } int ScriptMe::getVal() { return value; } void ScriptMe::mulVal(int mul) { value *= mul; } ''' # Way 1: use demangler and namespacer script_src = ''' var sme = Module._.ScriptMe.__new__(83); // malloc(sizeof(ScriptMe)), ScriptMe::ScriptMe(sme, 83) / new ScriptMe(83) (at addr sme) Module._.ScriptMe.mulVal(sme, 2); // ScriptMe::mulVal(sme, 2) sme.mulVal(2) Module.print('*' + Module._.ScriptMe.getVal(sme) + '*'); _free(sme); Module.print('*ok*'); ''' post = ''' def process(filename): Popen([PYTHON, DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate() Popen([PYTHON, NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate() src = open(filename, 'r').read().replace( '// {{MODULE_ADDITIONS}', 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().replace('var ModuleNames = ', '').rstrip() + ';\n\n' + script_src + '\n\n' + '// {{MODULE_ADDITIONS}' ) open(filename, 'w').write(src) ''' # XXX disable due to possible v8 bug -- self.do_run(src, '*166*\n*ok*', post_build=post) if self.emcc_args is not None and '-O2' in self.emcc_args and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right # Way 2: use CppHeaderParser Settings.RUNTIME_TYPE_INFO = 1 header = ''' #include class Parent { protected: int value; public: Parent(int val); Parent(Parent *p, Parent *q); // overload constructor int getVal() { return value; }; // inline should work just fine here, unlike Way 1 before void mulVal(int mul); }; class Child1 : public Parent { public: Child1() : Parent(7) { printf("Child1:%d\\n", value); }; Child1(int val) : Parent(val*2) { value -= 1; printf("Child1:%d\\n", value); }; int getValSqr() { return value*value; } int getValSqr(int more) { return value*value*more; } int getValTimes(int times=1) { return value*times; } }; class Child2 : public Parent { public: Child2() : Parent(9) { printf("Child2:%d\\n", value); }; int getValCube() { return value*value*value; } static void printStatic() { printf("*static*\\n"); } virtual void virtualFunc() { printf("*virtualf*\\n"); } virtual void virtualFunc2() { printf("*virtualf2*\\n"); } static void runVirtualFunc(Child2 *self) { self->virtualFunc(); }; private: void doSomethingSecret() { printf("security breached!\\n"); }; // we should not be able to do this }; ''' open(header_filename, 'w').write(header) basename = os.path.join(self.get_dir(), 'bindingtest') output = Popen([PYTHON, BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] #print output assert 'Traceback' not in output, 'Failure in binding generation: ' + output src = ''' #include "header.h" Parent::Parent(int val) : value(val) { printf("Parent:%d\\n", val); } Parent::Parent(Parent *p, Parent *q) : value(p->value + q->value) { printf("Parent:%d\\n", value); } void Parent::mulVal(int mul) { value *= mul; } #include "bindingtest.cpp" ''' post2 = ''' def process(filename): src = open(filename, 'a') src.write(open('bindingtest.js').read() + '\\n\\n') src.close() ''' def post3(filename): script_src_2 = ''' var sme = new Module.Parent(42); sme.mulVal(2); Module.print('*') Module.print(sme.getVal()); Module.print('c1'); var c1 = new Module.Child1(); Module.print(c1.getVal()); c1.mulVal(2); Module.print(c1.getVal()); Module.print(c1.getValSqr()); Module.print(c1.getValSqr(3)); Module.print(c1.getValTimes()); // default argument should be 1 Module.print(c1.getValTimes(2)); Module.print('c1 v2'); c1 = new Module.Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2 Module.print(c1.getVal()); c1.mulVal(2); Module.print(c1.getVal()); Module.print(c1.getValSqr()); Module.print(c1.getValSqr(3)); Module.print('c2') var c2 = new Module.Child2(); Module.print(c2.getVal()); c2.mulVal(2); Module.print(c2.getVal()); Module.print(c2.getValCube()); var succeeded; try { succeeded = 0; Module.print(c2.doSomethingSecret()); // should fail since private succeeded = 1; } catch(e) {} Module.print(succeeded); try { succeeded = 0; Module.print(c2.getValSqr()); // function from the other class succeeded = 1; } catch(e) {} Module.print(succeeded); try { succeeded = 0; c2.getValCube(); // sanity succeeded = 1; } catch(e) {} Module.print(succeeded); Module.Child2.prototype.printStatic(); // static calls go through the prototype // virtual function c2.virtualFunc(); Module.Child2.prototype.runVirtualFunc(c2); c2.virtualFunc2(); // extend the class from JS var c3 = new Module.Child2; Module.customizeVTable(c3, [{ original: Module.Child2.prototype.virtualFunc, replacement: function() { Module.print('*js virtualf replacement*'); } }, { original: Module.Child2.prototype.virtualFunc2, replacement: function() { Module.print('*js virtualf2 replacement*'); } }]); c3.virtualFunc(); Module.Child2.prototype.runVirtualFunc(c3); c3.virtualFunc2(); c2.virtualFunc(); // original should remain the same Module.Child2.prototype.runVirtualFunc(c2); c2.virtualFunc2(); Module.print('*ok*'); ''' code = open(filename).read() src = open(filename, 'w') src.write('var Module = {};\n') # name Module src.write(code) src.write(script_src_2 + '\n') src.close() Settings.RESERVED_FUNCTION_POINTERS = 20 self.do_run(src, '''* 84 c1 Parent:7 Child1:7 7 14 196 588 14 28 c1 v2 Parent:16 Child1:15 15 30 900 2700 c2 Parent:9 Child2:9 9 18 5832 0 0 1 *static* *virtualf* *virtualf* *virtualf2*''' + (''' Parent:9 Child2:9 *js virtualf replacement* *js virtualf replacement* *js virtualf2 replacement* *virtualf* *virtualf* *virtualf2*''') + ''' *ok* ''', post_build=(post2, post3)) def test_scriptaclass_2(self): if self.emcc_args is None: return self.skip('requires emcc') Settings.EXPORT_BINDINGS = 1 header_filename = os.path.join(self.get_dir(), 'header.h') header = ''' #include #include class StringUser { char *s; int i; public: StringUser(char *string, int integer) : s(strdup(string)), i(integer) {} void Print(int anotherInteger, char *anotherString) { printf("|%s|%d|%s|%d|\\n", s, i, anotherString, anotherInteger); } void CallOther(StringUser *fr) { fr->Print(i, s); } }; ''' open(header_filename, 'w').write(header) basename = os.path.join(self.get_dir(), 'bindingtest') output = Popen([PYTHON, BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] #print output assert 'Traceback' not in output, 'Failure in binding generation: ' + output src = ''' #include "header.h" #include "bindingtest.cpp" ''' post = ''' def process(filename): src = open(filename, 'a') src.write(open('bindingtest.js').read() + '\\n\\n') src.write(\'\'\' var user = new Module.StringUser("hello", 43); user.Print(41, "world"); \'\'\') src.close() ''' self.do_run(src, '|hello|43|world|41|', post_build=post) def test_typeinfo(self): if self.emcc_args is not None and self.emcc_args != []: return self.skip('full LLVM opts optimize out all the code that uses the type') Settings.RUNTIME_TYPE_INFO = 1 if Settings.QUANTUM_SIZE != 4: return self.skip('We assume normal sizes in the output here') src = ''' #include struct UserStruct { int x; char y; short z; }; struct Encloser { short x; UserStruct us; int y; }; int main() { Encloser e; e.us.y = 5; printf("*ok:%d*\\n", e.us.y); return 0; } ''' post = ''' def process(filename): src = open(filename, 'r').read().replace( '// {{POST_RUN_ADDITIONS}}', \'\'\' if (Runtime.typeInfo) { Module.print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|'); var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser') Module.print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|'); Module.print('|' + JSON.stringify(Runtime.generateStructInfo(['x', 'y', 'z'], 'UserStruct')) + '|'); } else { Module.print('No type info.'); } \'\'\' ) open(filename, 'w').write(src) ''' self.do_run(src, '*ok:5*\n|i32,i8,i16|0,4,6|\n|0,4,8,10,12|\n|{"__size__":8,"x":0,"y":4,"z":6}|', post_build=post) # Make sure that without the setting, we don't spam the .js with the type info Settings.RUNTIME_TYPE_INFO = 0 self.do_run(src, 'No type info.', post_build=post) ### Tests for tools def test_safe_heap(self): if not Settings.SAFE_HEAP: return self.skip('We need SAFE_HEAP to test SAFE_HEAP') if Settings.USE_TYPED_ARRAYS == 2: return self.skip('It is ok to violate the load-store assumption with TA2') if Building.LLVM_OPTS: return self.skip('LLVM can optimize away the intermediate |x|') src = ''' #include #include int main() { int *x = (int*)malloc(sizeof(int)); *x = 20; float *y = (float*)x; printf("%f\\n", *y); printf("*ok*\\n"); return 0; } ''' try: self.do_run(src, '*nothingatall*') except Exception, e: # This test *should* fail, by throwing this exception assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) # And we should not fail if we disable checking on that line Settings.SAFE_HEAP = 3 Settings.SAFE_HEAP_LINES = ["src.cpp:7"] self.do_run(src, '*ok*') # But if we disable the wrong lines, we still fail Settings.SAFE_HEAP_LINES = ["src.cpp:99"] try: self.do_run(src, '*nothingatall*') except Exception, e: # This test *should* fail, by throwing this exception assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) # And reverse the checks with = 2 Settings.SAFE_HEAP = 2 Settings.SAFE_HEAP_LINES = ["src.cpp:99"] self.do_run(src, '*ok*') Settings.SAFE_HEAP = 1 # Linking multiple files should work too module = ''' #include #include void callFunc() { int *x = (int*)malloc(sizeof(int)); *x = 20; float *y = (float*)x; printf("%f\\n", *y); } ''' module_name = os.path.join(self.get_dir(), 'module.cpp') open(module_name, 'w').write(module) main = ''' #include #include extern void callFunc(); int main() { callFunc(); int *x = (int*)malloc(sizeof(int)); *x = 20; float *y = (float*)x; printf("%f\\n", *y); printf("*ok*\\n"); return 0; } ''' main_name = os.path.join(self.get_dir(), 'main.cpp') open(main_name, 'w').write(main) Building.emcc(module_name, ['-g']) Building.emcc(main_name, ['-g']) all_name = os.path.join(self.get_dir(), 'all.bc') Building.link([module_name + '.o', main_name + '.o'], all_name) try: self.do_ll_run(all_name, '*nothingatall*') except Exception, e: # This test *should* fail, by throwing this exception assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) # And we should not fail if we disable checking on those lines Settings.SAFE_HEAP = 3 Settings.SAFE_HEAP_LINES = ["module.cpp:7", "main.cpp:9"] self.do_ll_run(all_name, '*ok*') # But we will fail if we do not disable exactly what we need to - any mistake leads to error for lines in [["module.cpp:22", "main.cpp:9"], ["module.cpp:7", "main.cpp:29"], ["module.cpp:127", "main.cpp:449"], ["module.cpp:7"], ["main.cpp:9"]]: Settings.SAFE_HEAP_LINES = lines try: self.do_ll_run(all_name, '*nothingatall*') except Exception, e: # This test *should* fail, by throwing this exception assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) def test_debug(self): if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') if self.emcc_args is not None: if '-O1' in self.emcc_args or '-O2' in self.emcc_args: return self.skip('optimizations remove LLVM debug info') src = ''' #include #include void checker(int x) { x += 20; assert(x < 15); // this is line 7! } int main() { checker(10); return 0; } ''' try: self.do_run(src, '*nothingatall*') except Exception, e: # This test *should* fail assert 'Assertion failed: x < 15' in str(e), str(e) lines = open('src.cpp.o.js', 'r').readlines() lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines) found_line_num = any(('//@line 7 "' in line) for line in lines) found_filename = any(('src.cpp"\n' in line) for line in lines) assert found_line_num, 'Must have debug info with the line number' assert found_filename, 'Must have debug info with the filename' def test_source_map(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') src = ''' #include #include __attribute__((noinline)) int foo() { printf("hi"); // line 6 return 1; // line 7 } int main() { printf("%d", foo()); // line 11 return 0; // line 12 } ''' dirname = self.get_dir() src_filename = os.path.join(dirname, 'src.cpp') out_filename = os.path.join(dirname, 'a.out.js') no_maps_filename = os.path.join(dirname, 'no-maps.out.js') with open(src_filename, 'w') as f: f.write(src) assert '-g4' not in Building.COMPILER_TEST_OPTS Building.emcc(src_filename, Settings.serialize() + self.emcc_args + Building.COMPILER_TEST_OPTS, out_filename) # the file name may find its way into the generated code, so make sure we # can do an apples-to-apples comparison by compiling with the same file name shutil.move(out_filename, no_maps_filename) with open(no_maps_filename) as f: no_maps_file = f.read() no_maps_file = re.sub(' *//@.*$', '', no_maps_file, flags=re.MULTILINE) Building.COMPILER_TEST_OPTS.append('-g4') def build_and_check(): import json Building.emcc(src_filename, Settings.serialize() + self.emcc_args + Building.COMPILER_TEST_OPTS, out_filename, stderr=PIPE) with open(out_filename) as f: out_file = f.read() # after removing the @line and @sourceMappingURL comments, the build # result should be identical to the non-source-mapped debug version. # this is worth checking because the parser AST swaps strings for token # objects when generating source maps, so we want to make sure the # optimizer can deal with both types. out_file = re.sub(' *//@.*$', '', out_file, flags=re.MULTILINE) def clean(code): code = code.replace('{\n}', '{}') return '\n'.join(sorted(code.split('\n'))) self.assertIdentical(clean(no_maps_file), clean(out_file)) map_filename = out_filename + '.map' data = json.load(open(map_filename, 'r')) self.assertPathsIdentical(out_filename, data['file']) self.assertPathsIdentical(src_filename, data['sources'][0]) self.assertTextDataIdentical(src, data['sourcesContent'][0]) mappings = json.loads(jsrun.run_js( path_from_root('tools', 'source-maps', 'sourcemap2json.js'), tools.shared.NODE_JS, [map_filename])) seen_lines = set() for m in mappings: self.assertPathsIdentical(src_filename, m['source']) seen_lines.add(m['originalLine']) # ensure that all the 'meaningful' lines in the original code get mapped assert seen_lines.issuperset([6, 7, 11, 12]) # EMCC_DEBUG=2 causes lots of intermediate files to be written, and so # serves as a stress test for source maps because it needs to correlate # line numbers across all those files. old_emcc_debug = os.environ.get('EMCC_DEBUG', None) os.environ.pop('EMCC_DEBUG', None) try: build_and_check() os.environ['EMCC_DEBUG'] = '2' build_and_check() finally: if old_emcc_debug is not None: os.environ['EMCC_DEBUG'] = old_emcc_debug else: os.environ.pop('EMCC_DEBUG', None) def test_exception_source_map(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") if '-g4' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g4') if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') src = ''' #include __attribute__((noinline)) void foo(int i) { if (i < 10) throw i; // line 5 } int main() { int i; scanf("%d", &i); foo(i); return 0; } ''' def post(filename): import json map_filename = filename + '.map' mappings = json.loads(jsrun.run_js( path_from_root('tools', 'source-maps', 'sourcemap2json.js'), tools.shared.NODE_JS, [map_filename])) with open(filename) as f: lines = f.readlines() for m in mappings: if m['originalLine'] == 5 and '__cxa_throw' in lines[m['generatedLine']]: return assert False, 'Must label throw statements with line numbers' dirname = self.get_dir() self.build(src, dirname, os.path.join(dirname, 'src.cpp'), post_build=(None, post)) def test_linespecific(self): if Settings.ASM_JS: return self.skip('asm always has corrections on') if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') if self.emcc_args: self.emcc_args += ['--llvm-opts', '0'] # llvm full opts make the expected failures here not happen Building.COMPILER_TEST_OPTS += ['--llvm-opts', '0'] Settings.CHECK_SIGNS = 0 Settings.CHECK_OVERFLOWS = 0 # Signs src = ''' #include #include int main() { int varey = 100; unsigned int MAXEY = -1; printf("*%d*\\n", varey >= MAXEY); // 100 >= -1? not in unsigned! } ''' Settings.CORRECT_SIGNS = 0 self.do_run(src, '*1*') # This is a fail - we expect 0 Settings.CORRECT_SIGNS = 1 self.do_run(src, '*0*') # Now it will work properly # And now let's fix just that one line Settings.CORRECT_SIGNS = 2 Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"] self.do_run(src, '*0*') # Fixing the wrong line should not work Settings.CORRECT_SIGNS = 2 Settings.CORRECT_SIGNS_LINES = ["src.cpp:3"] self.do_run(src, '*1*') # And reverse the checks with = 2 Settings.CORRECT_SIGNS = 3 Settings.CORRECT_SIGNS_LINES = ["src.cpp:3"] self.do_run(src, '*0*') Settings.CORRECT_SIGNS = 3 Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"] self.do_run(src, '*1*') Settings.CORRECT_SIGNS = 0 # Overflows src = ''' #include int main() { int t = 77; for (int i = 0; i < 30; i++) { t = t + t + t + t + t + 1; } printf("*%d,%d*\\n", t, t & 127); return 0; } ''' correct = '*186854335,63*' Settings.CORRECT_OVERFLOWS = 0 try: self.do_run(src, correct) raise Exception('UNEXPECTED-PASS') except Exception, e: assert 'UNEXPECTED' not in str(e), str(e) assert 'Expected to find' in str(e), str(e) Settings.CORRECT_OVERFLOWS = 1 self.do_run(src, correct) # Now it will work properly # And now let's fix just that one line Settings.CORRECT_OVERFLOWS = 2 Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:6"] self.do_run(src, correct) # Fixing the wrong line should not work Settings.CORRECT_OVERFLOWS = 2 Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:3"] try: self.do_run(src, correct) raise Exception('UNEXPECTED-PASS') except Exception, e: assert 'UNEXPECTED' not in str(e), str(e) assert 'Expected to find' in str(e), str(e) # And reverse the checks with = 2 Settings.CORRECT_OVERFLOWS = 3 Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:3"] self.do_run(src, correct) Settings.CORRECT_OVERFLOWS = 3 Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:6"] try: self.do_run(src, correct) raise Exception('UNEXPECTED-PASS') except Exception, e: assert 'UNEXPECTED' not in str(e), str(e) assert 'Expected to find' in str(e), str(e) Settings.CORRECT_OVERFLOWS = 0 # Roundings src = ''' #include #include int main() { TYPE x = -5; printf("*%d*", x/2); x = 5; printf("*%d*", x/2); float y = -5.33; x = y; printf("*%d*", x); y = 5.33; x = y; printf("*%d*", x); printf("\\n"); } ''' if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 0 self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here! self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-6**5*') Settings.CORRECT_ROUNDINGS = 1 Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') # Correct self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Correct self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') # Correct Settings.CORRECT_SIGNS = 0 if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 2 Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*') self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-5**5*') # And reverse the check with = 2 if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 3 Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:999"] self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') Settings.CORRECT_SIGNS = 0 def test_exit_status(self): if self.emcc_args is None: return self.skip('need emcc') src = r''' #include #include static void cleanup() { printf("cleanup\n"); } int main() { atexit(cleanup); // this atexit should still be called printf("hello, world!\n"); exit(118); // Unusual exit status to make sure it's working! } ''' open('post.js', 'w').write(''' Module.addOnExit(function () { Module.print('I see exit status: ' + EXITSTATUS); }); Module.callMain(); ''') self.emcc_args += ['-s', 'INVOKE_RUN=0', '--post-js', 'post.js'] self.do_run(src, 'hello, world!\ncleanup\nI see exit status: 118') def test_gc(self): if self.emcc_args == None: return self.skip('needs ta2') if Settings.ASM_JS: return self.skip('asm cannot support generic function table') Settings.GC_SUPPORT = 1 src = r''' #include #include #include void *global; void finalizer(void *ptr, void *arg) { printf("finalizing %d (global == %d)\n", (int)arg, ptr == global); } void finalizer2(void *ptr, void *arg) { printf("finalizing2 %d (global == %d)\n", (int)arg, ptr == global); } int main() { GC_INIT(); void *local, *local2, *local3, *local4, *local5, *local6; // Hold on to global, drop locals global = GC_MALLOC(1024); // rooted since in a static allocation GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); printf("alloc %p\n", global); local = GC_MALLOC(1024); // not rooted since stack is not scanned GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); printf("alloc %p\n", local); assert((char*)local - (char*)global >= 1024 || (char*)global - (char*)local >= 1024); local2 = GC_MALLOC(1024); // no finalizer printf("alloc %p\n", local2); local3 = GC_MALLOC(1024); // with finalizable2 GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer2, (void*)2, 0, 0); printf("alloc %p\n", local); local4 = GC_MALLOC(1024); // yet another GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer2, (void*)3, 0, 0); printf("alloc %p\n", local); printf("basic test\n"); GC_FORCE_COLLECT(); printf("*\n"); GC_FREE(global); // force free will actually work // scanning inside objects global = GC_MALLOC(12); GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); local = GC_MALLOC(12); GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); local2 = GC_MALLOC_ATOMIC(12); GC_REGISTER_FINALIZER_NO_ORDER(local2, finalizer, (void*)2, 0, 0); local3 = GC_MALLOC(12); GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer, (void*)3, 0, 0); local4 = GC_MALLOC(12); GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer, (void*)4, 0, 0); local5 = GC_MALLOC_UNCOLLECTABLE(12); // This should never trigger since local5 is uncollectable GC_REGISTER_FINALIZER_NO_ORDER(local5, finalizer, (void*)5, 0, 0); printf("heap size = %d\n", GC_get_heap_size()); local4 = GC_REALLOC(local4, 24); printf("heap size = %d\n", GC_get_heap_size()); local6 = GC_MALLOC(12); GC_REGISTER_FINALIZER_NO_ORDER(local6, finalizer, (void*)6, 0, 0); // This should be the same as a free GC_REALLOC(local6, 0); void **globalData = (void**)global; globalData[0] = local; globalData[1] = local2; void **localData = (void**)local; localData[0] = local3; void **local2Data = (void**)local2; local2Data[0] = local4; // actually ignored, because local2 is atomic, so 4 is freeable printf("object scan test test\n"); GC_FORCE_COLLECT(); printf("*\n"); GC_FREE(global); // force free will actually work printf("*\n"); GC_FORCE_COLLECT(); printf(".\n"); global = 0; return 0; } ''' self.do_run(src, '''basic test finalizing 1 (global == 0) finalizing2 2 (global == 0) finalizing2 3 (global == 0) * finalizing 0 (global == 1) heap size = 72 heap size = 84 finalizing 6 (global == 0) object scan test test finalizing 4 (global == 0) * finalizing 0 (global == 1) * finalizing 1 (global == 0) finalizing 2 (global == 0) finalizing 3 (global == 0) . ''') # Generate tests for everything def make_run(fullname, name=-1, compiler=-1, embetter=0, quantum_size=0, typed_arrays=0, emcc_args=None, env=None): if env is None: env = {} TT = type(fullname, (T,), dict(run_name = fullname, env = env)) def tearDown(self): super(TT, self).tearDown() for k, v in self.env.iteritems(): del os.environ[k] # clear global changes to Building Building.COMPILER_TEST_OPTS = [] Building.COMPILER = CLANG Building.LLVM_OPTS = 0 TT.tearDown = tearDown def setUp(self): super(TT, self).setUp() for k, v in self.env.iteritems(): assert k not in os.environ, k + ' should not be in environment' os.environ[k] = v global checked_sanity if not checked_sanity: print '(checking sanity from test runner)' # do this after we set env stuff check_sanity(force=True) checked_sanity = True Building.COMPILER_TEST_OPTS = ['-g'] os.chdir(self.get_dir()) # Ensure the directory exists and go there Building.COMPILER = compiler self.emcc_args = None if emcc_args is None else emcc_args[:] if self.emcc_args is not None: Settings.load(self.emcc_args) Building.LLVM_OPTS = 0 if '-O2' in self.emcc_args: Building.COMPILER_TEST_OPTS = [] # remove -g in -O2 tests, for more coverage #Building.COMPILER_TEST_OPTS += self.emcc_args for arg in self.emcc_args: if arg.startswith('-O'): Building.COMPILER_TEST_OPTS.append(arg) # so bitcode is optimized too, this is for cpp to ll else: try: key, value = arg.split('=') Settings[key] = value # forward -s K=V except: pass return # TODO: Move much of these to a init() function in shared.py, and reuse that Settings.USE_TYPED_ARRAYS = typed_arrays Settings.INVOKE_RUN = 1 Settings.RELOOP = 0 # we only do them in the "o2" pass Settings.MICRO_OPTS = embetter Settings.QUANTUM_SIZE = quantum_size Settings.ASSERTIONS = 1-embetter Settings.SAFE_HEAP = 1-embetter Settings.CHECK_OVERFLOWS = 1-embetter Settings.CORRECT_OVERFLOWS = 1-embetter Settings.CORRECT_SIGNS = 0 Settings.CORRECT_ROUNDINGS = 0 Settings.CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = [] Settings.CHECK_SIGNS = 0 #1-embetter Settings.RUNTIME_TYPE_INFO = 0 Settings.DISABLE_EXCEPTION_CATCHING = 0 Settings.INCLUDE_FULL_LIBRARY = 0 Settings.BUILD_AS_SHARED_LIB = 0 Settings.RUNTIME_LINKED_LIBS = [] Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2) Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0 Settings.PRECISE_I64_MATH = 0 Settings.NAMED_GLOBALS = 0 if not embetter else 1 TT.setUp = setUp return TT # Make one run with the defaults default = make_run("default", compiler=CLANG, emcc_args=[]) # Make one run with -O1, with safe heap o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "ASM_JS=0", "-s", "SAFE_HEAP=1"]) # Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow) o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=0", "-s", "JS_CHUNK_SIZE=1024"]) # asm.js asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"]) asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"]) asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-O2", "-s", "PRECISE_F32=1"]) asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "CHECK_HEAP_ALIGN=1"]) asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"}) # Make custom runs with various options for compiler, quantum, embetter, typed_arrays in [ (CLANG, 4, 0, 0), (CLANG, 4, 1, 1), ]: fullname = 's_0_%d%s%s' % ( embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays) ) locals()[fullname] = make_run(fullname, fullname, compiler, embetter, quantum, typed_arrays) del T # T is just a shape for the specific subclasses, we don't test it itself