Skip to content
Permalink
1.29.12
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
7435 lines (5999 sloc) 260 KB
# coding=utf-8
import glob, hashlib, os, re, shutil, subprocess, sys
import tools.shared
from tools.shared import *
from tools.line_endings import check_line_endings
from runner import RunnerCore, path_from_root, checked_sanity, test_modes, get_bullet_library
class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline
def is_emscripten_abi(self):
return not ('i386-pc-linux-gnu' in COMPILER_OPTS or self.env.get('EMCC_LLVM_TARGET') == 'i386-pc-linux-gnu')
def is_emterpreter(self):
return 'EMTERPRETIFY=1' in self.emcc_args
def test_hello_world(self):
test_path = path_from_root('tests', 'core', 'test_hello_world')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
src = open(self.in_dir('src.cpp.o.js')).read()
assert 'EMSCRIPTEN_GENERATED_FUNCTIONS' not in src, 'must not emit this unneeded internal thing'
def test_intvars(self):
if self.emcc_args == None: return self.skip('needs ta2')
test_path = path_from_root('tests', 'core', 'test_intvars')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_sintvars(self):
Settings.CORRECT_SIGNS = 1 # Relevant to this test
Settings.CORRECT_OVERFLOWS = 0 # We should not need overflow correction to get this right
test_path = path_from_root('tests', 'core', 'test_sintvars')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(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')
test_path = path_from_root('tests', 'core', 'test_i64_b')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i64_cmp(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_cmp')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i64_cmp2(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_cmp2')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i64_double(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_double')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i64_umul(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_umul')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
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
test_path = path_from_root('tests', 'core', 'test_i64_llabs')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i64_zextneg(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_zextneg')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i64_7z(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_7z')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output, ['hallo'])
def test_i64_i16(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_i16')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i64_qdouble(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_qdouble')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i64_varargs(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
test_path = path_from_root('tests', 'core', 'test_i64_varargs')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output, 'waka fleefl asdfasdfasdfasdf'.split(' '))
def test_double_varargs(self):
test_path = path_from_root('tests', 'core', 'test_double_varargs')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
def test_struct_varargs(self):
if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('struct varargs requires fastcomp')
test_path = path_from_root('tests', 'core', 'test_struct_varargs')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
def zzztest_nested_struct_varargs(self):
if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('struct varargs requires fastcomp')
test_path = path_from_root('tests', 'core', 'test_nested_struct_varargs')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
def test_i32_mul_precise(self):
if self.emcc_args == None: return self.skip('needs ta2')
test_path = path_from_root('tests', 'core', 'test_i32_mul_precise')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
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
test_path = path_from_root('tests', 'core', 'test_i32_mul_semiprecise')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_i16_emcc_intrinsic(self):
Settings.CORRECT_SIGNS = 1 # Relevant to this test
test_path = path_from_root('tests', 'core', 'test_i16_emcc_intrinsic')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_double_i64_conversion(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2')
test_path = path_from_root('tests', 'core', 'test_double_i64_conversion')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_float32_precise(self):
Settings.PRECISE_F32 = 1
test_path = path_from_root('tests', 'core', 'test_float32_precise')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_negative_zero(self):
test_path = path_from_root('tests', 'core', 'test_negative_zero')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_line_endings(self):
self.build(open(path_from_root('tests', 'hello_world.cpp')).read(), self.get_dir(), self.in_dir('hello_world.cpp'))
def test_literal_negative_zero(self):
if self.emcc_args == None: return self.skip('needs emcc')
test_path = path_from_root('tests', 'core', 'test_literal_negative_zero')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_llvm_intrinsics(self):
if self.emcc_args == None: return self.skip('needs ta2')
Settings.PRECISE_I64_MATH = 2 # for bswap64
test_path = path_from_root('tests', 'core', 'test_llvm_intrinsics')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_bswap64(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2')
test_path = path_from_root('tests', 'core', 'test_bswap64')
src, output = (test_path + s for s in ('.in', '.out'))
for emulate_fps in [0, 1]:
if os.environ.get('EMCC_FAST_COMPILER') == '0' and emulate_fps: continue # only in fastcomp
print emulate_fps
Settings.EMULATE_FUNCTION_POINTER_CASTS = emulate_fps # extra coverage for this
self.do_run_from_file(src, output)
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_asmjs_unknown_emscripten(self):
if self.emcc_args == None: return self.skip('needs emcc')
if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for asmjs-unknown-emscripten target test')
if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('fastcomp needed for asmjs-unknonw-emscripten target')
self.do_run(open(path_from_root('tests', 'asmjs-unknown-emscripten.c')).read(), '')
def test_cube2md5(self):
if self.emcc_args == None: return self.skip('needs emcc')
if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for accurate math')
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):
# extra testing for various codegen modes
try:
old_chunk_size = os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or ''
for chunk_size in ['1', old_chunk_size]: # test splitting out each function to a chunk in emscripten.py (21 functions here)
print ' chunks', chunk_size
os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = chunk_size
# 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_emscripten_abi():
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_emscripten_abi():
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
Settings.CHECK_SIGNS = 0
if Settings.USE_TYPED_ARRAYS == 2:
Settings.CORRECT_SIGNS = 0
else:
Settings.CORRECT_SIGNS = 1
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
test_path = path_from_root('tests', 'core', 'test_bitfields')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_floatvars(self):
if self.run_name == 'slow2asm': return self.skip('FIXME in slow2asm')
test_path = path_from_root('tests', 'core', 'test_floatvars')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_closebitcasts(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
test_path = path_from_root('tests', 'core', 'closebitcasts')
src, output = (test_path + s for s in ('.c', '.txt'))
self.do_run_from_file(src, output)
def test_fast_math(self):
if self.emcc_args is None: return self.skip('requires emcc')
Building.COMPILER_TEST_OPTS += ['-ffast-math']
test_path = path_from_root('tests', 'core', 'test_fast_math')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output, ['5', '6', '8'])
def test_zerodiv(self):
test_path = path_from_root('tests', 'core', 'test_zerodiv')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_zero_multiplication(self):
test_path = path_from_root('tests', 'core', 'test_zero_multiplication')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_isnan(self):
test_path = path_from_root('tests', 'core', 'test_isnan')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_globaldoubles(self):
test_path = path_from_root('tests', 'core', 'test_globaldoubles')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_math(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
test_path = path_from_root('tests', 'core', 'test_math')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_erf(self):
test_path = path_from_root('tests', 'core', 'test_erf')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
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_math_lgamma(self):
if self.emcc_args is None: return self.skip('requires emcc')
if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for accurate math')
if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('fastcomp needed for proper handling of _signgam extern')
test_path = path_from_root('tests', 'math', 'lgamma')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_frexp(self):
if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sprintf.')
test_path = path_from_root('tests', 'core', 'test_frexp')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_rounding(self):
test_path = path_from_root('tests', 'core', 'test_rounding')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_fcvt(self):
if self.emcc_args is None: return self.skip('requires emcc')
test_path = path_from_root('tests', 'core', 'test_fcvt')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_llrint(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
test_path = path_from_root('tests', 'core', 'test_llrint')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_getgep(self):
# Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP
test_path = path_from_root('tests', 'core', 'test_getgep')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
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):
test_path = path_from_root('tests', 'core', 'test_if')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_if_else(self):
test_path = path_from_root('tests', 'core', 'test_if_else')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_loop(self):
test_path = path_from_root('tests', 'core', 'test_loop')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_stack(self):
Settings.INLINING_LIMIT = 50
test_path = path_from_root('tests', 'core', 'test_stack')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_stack_align(self):
if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('fastcomp-only')
Settings.INLINING_LIMIT = 50
src = path_from_root('tests', 'core', 'test_stack_align.cpp')
self.do_run(open(src).read(), ['''align 4: 0
align 8: 0
align 16: 0
align 32: 0
base align: 0, 0, 0, 0'''])
def test_stack_restore(self):
if self.is_emterpreter(): return self.skip('generated code not available in emterpreter')
self.emcc_args += ['-g3'] # to be able to find the generated code
test_path = path_from_root('tests', 'core', 'test_stack_restore')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
generated = open('src.cpp.o.js').read()
def ensure_stack_restore_count(function_name, expected_count):
code = generated[generated.find(function_name):]
code = code[:code.find('\n}') + 2]
actual_count = code.count('STACKTOP = sp')
assert actual_count == expected_count, ('Expected %d stack restorations, got %d' % (expected_count, actual_count)) + ': ' + code
ensure_stack_restore_count('function _no_stack_usage', 0)
ensure_stack_restore_count('function _alloca_gets_restored', 1)
ensure_stack_restore_count('function _stack_usage', 1)
def test_strings(self):
if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.')
test_path = path_from_root('tests', 'core', 'test_strings')
src, output = (test_path + s for s in ('.in', '.out'))
for named in (0, 1):
print named
if os.environ.get('EMCC_FAST_COMPILER') != '0' and named: continue # no named globals in fastcomp
Settings.NAMED_GLOBALS = named
self.do_run_from_file(src, output, ['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):
test_path = path_from_root('tests', 'core', 'test_strcmp_uni')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_strndup(self):
if self.run_name.startswith('s_'): return self.skip('musl libc strndup() assumes that C strings can be loaded via i16 and i32 loads.')
test_path = path_from_root('tests', 'core', 'test_strndup')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_errar(self):
test_path = path_from_root('tests', 'core', 'test_errar')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_mainenv(self):
test_path = path_from_root('tests', 'core', 'test_mainenv')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_funcs(self):
test_path = path_from_root('tests', 'core', 'test_funcs')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_structs(self):
test_path = path_from_root('tests', 'core', 'test_structs')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
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):
test_path = path_from_root('tests', 'core', 'test_addr_of_stacked')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_globals(self):
test_path = path_from_root('tests', 'core', 'test_globals')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_linked_list(self):
test_path = path_from_root('tests', 'core', 'test_linked_list')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
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):
test_path = path_from_root('tests', 'core', 'test_assert')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_libcextra(self):
if self.emcc_args is None: return self.skip('needs emcc for libcextra')
test_path = path_from_root('tests', 'core', 'test_libcextra')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_regex(self):
if self.emcc_args is None: return self.skip('needs emcc for libcextra')
test_path = path_from_root('tests', 'core', 'test_regex')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp(self):
test_path = path_from_root('tests', 'core', 'test_longjmp')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp2(self):
test_path = path_from_root('tests', 'core', 'test_longjmp2')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp3(self):
test_path = path_from_root('tests', 'core', 'test_longjmp3')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp4(self):
test_path = path_from_root('tests', 'core', 'test_longjmp4')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp_funcptr(self):
test_path = path_from_root('tests', 'core', 'test_longjmp_funcptr')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp_repeat(self):
test_path = path_from_root('tests', 'core', 'test_longjmp_repeat')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp_stacked(self):
test_path = path_from_root('tests', 'core', 'test_longjmp_stacked')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp_exc(self):
test_path = path_from_root('tests', 'core', 'test_longjmp_exc')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_longjmp_throw(self):
for disable_throw in [0, 1]:
print disable_throw
Settings.DISABLE_EXCEPTION_CATCHING = disable_throw
test_path = path_from_root('tests', 'core', 'test_longjmp_throw')
src, output = (test_path + s for s in ('.cpp', '.out'))
self.do_run_from_file(src, output)
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 [1, 5, 20, 1000]:
print num
self.do_run(src.replace('NUM', str(num)), '0\n' * num)
def test_setjmp_many_2(self):
if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('non-fastcomp do not hit the limit.')
src = r'''
#include <setjmp.h>
#include <stdio.h>
jmp_buf env;
void luaWork(int d){
int x;
printf("d is at %d\n", d);
longjmp(env, 1);
}
int main()
{
const int ITERATIONS=25;
for(int i = 0; i < ITERATIONS; i++){
if(!setjmp(env)){
luaWork(i);
}
}
return 0;
}
'''
self.do_run(src, r'''d is at 24''')
def test_setjmp_noleak(self):
if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('non-fastcomp do not hit the limit.')
src = r'''
#include <setjmp.h>
#include <stdio.h>
#include <assert.h>
jmp_buf env;
void luaWork(int d){
int x;
printf("d is at %d\n", d);
longjmp(env, 1);
}
#include <malloc.h>
#include <stdlib.h>
void dump() {
struct mallinfo m = mallinfo();
printf("dump: %d , %d\n", m.arena, m.uordblks);
}
void work(int n)
{
printf("work %d\n", n);
dump();
if(!setjmp(env)){
luaWork(n);
}
if (n > 0) work(n-1);
}
int main() {
struct mallinfo m1 = mallinfo();
dump();
work(10);
dump();
struct mallinfo m2 = mallinfo();
assert(m1.arena == m2.arena && m1.uordblks == m2.uordblks);
printf("ok.\n");
}
'''
self.do_run(src, r'''ok.''')
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 and os.environ.get('EMCC_FAST_COMPILER') != '0':
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 << "Caught...";
}
try
{
function2();
}
catch (MyException e)
{
std::cout << "Caught...";
}
return 0;
}
'''
Settings.DISABLE_EXCEPTION_CATCHING = 0
self.do_run(src, 'Throw...Construct...Caught...Destruct...Throw...Construct...Copy...Caught...Destruct...Destruct...')
def test_exceptions_2(self):
if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly')
if self.run_name == 'asm2x86': return self.skip('TODO')
Settings.DISABLE_EXCEPTION_CATCHING = 0
for safe in [0,1]:
print safe
Settings.SAFE_HEAP = safe
test_path = path_from_root('tests', 'core', 'test_exceptions_2')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
def test_exceptions_3(self):
if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly')
if self.run_name == 'asm2x86': return self.skip('TODO')
if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp')
Settings.DISABLE_EXCEPTION_CATCHING = 0
src = r'''
#include <iostream>
#include <stdexcept>
int main(int argc, char **argv)
{
if (argc != 2) {
std::cout << "need an arg" << std::endl;
return 1;
}
int arg = argv[1][0] - '0';
try {
if (arg == 0) throw "a c string";
if (arg == 1) throw std::exception();
if (arg == 2) throw std::runtime_error("Hello");
} catch(const char * ex) {
std::cout << "Caught C string: " << ex << std::endl;
} catch(const std::exception &ex) {
std::cout << "Caught exception: " << ex.what() << std::endl;
} catch(...) {
std::cout << "Caught something else" << std::endl;
}
std::cout << "Done.\n";
}
'''
print '0'
self.do_run(src, 'Caught C string: a c string\nDone.', ['0'])
print '1'
self.do_run(src, 'Caught exception: std::exception\nDone.', ['1'], no_build=True)
print '2'
self.do_run(src, 'Caught exception: Hello\nDone.', ['2'], no_build=True)
def test_exceptions_white_list(self):
if self.emcc_args is None: return self.skip('requires emcc')
Settings.DISABLE_EXCEPTION_CATCHING = 2
Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"]
Settings.INLINING_LIMIT = 50 # otherwise it is inlined and not identified
test_path = path_from_root('tests', 'core', 'test_exceptions_white_list')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
size = len(open('src.cpp.o.js').read())
shutil.copyfile('src.cpp.o.js', 'orig.js')
if os.environ.get('EMCC_FAST_COMPILER') != '0':
# check that an empty whitelist works properly (as in, same as exceptions disabled)
empty_output = path_from_root('tests', 'core', 'test_exceptions_white_list_empty.out')
Settings.EXCEPTION_CATCHING_WHITELIST = []
self.do_run_from_file(src, empty_output)
empty_size = len(open('src.cpp.o.js').read())
shutil.copyfile('src.cpp.o.js', 'empty.js')
Settings.EXCEPTION_CATCHING_WHITELIST = ['fake']