Permalink
Cannot retrieve contributors at this time
# 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'] | |