summaryrefslogtreecommitdiff
path: root/tests/test_core.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_core.py')
-rw-r--r--tests/test_core.py10074
1 files changed, 10074 insertions, 0 deletions
diff --git a/tests/test_core.py b/tests/test_core.py
new file mode 100644
index 00000000..31db6ca5
--- /dev/null
+++ b/tests/test_core.py
@@ -0,0 +1,10074 @@
+# 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <time.h>
+ #include <stdio.h>
+ #include <stdint.h>
+
+ 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, x1<x2, x1<=x2, x1>x2, x1>=x2, // note: some rounding in the printing!
+ x1==x3, x1<x3, x1<=x3, x1>x3, 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 <stdio.h>
+ #include <limits>
+
+ 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<long long>::min() ? "Ok": "Fail" );
+ printf( "%s\n", i < std::numeric_limits<long long>::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 <stdio.h>
+ #include <stdint.h>
+
+ 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 <stdio.h>
+ #include <sys/time.h>
+
+ 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 <stdio.h>
+
+ 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 <inttypes.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <inttypes.h>
+ #include <stdio.h>
+
+ 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 <inttypes.h>
+ #include <stdio.h>
+
+ 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 <inttypes.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdint.h>
+ #include <stdio.h>
+
+ 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 <stdint.h>
+ #include <stdio.h>
+ 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 <stdint.h>
+ #include <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ #include <stdint.h>
+ #include <stdarg.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ 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_negative_zero(self):
+ src = r'''
+ #include <stdio.h>
+ #include <math.h>
+
+ 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 <stdio.h>
+ #include <sys/types.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ #include <iostream>
+ #include <string>
+ #include <sstream>
+
+ 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 <seed>',
+ 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
+
+ def test_unaligned(self):
+ if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1')
+
+ src = r'''
+ #include<stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ // 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+
+ // headers test, see issue #1013
+ #include<cfloat>
+ #include<cmath>
+
+ 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_isnan(self):
+ src = r'''
+ #include <stdio.h>
+
+ 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 <stdlib.h>
+ #include <stdio.h>
+
+ 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):
+ src = '''
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <cmath>
+ int main()
+ {
+ 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);
+ 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*')
+
+ def test_erf(self):
+ src = '''
+ #include <math.h>
+ #include <stdio.h>
+ 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 <stdio.h>
+ #include <math.h>
+ #include <assert.h>
+
+ 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)<tol);
+ assert(x==0 || (fabs(x)>=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 <stdio.h>
+ #include <math.h>
+
+ 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 <stdlib.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+ #include <math.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+
+ 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_xyz_543_defg", "abc_%d.%2x_xyz_%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 <stdio.h>
+ #include <string.h>
+ 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 <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+
+ 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 <stdio.h>
+ #include <errno.h>
+ #include <string.h>
+
+ 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 = '''
+ <Math arg out of domain of func>
+ <No more processes>
+ <34>
+ <123>
+ '''
+ self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
+
+ def test_mainenv(self):
+ src = '''
+ #include <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ #include <stdlib.h>
+ #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 <stdio.h>
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <assert.h>
+ 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 <stdio.h>
+ #include <wchar.h>
+
+ int main()
+ {
+ const wchar_t* wstr = L"Hello";
+
+ printf("wcslen: %d\n", wcslen(wstr));
+
+ return 0;
+ }
+ '''
+ self.do_run(src, 'wcslen: 5')
+
+ def test_longjmp(self):
+ src = r'''
+ #include <stdio.h>
+ #include <setjmp.h>
+
+ 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 <setjmp.h>
+ #include <stdio.h>
+
+ 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 <setjmp.h>
+ #include <stdio.h>
+
+ 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 <setjmp.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+ #include <setjmp.h>
+
+ 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 <stdio.h>
+ #include <setjmp.h>
+
+ 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 <stdio.h>
+ #include <setjmp.h>
+ #include <stdlib.h>
+ #include <string.h>
+
+ 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 <stdlib.h>
+ #include <stdio.h>
+ #include <setjmp.h>
+ #include <emscripten.h>
+
+ 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 <stdio.h>
+ #include <setjmp.h>
+
+ 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 <stdio.h>
+ 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 <iostream>
+
+ 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 <stdexcept>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <exception>
+ 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 <fstream>
+ #include <iostream>
+ 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 <stdio.h>
+
+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 <stdio.h>
+ #include <exception>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+ #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 <stdio.h>
+ #include <stdlib.h>
+
+ 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(1) called\nExit Status: 1\npostRun\nok.\n''')
+
+ def test_class(self):
+ src = '''
+ #include <stdio.h>
+ 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 <stdio.h>
+ 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 <iostream>
+ int main() {
+ using namespace std;
+ use_facet<num_put<char> >(cout.getloc()).put(cout, cout, '0', 3.14159265);
+ }
+ '''
+ self.do_run(src, '3.14159')
+
+ def test_polymorph(self):
+ if self.emcc_args is None: return self.skip('requires emcc')
+ src = '''
+ #include <stdio.h>
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ struct Support {
+ virtual void f() {
+ printf("f()\n");
+ }
+ };
+
+ struct Derived : Support {
+ };
+
+ int main() {
+ Support * p = new Derived;
+ dynamic_cast<Derived*>(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 <stdio.h>
+
+ 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<CDerivedest*>(pa) != NULL);
+ printf("a2: %d\\n", dynamic_cast<CDerived*>(pa) != NULL);
+ printf("a3: %d\\n", dynamic_cast<CBase*>(pa) != NULL);
+
+ printf("b1: %d\\n", dynamic_cast<CDerivedest*>(pb) != NULL);
+ printf("b2: %d\\n", dynamic_cast<CDerived*>(pb) != NULL);
+ printf("b3: %d\\n", dynamic_cast<CBase*>(pb) != NULL);
+
+ printf("c1: %d\\n", dynamic_cast<CDerivedest*>(pc) != NULL);
+ printf("c2: %d\\n", dynamic_cast<CDerived*>(pc) != NULL);
+ printf("c3: %d\\n", dynamic_cast<CBase*>(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 <stdio.h>
+ #include <typeinfo>
+
+ class Class {};
+
+ int main() {
+ const Class* dp = dynamic_cast<const Class*>(&typeid(Class));
+ // should return dp == NULL,
+ printf("pointer: %p\n", dp);
+ }
+ '''
+ self.do_run(src, "pointer: (nil)")
+
+ def test_funcptr(self):
+ src = '''
+ #include <stdio.h>
+ 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 <math.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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<stdio.h>
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+ #include <stdarg.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ #include <assert.h>
+
+ #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 <stdio.h>
+
+ 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 <stdio.h>
+
+ // 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+ #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 <stdio.h>
+ #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_inlinejs(self):
+ if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
+ src = r'''
+ #include <stdio.h>
+
+ double get() {
+ double ret = 0;
+ __asm __volatile__("Math.abs(-12/3.3)":"=r"(ret)); // write to a variable
+ return ret;
+ }
+
+ int main() {
+ asm("Module.print('Inline JS is very cool')");
+ printf("%.2f\n", get());
+ return 0;
+ }
+ '''
+
+ self.do_run(src, 'Inline JS is very cool\n3.64')
+
+ def test_inlinejs2(self):
+ if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
+ src = r'''
+ #include <stdio.h>
+
+ 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;"); // multiline
+ asm __volatile__("Module.print($_$1); Module.print('\n')");
+ }
+
+ int main(int argc, char **argv) {
+ printf("%d\n", mix(argc, argc/2));
+ mult();
+ return 0;
+ }
+ '''
+
+ self.do_run(src, '4\n200\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 <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <assert.h>
+ #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 <stdio.h>
+
+ // 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+
+ 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 <stdio.h>
+
+ #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 Settings.RELOOP: return self.skip('TODO: switch in relooper, issue #781')
+ if Settings.ASM_JS: return self.skip('TODO: switch too large for asm')
+
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ #include <string.h>
+
+ #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 <stdio.h>
+ #include <stdarg.h>
+
+ 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 <BAD+0><BAD+1>.....
+ 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", "<BAD+0>", "<BAD+1>", "<BAD+2>", "<BAD+3>", "<BAD+4>", "<BAD+5>", "<BAD+6>", "<BAD+7>", "<BAD+8>", "<BAD+9>", "<BAD+10>", "<BAD+11>", "<BAD+12>", "<BAD+13>", "<BAD+14>", "<BAD+15>", "<BAD+16>");
+ }
+
+ #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<BAD+0><BAD+1><BAD+2><BAD+3><BAD+4>\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 <stdio.h>
+ #include <stdarg.h>
+
+ 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 <stdio.h>
+ #include <fcntl.h>
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #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 <stdio.h>
+ #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 <stdio.h>
+ #include <stdlib.h>
+ #include <sys/time.h>
+
+ 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 <stdio.h>
+ #include <stdbool.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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_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 <stdio.h>
+ #include <assert.h>
+ #include <sys/timeb.h>
+
+ 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 <time.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+ #include <time.h>
+ #include <assert.h>
+
+ 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 <time.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ 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 <time.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ 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 <time.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ 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 <time.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ 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 0;
+ }
+ '''
+ self.do_run(src, 'fault on write to 0' if not Settings.ASM_JS else 'abort()')
+
+ def test_trickystring(self):
+ src = r'''
+ #include <stdio.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+
+ #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 <stdio.h>
+ #include <math.h>
+ #include <string.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+ #include <assert.h>
+
+ #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 <stdio.h>
+ #include <string.h>
+ #include <assert.h>
+ 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 <unistd.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+
+ 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 <stdio.h> /* for printf */
+ #include <stdlib.h> /* for exit */
+ #include <getopt.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+ 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 <stdio.h>
+ #include <string.h>
+ #include <assert.h>
+ 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 <stdio.h>
+ #include <string.h>
+ 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_bsearch(self):
+ if Settings.QUANTUM_SIZE == 1: return self.skip('Test cannot work with q1')
+
+ src = '''
+ #include <stdlib.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+ #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('shared libs are 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 test_dlfcn_basic(self):
+ return self.skip('shared libs are deprecated')
+ if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+
+ Settings.NAMED_GLOBALS = 1
+ Settings.LINKABLE = 1
+
+ lib_src = '''
+ #include <cstdio>
+
+ class Foo {
+ public:
+ Foo() {
+ printf("Constructing lib object.\\n");
+ }
+ };
+
+ Foo global;
+ '''
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.cpp')
+ Settings.BUILD_AS_SHARED_LIB = 1
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ src = '''
+ #include <cstdio>
+ #include <dlfcn.h>
+
+ class Bar {
+ public:
+ Bar() {
+ printf("Constructing main object.\\n");
+ }
+ };
+
+ Bar global;
+
+ int main() {
+ dlopen("liblib.so", RTLD_NOW);
+ return 0;
+ }
+ '''
+ Settings.BUILD_AS_SHARED_LIB = 0
+ add_pre_run_and_checks = '''
+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)
+'''
+ self.do_run(src, 'Constructing main object.\nConstructing lib object.\n',
+ post_build=add_pre_run_and_checks)
+
+ def test_dlfcn_qsort(self):
+ return self.skip('shared libs are deprecated')
+ if self.emcc_args is None: return self.skip('requires emcc')
+ if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+
+ Settings.LINKABLE = 1
+ Settings.NAMED_GLOBALS = 1
+
+ if Settings.USE_TYPED_ARRAYS == 2:
+ Settings.CORRECT_SIGNS = 1 # Needed for unsafe optimizations
+
+ 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')
+ Settings.BUILD_AS_SHARED_LIB = 1
+ Settings.EXPORTED_FUNCTIONS = ['_get_cmp']
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ src = '''
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <dlfcn.h>
+
+ 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};
+
+ 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), main_cmp);
+ printf("Sort with main comparison: ");
+ for (int i = 0; i < 5; i++) {
+ printf("%d ", arr[i]);
+ }
+ printf("\\n");
+
+ 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;
+ }
+ '''
+ Settings.BUILD_AS_SHARED_LIB = 0
+ Settings.EXPORTED_FUNCTIONS = ['_main']
+ add_pre_run_and_checks = '''
+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)
+'''
+ 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=add_pre_run_and_checks)
+
+ def test_dlfcn_data_and_fptr(self):
+ return self.skip('shared libs are deprecated')
+ if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+ if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
+
+ Settings.LINKABLE = 1
+ Settings.NAMED_GLOBALS = 1
+
+ lib_src = '''
+ #include <stdio.h>
+
+ 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.BUILD_AS_SHARED_LIB = 1
+ 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'))
+
+ src = '''
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ typedef void (*FUNCTYPE(int, void(*)()))();
+
+ FUNCTYPE func;
+
+ void 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.BUILD_AS_SHARED_LIB = 0
+ Settings.EXPORTED_FUNCTIONS = ['_main']
+ Settings.EXPORTED_GLOBALS = []
+ add_pre_run_and_checks = '''
+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)
+'''
+ 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=add_pre_run_and_checks)
+
+ def test_dlfcn_alias(self):
+ return self.skip('shared libs are deprecated')
+ if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+
+ 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 <stdio.h>
+ 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 <dlfcn.h>
+
+ 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']
+ add_pre_run_and_checks = '''
+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)
+'''
+ self.do_run(src, 'Parent global: 123.*Parent global: 456.*',
+ output_nicerizer=lambda x, err: x.replace('\n', '*'),
+ post_build=add_pre_run_and_checks,
+ 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):
+ return self.skip('shared libs are deprecated')
+ if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+
+ Settings.LINKABLE = 1
+ Settings.NAMED_GLOBALS = 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')
+
+ 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.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 <stdarg.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ 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);
+ fptr = (void (*)())dlsym(lib_handle, "func");
+ fptr();
+
+ return 0;
+ }
+ '''
+ Settings.BUILD_AS_SHARED_LIB = 0
+ Settings.EXPORTED_FUNCTIONS = ['_main']
+ add_pre_run_and_checks = '''
+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)
+'''
+ self.do_run(src, '100\n200\n13\n42\n',
+ post_build=add_pre_run_and_checks)
+
+ 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 <stdio.h>
+#include <dlfcn.h>
+
+int global = 123;
+
+extern "C" __attribute__((noinline)) void foo(int x) {
+printf("%d\n", x);
+}
+
+extern "C" __attribute__((noinline)) 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!')
+ import json
+ table = json.loads(table[table.find('{'):table.rfind('}')+1])
+ actual = list(sorted(table.keys()))
+ # ensure there aren't too many globals; we don't want unnamed_addr
+ assert actual == ['_foo', '_global', '_main', '_repeatable'], \
+ "Symbol table does not match: %s" % actual
+
+ self.do_run(src, '123\n123', post_build=(None, post))
+
+ def test_rand(self):
+ return self.skip('rand() is now random') # FIXME
+
+ src = r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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<stdio.h>
+ #include<string.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <stdarg.h>
+
+ 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 <stdio.h>
+ #include <stdarg.h>
+ #include <stdint.h>
+
+ 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 <stdio.h>
+ 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 <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ 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);
+ }
+
+ 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')
+
+ def test_sscanf_2(self):
+ # doubles
+ if Settings.USE_TYPED_ARRAYS == 2:
+ for ftype in ['float', 'double']:
+ src = r'''
+ #include <stdio.h>
+
+ 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<stdio.h>
+ 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<stdio.h>
+
+ 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<stdio.h>
+
+ 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 <stdint.h>
+ #include <stdio.h>
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+ 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 <stdint.h>
+ #include <stdio.h>
+
+ 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_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):
+ 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
+
+ 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()
+ 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'])
+
+ 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 <stdio.h>
+ #include <unistd.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ #include <assert.h>
+ #include <float.h>
+ 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}}',
+ \'\'\'
+ FS.createDataFile('/', 'file', 'abcdef', true, true);
+ FS.createDevice('/', 'device', function() {}, function() {});
+ \'\'\'
+ )
+ open(filename, 'w').write(src)
+'''
+ src = r'''
+ #include <stdio.h>
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <poll.h>
+
+ 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 <stdio.h>
+ #include <errno.h>
+ #include <sys/statvfs.h>
+
+ 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 <stdio.h>
+ #include <libgen.h>
+
+ 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 <stdio.h>
+ #include <emscripten.h>
+
+ 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_direct_string_constant_usage(self):
+ if self.emcc_args is None: return self.skip('requires libcxx')
+
+ src = '''
+ #include <iostream>
+ template<int i>
+ 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 <iostream>
+
+ 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<int i>
+ 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 <string>
+ #include <sstream>
+ #include <iostream>
+
+ 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_unistd_access(self):
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'access.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
+ src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read()
+ expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read()
+ self.do_run(src, expected, post_build=add_pre_run)
+
+ def test_unistd_curdir(self):
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'curdir.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
+ 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, post_build=add_pre_run)
+
+ 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):
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'truncate.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
+ src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read()
+ expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read()
+ self.do_run(src, expected, post_build=add_pre_run)
+
+ 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):
+ src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read()
+ self.do_run(src, 'success', force_c=True)
+
+ def test_unistd_links(self):
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'links.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
+ src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read()
+ expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read()
+ self.do_run(src, expected, post_build=add_pre_run)
+
+ 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):
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'io.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
+ src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read()
+ expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read()
+ self.do_run(src, expected, post_build=add_pre_run)
+
+ def test_unistd_misc(self):
+ src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read()
+ expected = open(path_from_root('tests', 'unistd', 'misc.out'), 'r').read()
+ self.do_run(src, expected)
+
+ def test_uname(self):
+ src = r'''
+ #include <stdio.h>
+ #include <sys/utsname.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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_inet(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+
+ int main() {
+ printf("*%x,%x,%x,%x,%x,%x*\n", htonl(0xa1b2c3d4), htonl(0xfe3572e0), htonl(0x07abcdf0), htons(0xabcd), ntohl(0x43211234), ntohs(0xbeaf));
+ in_addr_t i = inet_addr("190.180.10.78");
+ printf("%x\n", i);
+ return 0;
+ }
+ '''
+ self.do_run(src, '*d4c3b2a1,e07235fe,f0cdab07,cdab,34122143,afbe*\n4e0ab4be\n')
+
+ def test_inet2(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+
+ int main() {
+ struct in_addr x, x2;
+ int *y = (int*)&x;
+ *y = 0x12345678;
+ printf("%s\n", inet_ntoa(x));
+ int r = inet_aton(inet_ntoa(x), &x2);
+ printf("%s\n", inet_ntoa(x2));
+ return 0;
+ }
+ '''
+ self.do_run(src, '120.86.52.18\n120.86.52.18\n')
+
+ def test_inet3(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+ int main() {
+ char dst[64];
+ struct in_addr x, x2;
+ int *y = (int*)&x;
+ *y = 0x12345678;
+ printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst));
+ int r = inet_aton(inet_ntoa(x), &x2);
+ printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst));
+ return 0;
+ }
+ '''
+ self.do_run(src, '120.86.52.18\n120.86.52.18\n')
+
+ def test_inet4(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
+
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+
+ void test(char *test_addr){
+ char str[40];
+ struct in6_addr addr;
+ unsigned char *p = (unsigned char*)&addr;
+ int ret;
+ ret = inet_pton(AF_INET6,test_addr,&addr);
+ if(ret == -1) return;
+ if(ret == 0) return;
+ if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return;
+ printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n",
+ p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str);
+ }
+ int main(){
+ test("::");
+ test("::1");
+ test("::1.2.3.4");
+ test("::17.18.19.20");
+ test("::ffff:1.2.3.4");
+ test("1::ffff");
+ test("::255.255.255.255");
+ test("0:ff00:1::");
+ test("0:ff::");
+ test("abcd::");
+ test("ffff::a");
+ test("ffff::a:b");
+ test("ffff::a:b:c");
+ test("ffff::a:b:c:d");
+ test("ffff::a:b:c:d:e");
+ test("::1:2:0:0:0");
+ test("0:0:1:2:3::");
+ test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ test("1::255.255.255.255");
+
+ //below should fail and not produce results..
+ test("1.2.3.4");
+ test("");
+ test("-");
+ }
+ '''
+ self.do_run(src,
+ "0000:0000:0000:0000:0000:0000:0000:0000 - ::\n"
+ "0000:0000:0000:0000:0000:0000:0000:0001 - ::1\n"
+ "0000:0000:0000:0000:0000:0000:0102:0304 - ::1.2.3.4\n"
+ "0000:0000:0000:0000:0000:0000:1112:1314 - ::17.18.19.20\n"
+ "0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4\n"
+ "0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff\n"
+ "0000:0000:0000:0000:0000:0000:ffff:ffff - ::255.255.255.255\n"
+ "0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::\n"
+ "0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::\n"
+ "abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::\n"
+ "ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a\n"
+ "ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b\n"
+ "ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c\n"
+ "ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d\n"
+ "ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e\n"
+ "0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0\n"
+ "0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::\n"
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n"
+ "0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff\n"
+ )
+
+ def test_gethostbyname(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip("assume t2 in gethostbyname")
+
+ src = r'''
+ #include <netdb.h>
+ #include <stdio.h>
+
+ void test(char *hostname) {
+ hostent *host = gethostbyname(hostname);
+ if (!host) {
+ printf("no such thing\n");
+ return;
+ }
+ printf("%s : %d : %d\n", host->h_name, host->h_addrtype, host->h_length);
+ char **name = host->h_aliases;
+ while (*name) {
+ printf("- %s\n", *name);
+ name++;
+ }
+ name = host->h_addr_list;
+ while (name && *name) {
+ printf("* ");
+ for (int i = 0; i < host->h_length; i++)
+ printf("%d.", (*name)[i]);
+ printf("\n");
+ name++;
+ }
+ }
+
+ int main() {
+ test("www.cheezburger.com");
+ test("fail.on.this.never.work"); // we will "work" on this - because we are just making aliases of names to ips
+ test("localhost");
+ return 0;
+ }
+ '''
+ self.do_run(src, '''www.cheezburger.com : 2 : 4
+* -84.29.1.0.
+fail.on.this.never.work : 2 : 4
+* -84.29.2.0.
+localhost : 2 : 4
+* -84.29.3.0.
+''')
+
+ 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 <stdio.h>
+ #include <strings.h>
+ 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 <stdio.h>
+ 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 <stdlib.h>
+#include <stdio.h>
+
+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<argc+10;i++)
+ {
+ if (state % 2 == 0)
+ r.e.c.a = 3;
+ else
+ printf("%d\n", r.e.c.a);
+ state++;
+ }
+ return 0;
+}
+ '''
+
+ self.do_run(src, '3\n3\n3\n3\n3\n')
+
+ # libc++ tests
+
+ def test_iostream(self):
+ if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1")
+
+ if self.emcc_args is None:
+ if Building.LLVM_OPTS: return self.skip('optimizing bitcode before emcc can confuse libcxx inclusion')
+ self.emcc_args = [] # libc++ auto-inclusion is only done if we use emcc
+ Settings.SAFE_HEAP = 0 # Some spurious warnings from libc++ internals
+
+ src = '''
+ #include <iostream>
+
+ 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 <vector>
+ #include <stdio.h>
+
+ 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<S> 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 <stdio.h>
+
+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<Foo*>(0xDEAD111F);
+const Foo *magic2 = reinterpret_cast<Foo*>(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 <jansson.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ 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; i<json_array_size(array); ++i)
+ {
+ json_t *arrayNode = json_array_get(array, i);
+ if(!root || !json_is_string(arrayNode))
+ return 0;
+ printf("%s\\n", json_string_value(arrayNode));
+ }
+
+ json_t *dict = json_object_get(root, "dict");
+ if(!dict || !json_is_object(dict))
+ return 0;
+
+ json_t *numberNode = json_object_get(dict, "number");
+ json_t *floatNode = json_object_get(dict, "float");
+
+ if(!numberNode || !json_is_number(numberNode) ||
+ !floatNode || !json_is_real(floatNode))
+ return 0;
+
+ printf("%i\\n", json_integer_value(numberNode));
+ printf("%.2f\\n", json_number_value(numberNode));
+ printf("%.2f\\n", json_real_value(floatNode));
+
+ json_t *invalidNode = json_object_get(dict, "invalidNode");
+ if(invalidNode)
+ return 0;
+
+ printf("%i\\n", json_number_value(invalidNode));
+
+ json_decref(root);
+
+ if(!json_is_object(root))
+ printf("jansson!\\n");
+
+ return 0;
+ }
+ '''
+ self.do_run(src, 'value\narray_item1\narray_item2\narray_item3\n3\n3.00\n2.20\nJansson: Node with ID `0` not found. Context has `10` nodes.\n0\nJansson: No JSON context.\njansson!')
+
+ ### 'Medium' tests
+
+ def test_fannkuch(self):
+ results = [ (1,0), (2,1), (3,2), (4,4), (5,7), (6,10), (7, 16), (8,22) ]
+ for i, j in results:
+ src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read()
+ self.do_run(src, 'Pfannkuchen(%d) = %d.' % (i,j), [str(i)], no_build=i>1)
+
+ 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 i, j in results:
+ src = open(path_from_root('tests', 'fasta.cpp'), 'r').read()
+ self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+ 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 <set>
+ #include <stdio.h>
+ int main() {
+ std::set<int> *fetchOriginatorNums = new std::set<int>();
+ fetchOriginatorNums->insert(171);
+ printf("hello world\\n");
+ return 0;
+ }
+ ''', 'hello world');
+
+ def test_typeid(self):
+ self.do_run(r'''
+ #include <stdio.h>
+ #include <string.h>
+ #include <typeinfo>
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ 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 <stdio.h>
+ #include <sys/mman.h>
+ #include <assert.h>
+
+ 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')
+ self.emcc_args += ['--embed-file', 'data.dat']
+
+ open(self.in_dir('data.dat'), 'w').write('data from the file ' + ('.' * 9000))
+
+ src = r'''
+ #include <stdio.h>
+ #include <sys/mman.h>
+
+ int main() {
+ printf("*\n");
+ FILE *f = fopen("data.dat", "r");
+ char *m;
+ m = (char*)mmap(NULL, 9000, PROT_READ, MAP_PRIVATE, fileno(f), 0);
+ for (int i = 0; i < 20; i++) putchar(m[i]);
+ munmap(m, 9000);
+ printf("\n");
+ m = (char*)mmap(NULL, 9000, PROT_READ, MAP_PRIVATE, fileno(f), 5);
+ for (int i = 0; i < 20; i++) putchar(m[i]);
+ munmap(m, 9000);
+ printf("\n*\n");
+ return 0;
+ }
+ '''
+ 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'
+
+ 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')
+ if Settings.ASM_JS and '-O2' not in self.emcc_args: return self.skip('mozilla bug 863867')
+
+ 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
+ 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)
+ #build_ll_hook=self.do_autodebug)
+
+ # 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 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']
+
+ 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', [os.path.join('src', '.libs', 'libBulletDynamics.a'),
+ os.path.join('src', '.libs', 'libBulletCollision.a'),
+ os.path.join('src', '.libs', 'libLinearMath.a')],
+ configure_args=['--disable-demos','--disable-dependency-tracking']),
+ includes=[path_from_root('tests', 'bullet', 'src')])
+ test()
+
+ assert 'asm2g' in test_modes
+ if self.run_name == 'asm2g':
+ # 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:
+ 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):
+ return text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\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")
+
+ 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
+ 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']
+
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ 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 <iostream>
+ #include <fstream>
+ #include <stdlib.h>
+ #include <stdio.h>
+
+ 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 <stdlib.h>
+ #include <stdio.h>
+ #include <assert.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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(('missing function: unused'), args=['a', 'b'], no_build=True)
+
+ # Normal stuff
+ run_all('normal', r'''
+ #include <stdio.h>
+ 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 <stdio.h>
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+
+ int main(int argc, char **argv) {
+ int fp = atoi(argv[1]);
+ printf("fp: %d\n", fp);
+ void (*f)(int) = reinterpret_cast<void (*)(int)>(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_embind(self):
+ if self.emcc_args is None: return self.skip('requires emcc')
+ Building.COMPILER_TEST_OPTS += ['--bind']
+
+ src = r'''
+ #include<stdio.h>
+ #include<emscripten/val.h>
+
+ using namespace emscripten;
+
+ int main() {
+ val Math = val::global("Math");
+
+ // two ways to call Math.abs
+ printf("abs(-10): %d\n", Math.call<int>("abs", -10));
+ printf("abs(-11): %d\n", Math["abs"](-11).as<int>());
+
+ 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 <stdio.h>
+ #include <SDL/SDL.h>
+ #include <emscripten/bind.h>
+ 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 <stdio.h>
+
+ 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 <stdio.h>
+ #include <string.h>
+
+ 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<stdio.h>
+ 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<stdio.h>
+ #include<stdlib.h>
+ 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<stdio.h>
+ #include<stdlib.h>
+ 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<stdio.h>
+ #include<stdlib.h>
+ 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 <stdio.h>
+ #include <assert.h>
+
+ 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 <stdio.h>
+ #include <assert.h>
+
+ __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):
+ return code.replace('{\n}', '{}')
+ self.assertIdentical(clean(no_maps_file), clean(out_file))
+ map_filename = out_filename + '.map'
+ data = json.load(open(map_filename, 'r'))
+ self.assertIdentical(out_filename, data['file'])
+ self.assertIdentical(src_filename, data['sources'][0])
+ self.assertIdentical(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.assertIdentical(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 <stdio.h>
+
+ __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 <stdio.h>
+ #include <assert.h>
+
+ 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<stdio.h>
+ 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 <stdio.h>
+ #include <assert.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+ 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!\nexit(118) called\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 <stdio.h>
+ #include <gc.h>
+ #include <assert.h>
+
+ 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", "-s", "CHECK_HEAP_ALIGN=1"])
+asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])
+asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "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 \ No newline at end of file