7753 строки
256 KiB
Python
7753 строки
256 KiB
Python
# coding=utf-8
|
|
|
|
import glob, hashlib, os, re, shutil, subprocess, sys, json
|
|
from textwrap import dedent
|
|
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_zlib_library, get_bullet_library
|
|
|
|
class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline
|
|
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):
|
|
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):
|
|
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):
|
|
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("*%lld,%lld,%lld,%lld*\\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("*%lld,%lld,%lld,%lld*\\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("*%lld,%lld,%lld,%lld,%lld*\\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: %lld\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("*%lld\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("*%lld*\n", returner1());
|
|
printf("*%lld*\n", returner2(30));
|
|
|
|
uint64_t maxx = -1ULL;
|
|
printf("*%llu*\n*%llu*\n", maxx, maxx >> 5);
|
|
|
|
// Make sure params are not modified if they shouldn't be
|
|
int64_t t = 123;
|
|
modifier1(t);
|
|
printf("*%lld*\n", t);
|
|
modifier2(t);
|
|
printf("*%lld*\n", t);
|
|
|
|
// global structs with i64s
|
|
printf("*%d,%lld*\n*%d,%lld*\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("*%lld,%lld,%lld,%lld*\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("*%lld,%lld,%lld,%lld*\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("*%lld,%lld,%lld,%lld*\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("*%lld,%lld,%lld,%lld*\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("*%lld,%lld,%lld,%lld*\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("*%llx,%llx,%llx,%llx*\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("*%llx,%llx,%llx,%llx*\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
|
|
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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 never be included, musl does its own printfing'
|
|
|
|
# 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):
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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_llvm_fabs(self):
|
|
Settings.PRECISE_F32 = 1
|
|
test_path = path_from_root('tests', 'core', 'test_llvm_fabs')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
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):
|
|
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):
|
|
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):
|
|
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_i16_emcc_intrinsic(self):
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_bswap64')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
# extra coverages
|
|
for emulate_casts in [0, 1]:
|
|
for emulate_fps in [0, 1, 2]:
|
|
print emulate_casts, emulate_fps
|
|
Settings.EMULATE_FUNCTION_POINTER_CASTS = emulate_casts
|
|
Settings.EMULATED_FUNCTION_POINTERS = emulate_fps
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sha1(self):
|
|
self.do_run(open(path_from_root('tests', 'sha1.c')).read(), 'SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6')
|
|
|
|
def test_asmjs_unknown_emscripten(self):
|
|
# No other configuration is supported, so always run this.
|
|
self.do_run(open(path_from_root('tests', 'asmjs-unknown-emscripten.c')).read(), '')
|
|
|
|
def test_cube2md5(self):
|
|
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
|
|
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):
|
|
return self.skip('LLVM marks the reads of s as fully aligned, making this test invalid')
|
|
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
|
|
|
|
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
|
|
|
|
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("*%lld*\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;
|
|
}
|
|
'''
|
|
|
|
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
|
|
''')
|
|
|
|
def test_align_moar(self):
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
def test():
|
|
self.do_run(r'''
|
|
#include <xmmintrin.h>
|
|
#include <stdio.h>
|
|
|
|
struct __attribute__((aligned(16))) float4x4
|
|
{
|
|
union
|
|
{
|
|
float v[4][4];
|
|
__m128 row[4];
|
|
};
|
|
};
|
|
|
|
float4x4 v;
|
|
__m128 m;
|
|
|
|
int main()
|
|
{
|
|
printf("Alignment: %d addr: %x\n", ((int)&v) % 16, (int)&v);
|
|
printf("Alignment: %d addr: %x\n", ((int)&m) % 16, (int)&m);
|
|
}
|
|
''', 'Alignment: 0 addr: 10\nAlignment: 0 addr: 50\n') # hardcoded addresses, just to track if this ever changes by surprise. will need normal updates.
|
|
|
|
test()
|
|
print 'relocatable'
|
|
Settings.RELOCATABLE = 1
|
|
test()
|
|
|
|
def test_unsigned(self):
|
|
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*')
|
|
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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)
|
|
|
|
print 'main module'
|
|
Settings.MAIN_MODULE = 1
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_frexp(self):
|
|
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):
|
|
Settings.PRECISE_F32 = 1 # in the move to llvm 3.7, froundf in musl became more sensitive to float/double differences
|
|
|
|
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):
|
|
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):
|
|
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):
|
|
Settings.INLINING_LIMIT = 50
|
|
src = path_from_root('tests', 'core', 'test_stack_align.cpp')
|
|
def test():
|
|
self.do_run(open(src).read(), ['''align 4: 0
|
|
align 8: 0
|
|
align 16: 0
|
|
align 32: 0
|
|
base align: 0, 0, 0, 0'''])
|
|
test()
|
|
if '-O' in str(self.emcc_args):
|
|
print 'outlining'
|
|
Settings.OUTLINING_LIMIT = 60
|
|
test()
|
|
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_strings')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
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):
|
|
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):
|
|
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;
|
|
}
|
|
'''
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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):
|
|
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 << "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):
|
|
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):
|
|
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):
|
|
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')
|
|
|
|
# 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']
|
|
self.do_run_from_file(src, empty_output)
|
|
fake_size = len(open('src.cpp.o.js').read())
|
|
shutil.copyfile('src.cpp.o.js', 'fake.js')
|
|
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 1
|
|
self.do_run_from_file(src, empty_output)
|
|
disabled_size = len(open('src.cpp.o.js').read())
|
|
shutil.copyfile('src.cpp.o.js', 'disabled.js')
|
|
|
|
print size, empty_size, fake_size, disabled_size
|
|
|
|
assert size - empty_size > 0.0025*size, [empty_size, size] # big change when we disable entirely
|
|
assert size - fake_size > 0.0025*size, [fake_size, size]
|
|
assert abs(empty_size - fake_size) < 0.007*size, [empty_size, fake_size]
|
|
assert empty_size - disabled_size < 0.007*size, [empty_size, disabled_size] # full disable removes a little bit more
|
|
assert fake_size - disabled_size < 0.007*size, [disabled_size, fake_size]
|
|
|
|
def test_exceptions_white_list_2(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 2
|
|
Settings.EXCEPTION_CATCHING_WHITELIST = ["_main"]
|
|
Settings.INLINING_LIMIT = 50 # otherwise it is inlined and not identified
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_white_list_2')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_uncaught(self):
|
|
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_exceptions_typed(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
self.emcc_args += ['-s', 'SAFE_HEAP=0'] # Throwing null will cause an ignorable null pointer access.
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_typed')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_virtual_inheritance(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_virtual_inheritance')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_convert(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_convert')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_multi(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_multi')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_std(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
Settings.ERROR_ON_UNDEFINED_SYMBOLS = 1
|
|
self.emcc_args += ['-s', 'SAFE_HEAP=0']
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_std')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_alias(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_alias')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_rethrow(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_rethrow')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_resume(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
Settings.EXCEPTION_DEBUG = 1
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_resume')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_destroy_virtual(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_destroy_virtual')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_refcount(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_refcount')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_exceptions_primary(self):
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
test_path = path_from_root('tests', 'core', 'test_exceptions_primary')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_bad_typeid(self):
|
|
Settings.ERROR_ON_UNDEFINED_SYMBOLS = 1
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
|
|
self.do_run(r'''
|
|
// exception example
|
|
#include <iostream> // std::cerr
|
|
#include <typeinfo> // operator typeid
|
|
#include <exception> // std::exception
|
|
|
|
class Polymorphic {virtual void member(){}};
|
|
|
|
int main () {
|
|
try
|
|
{
|
|
Polymorphic * pb = 0;
|
|
typeid(*pb); // throws a bad_typeid exception
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
std::cerr << "exception caught: " << e.what() << '\n';
|
|
}
|
|
return 0;
|
|
}
|
|
''', 'exception caught: std::bad_typeid')
|
|
|
|
def test_exit_stack(self):
|
|
if Settings.ASM_JS: return self.skip('uses report_stack without exporting')
|
|
|
|
Settings.INLINING_LIMIT = 50
|
|
Settings.NO_EXIT_RUNTIME = 1
|
|
|
|
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\n*0*\nExit Status: 0\npostRun\nok.\n''')
|
|
|
|
def test_class(self):
|
|
test_path = path_from_root('tests', 'core', 'test_class')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_inherit(self):
|
|
test_path = path_from_root('tests', 'core', 'test_inherit')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_isdigit_l(self):
|
|
test_path = path_from_root('tests', 'core', 'test_isdigit_l')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_iswdigit(self):
|
|
test_path = path_from_root('tests', 'core', 'test_iswdigit')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_polymorph(self):
|
|
test_path = path_from_root('tests', 'core', 'test_polymorph')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_complex(self):
|
|
self.do_run(r'''
|
|
#include <complex.h>
|
|
#include <stdio.h>
|
|
|
|
int main(int argc, char**argv)
|
|
{
|
|
float complex z1 = 1.0 + 3.0 * I;
|
|
printf("value = real %.2f imag %.2f\n",creal(z1),cimag(z1));
|
|
float abs_value = cabsf(z1);
|
|
printf("abs = %.2f\n",abs_value);
|
|
float complex z2 = conjf(z1);
|
|
printf("value = real %.2f imag %.2f\n",creal(z2),cimag(z2));
|
|
float complex z3 = cexpf(z1);
|
|
printf("value = real %.2f imag %.2f\n",creal(z3),cimag(z3));
|
|
float complex z4 = conj(z1);
|
|
printf("value = real %.2f imag %.2f\n",creal(z4),cimag(z4));
|
|
float complex z5 = cargf(z1);
|
|
printf("value = real %.2f imag %.2f\n",creal(z5),cimag(z5));
|
|
return 0;
|
|
}
|
|
''', '''value = real 1.00 imag 3.00
|
|
abs = 3.16
|
|
value = real 1.00 imag -3.00
|
|
value = real -2.69 imag 0.38
|
|
value = real 1.00 imag -3.00
|
|
value = real 1.25 imag 0.00''', force_c=True)
|
|
|
|
def test_segfault(self):
|
|
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_dynamic_cast(self):
|
|
test_path = path_from_root('tests', 'core', 'test_dynamic_cast')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_dynamic_cast_b(self):
|
|
test_path = path_from_root('tests', 'core', 'test_dynamic_cast_b')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_dynamic_cast_2(self):
|
|
test_path = path_from_root('tests', 'core', 'test_dynamic_cast_2')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_funcptr(self):
|
|
test_path = path_from_root('tests', 'core', 'test_funcptr')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_mathfuncptr(self):
|
|
test_path = path_from_root('tests', 'core', 'test_mathfuncptr')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
if self.is_emterpreter():
|
|
print 'emterpreter f32'
|
|
Settings.PRECISE_F32 = 1
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_funcptrfunc(self):
|
|
test_path = path_from_root('tests', 'core', 'test_funcptrfunc')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_funcptr_namecollide(self):
|
|
test_path = path_from_root('tests', 'core', 'test_funcptr_namecollide')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, force_c=True)
|
|
|
|
def test_emptyclass(self):
|
|
test_path = path_from_root('tests', 'core', 'test_emptyclass')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_alloca(self):
|
|
test_path = path_from_root('tests', 'core', 'test_alloca')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, 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):
|
|
test_path = path_from_root('tests', 'core', 'test_alloca_stack')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, force_c=True)
|
|
|
|
def test_stack_byval(self):
|
|
test_path = path_from_root('tests', 'core', 'test_stack_byval')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_stack_varargs(self):
|
|
Settings.INLINING_LIMIT = 50
|
|
Settings.TOTAL_STACK = 2048
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_stack_varargs')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_stack_varargs2(self):
|
|
Settings.TOTAL_STACK = 1536
|
|
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
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_stack_void')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_life(self):
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_array2')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_array2b(self):
|
|
test_path = path_from_root('tests', 'core', 'test_array2b')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_constglobalstructs(self):
|
|
test_path = path_from_root('tests', 'core', 'test_constglobalstructs')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_conststructs(self):
|
|
test_path = path_from_root('tests', 'core', 'test_conststructs')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_bigarray(self):
|
|
test_path = path_from_root('tests', 'core', 'test_bigarray')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_mod_globalstruct(self):
|
|
test_path = path_from_root('tests', 'core', 'test_mod_globalstruct')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
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)));
|
|
}
|
|
'''
|
|
def test():
|
|
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*')
|
|
|
|
test()
|
|
|
|
print 'relocatable' # this tests recursive global structs => nontrivial postSets for relocation
|
|
assert Settings.RELOCATABLE == Settings.EMULATED_FUNCTION_POINTERS == 0
|
|
Settings.RELOCATABLE = Settings.EMULATED_FUNCTION_POINTERS = 1
|
|
test()
|
|
Settings.RELOCATABLE = Settings.EMULATED_FUNCTION_POINTERS = 0
|
|
|
|
def test_ptrtoint(self):
|
|
runner = self
|
|
def check_warnings(output):
|
|
runner.assertEquals(filter(lambda line: 'Warning' in line, output.split('\n')).__len__(), 4)
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_ptrtoint')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, output_processor=check_warnings)
|
|
|
|
def test_sizeof(self):
|
|
# Has invalid writes between printouts
|
|
Settings.SAFE_HEAP = 0
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_sizeof')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, [], lambda x, err: x.replace('\n', '*'))
|
|
|
|
def test_llvm_used(self):
|
|
Building.LLVM_OPTS = 3
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_llvm_used')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_set_align(self):
|
|
Settings.SAFE_HEAP = 1
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_set_align')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_emscripten_api(self):
|
|
#if Building.LLVM_OPTS: return self.skip('FIXME')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_emscripten_api')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
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_from_file(src, output, post_build=check)
|
|
|
|
# test EXPORT_ALL
|
|
Settings.EXPORTED_FUNCTIONS = []
|
|
Settings.EXPORT_ALL = 1
|
|
Settings.LINKABLE = 1
|
|
self.do_run_from_file(src, output, post_build=check)
|
|
|
|
def test_emscripten_get_now(self):
|
|
self.banned_js_engines = [V8_ENGINE] # timer limitations in v8 shell
|
|
|
|
if self.run_name == 'asm2':
|
|
self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage
|
|
self.do_run(open(path_from_root('tests', 'emscripten_get_now.cpp')).read(), 'Timer resolution is good.')
|
|
|
|
def test_emscripten_get_compiler_setting(self):
|
|
test_path = path_from_root('tests', 'core', 'emscripten_get_compiler_setting')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.do_run(open(src).read(), 'You must build with -s RETAIN_COMPILER_SETTINGS=1')
|
|
Settings.RETAIN_COMPILER_SETTINGS = 1
|
|
self.do_run(open(src).read(), open(output).read().replace('waka', EMSCRIPTEN_VERSION))
|
|
|
|
# TODO: test only worked in non-fastcomp
|
|
def test_inlinejs(self):
|
|
return self.skip('non-fastcomp is deprecated and fails in 3.5') # only supports EM_ASM
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_inlinejs')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
if self.emcc_args == []: # opts will eliminate the comments
|
|
out = open('src.cpp.o.js').read()
|
|
for i in range(1, 5): assert ('comment%d' % i) in out
|
|
|
|
# TODO: test only worked in non-fastcomp
|
|
def test_inlinejs2(self):
|
|
return self.skip('non-fastcomp is deprecated and fails in 3.5') # only supports EM_ASM
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_inlinejs2')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_inlinejs3(self):
|
|
test_path = path_from_root('tests', 'core', 'test_inlinejs3')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
print 'no debugger, check validation'
|
|
src = open(src).read().replace('emscripten_debugger();', '')
|
|
self.do_run(src, open(output).read())
|
|
|
|
def test_inlinejs4(self):
|
|
self.do_run(r'''
|
|
#include <emscripten.h>
|
|
|
|
#define TO_STRING_INNER(x) #x
|
|
#define TO_STRING(x) TO_STRING_INNER(x)
|
|
#define assert_msg(msg, file, line) EM_ASM( throw 'Assert (' + msg + ') failed in ' + file + ':' + line + '!'; )
|
|
#define assert(expr) { \
|
|
if (!(expr)) { \
|
|
assert_msg(#expr, TO_STRING(__FILE__), TO_STRING(__LINE__)); \
|
|
} \
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
assert(argc != 17);
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
''', 'false')
|
|
|
|
def test_em_asm_unicode(self):
|
|
self.do_run(r'''
|
|
#include <emscripten.h>
|
|
|
|
int main() {
|
|
EM_ASM( Module.print("hello world…") );
|
|
}
|
|
''', 'hello world…')
|
|
|
|
def test_em_asm_unused_arguments(self):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <emscripten.h>
|
|
|
|
int main(int argc, char **argv) {
|
|
int sum = EM_ASM_INT({
|
|
return $0 + $2;
|
|
}, 0, 1, 2);
|
|
printf("0+2=%d\n", sum);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '''0+2=2''')
|
|
|
|
def test_memorygrowth(self):
|
|
self.banned_js_engines = [V8_ENGINE] # stderr printing limitations in v8
|
|
|
|
self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=0'] # start with 0
|
|
|
|
# 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
|
|
self.emcc_args += ['-s', '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 '-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' + str([len(fail), len(win)])
|
|
|
|
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;
|
|
}
|
|
'''
|
|
self.do_run(src, '''*16*\n0:22016,0,32,48\n1:22018,1,48,32\n''')
|
|
|
|
def test_tinyfuncstr(self):
|
|
test_path = path_from_root('tests', 'core', 'test_tinyfuncstr')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_llvmswitch(self):
|
|
test_path = path_from_root('tests', 'core', 'test_llvmswitch')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
# 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):
|
|
test_path = path_from_root('tests', 'core', 'test_cxx03_do_run')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_bigswitch(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
self.banned_js_engines = [SPIDERMONKEY_ENGINE] # bug 1174230
|
|
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)
|
|
3060: what?
|
|
''', args=['34962', '26214', '35040', str(0xbf4)])
|
|
|
|
def test_biggerswitch(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
self.banned_js_engines = [SPIDERMONKEY_ENGINE] # bug 1174230
|
|
num_cases = 20000
|
|
switch_case, err = Popen([PYTHON, path_from_root('tests', 'gen_large_switchcase.py'), str(num_cases)], stdout=PIPE, stderr=PIPE).communicate()
|
|
self.do_run(switch_case, '''58996: 589965899658996
|
|
59297: 592975929759297
|
|
59598: default
|
|
59899: 598995989959899
|
|
Success!''')
|
|
|
|
def test_indirectbr(self):
|
|
Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS)
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_indirectbr')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_indirectbr_many(self):
|
|
test_path = path_from_root('tests', 'core', 'test_indirectbr_many')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
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;
|
|
}
|
|
'''
|
|
self.do_run(src, '*4,3,4*\n*6,4,6*')
|
|
|
|
def test_varargs(self):
|
|
test_path = path_from_root('tests', 'core', 'test_varargs')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_varargs_byval(self):
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_functionpointer_libfunc_varargs')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
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 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()
|
|
print >> sys.stderr, 'skipping C/C++ conventions warning check, since not i386-pc-linux-gnu'
|
|
|
|
def test_stdlibs(self):
|
|
# safe heap prints a warning that messes up our output.
|
|
Settings.SAFE_HEAP = 0
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.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
|
|
|
|
printf("tolower_l: %c\\n", tolower_l('A', 0));
|
|
|
|
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*\ntolower_l: a\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):
|
|
# tests strtoll for hex strings (0x...)
|
|
test_path = path_from_root('tests', 'core', 'test_strtoll_hex')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strtoll_dec(self):
|
|
# tests strtoll for decimal strings (0x...)
|
|
test_path = path_from_root('tests', 'core', 'test_strtoll_dec')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strtoll_bin(self):
|
|
# tests strtoll for binary strings (0x...)
|
|
test_path = path_from_root('tests', 'core', 'test_strtoll_bin')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strtoll_oct(self):
|
|
# tests strtoll for decimal strings (0x...)
|
|
test_path = path_from_root('tests', 'core', 'test_strtoll_oct')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strtol_hex(self):
|
|
# tests strtoll for hex strings (0x...)
|
|
test_path = path_from_root('tests', 'core', 'test_strtol_hex')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strtol_dec(self):
|
|
# tests strtoll for decimal strings (0x...)
|
|
test_path = path_from_root('tests', 'core', 'test_strtol_dec')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strtol_bin(self):
|
|
# tests strtoll for binary strings (0x...)
|
|
test_path = path_from_root('tests', 'core', 'test_strtol_bin')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strtol_oct(self):
|
|
# tests strtoll for decimal strings (0x...)
|
|
test_path = path_from_root('tests', 'core', 'test_strtol_oct')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_atexit(self):
|
|
# Confirms they are called in reverse order
|
|
test_path = path_from_root('tests', 'core', 'test_atexit')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_pthread_specific(self):
|
|
src = open(path_from_root('tests', 'pthread', 'specific.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'pthread', 'specific.c.txt'), 'r').read()
|
|
self.do_run(src, expected, force_c=True)
|
|
|
|
def test_tcgetattr(self):
|
|
src = open(path_from_root('tests', 'termios', 'test_tcgetattr.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_time(self):
|
|
src = open(path_from_root('tests', 'time', 'src.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'time', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected);
|
|
|
|
def test_timeb(self):
|
|
# Confirms they are called in reverse order
|
|
test_path = path_from_root('tests', 'core', 'test_timeb')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_time_c(self):
|
|
test_path = path_from_root('tests', 'core', 'test_time_c')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_gmtime(self):
|
|
test_path = path_from_root('tests', 'core', 'test_gmtime')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strptime_tm(self):
|
|
test_path = path_from_root('tests', 'core', 'test_strptime_tm')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strptime_days(self):
|
|
test_path = path_from_root('tests', 'core', 'test_strptime_days')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strptime_reentrant(self):
|
|
test_path = path_from_root('tests', 'core', 'test_strptime_reentrant')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strftime(self):
|
|
test_path = path_from_root('tests', 'core', 'test_strftime')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_intentional_fault(self):
|
|
# Some programs intentionally segfault themselves, we should compile that into a throw
|
|
src = r'''
|
|
int main () {
|
|
*(volatile char *)0 = 0;
|
|
return *(volatile char *)0;
|
|
}
|
|
'''
|
|
self.do_run(src, 'abort()' if self.run_name != 'asm2g' else 'abort("segmentation fault')
|
|
|
|
def test_trickystring(self):
|
|
test_path = path_from_root('tests', 'core', 'test_trickystring')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_statics(self):
|
|
test_path = path_from_root('tests', 'core', 'test_statics')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_copyop(self):
|
|
# 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.
|
|
test_path = path_from_root('tests', 'core', 'test_copyop')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_memcpy_memcmp(self):
|
|
test_path = path_from_root('tests', 'core', 'test_memcpy_memcmp')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
def check(result, err):
|
|
result = result.replace('\n \n', '\n') # remove extra node output
|
|
return hashlib.sha1(result).hexdigest()
|
|
|
|
self.do_run_from_file(src, output, output_nicerizer = check)
|
|
|
|
def test_memcpy2(self):
|
|
test_path = path_from_root('tests', 'core', 'test_memcpy2')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_memcpy3(self):
|
|
test_path = path_from_root('tests', 'core', 'test_memcpy3')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_memset(self):
|
|
test_path = path_from_root('tests', 'core', 'test_memset')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_getopt(self):
|
|
test_path = path_from_root('tests', 'core', 'test_getopt')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, args=['-t', '12', '-n', 'foobar'])
|
|
|
|
def test_getopt_long(self):
|
|
test_path = path_from_root('tests', 'core', 'test_getopt_long')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, args=['--file', 'foobar', '-b'])
|
|
|
|
def test_memmove(self):
|
|
test_path = path_from_root('tests', 'core', 'test_memmove')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_memmove2(self):
|
|
test_path = path_from_root('tests', 'core', 'test_memmove2')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_memmove3(self):
|
|
test_path = path_from_root('tests', 'core', 'test_memmove3')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_flexarray_struct(self):
|
|
test_path = path_from_root('tests', 'core', 'test_flexarray_struct')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_bsearch(self):
|
|
test_path = path_from_root('tests', 'core', 'test_bsearch')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_stack_overflow(self):
|
|
Settings.ASSERTIONS = 1
|
|
self.do_run(open(path_from_root('tests', 'core', 'stack_overflow.cpp')).read(), 'abort()')
|
|
|
|
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;
|
|
}
|
|
'''
|
|
# Bloated memory; same layout as C/C++
|
|
self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*')
|
|
|
|
def test_runtimelink(self):
|
|
return self.skip('BUILD_AS_SHARED_LIB=2 is deprecated')
|
|
if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize printf into puts in the parent, and the child will still look for puts')
|
|
if Settings.ASM_JS: return self.skip('asm does not support runtime linking')
|
|
|
|
main, supp = self.setup_runtimelink_test()
|
|
|
|
self.banned_js_engines = [NODE_JS] # node's global scope behaves differently than everything else, needs investigation FIXME
|
|
Settings.LINKABLE = 1
|
|
Settings.BUILD_AS_SHARED_LIB = 2
|
|
|
|
self.build(supp, self.get_dir(), self.in_dir('supp.cpp'))
|
|
shutil.move(self.in_dir('supp.cpp.o.js'), self.in_dir('liblib.so'))
|
|
Settings.BUILD_AS_SHARED_LIB = 0
|
|
|
|
Settings.RUNTIME_LINKED_LIBS = ['liblib.so'];
|
|
self.do_run(main, 'supp: 54,2\nmain: 56\nsupp see: 543\nmain see: 76\nok.')
|
|
|
|
def can_dlfcn(self):
|
|
return True
|
|
|
|
def prep_dlfcn_lib(self):
|
|
Settings.MAIN_MODULE = 0
|
|
Settings.SIDE_MODULE = 1
|
|
|
|
def prep_dlfcn_main(self):
|
|
Settings.MAIN_MODULE = 1
|
|
Settings.SIDE_MODULE = 0
|
|
|
|
dlfcn_post_build = '''
|
|
def process(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
"FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
|
|
)
|
|
open(filename, 'w').write(src)
|
|
'''
|
|
|
|
def test_dlfcn_basic(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = '''
|
|
#include <cstdio>
|
|
|
|
class Foo {
|
|
public:
|
|
Foo() {
|
|
printf("Constructing lib object.\\n");
|
|
}
|
|
};
|
|
|
|
Foo global;
|
|
'''
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.cpp')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
src = '''
|
|
#include <cstdio>
|
|
#include <dlfcn.h>
|
|
|
|
class Bar {
|
|
public:
|
|
Bar() {
|
|
printf("Constructing main object.\\n");
|
|
}
|
|
};
|
|
|
|
Bar global;
|
|
|
|
int main() {
|
|
dlopen("liblib.so", RTLD_NOW);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, 'Constructing main object.\nConstructing lib object.\n',
|
|
post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_i64(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
self.prep_dlfcn_lib()
|
|
Settings.EXPORTED_FUNCTIONS = ['_foo']
|
|
lib_src = '''
|
|
int foo(int x) {
|
|
return (long long)x / (long long)1234;
|
|
}
|
|
'''
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.c')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
Settings.EXPORTED_FUNCTIONS = ['_main']
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <dlfcn.h>
|
|
|
|
typedef int (*intfunc)(int);
|
|
|
|
void *p;
|
|
|
|
int main() {
|
|
p = malloc(1024);
|
|
void *lib_handle = dlopen("liblib.so", 0);
|
|
printf("load %p\n", lib_handle);
|
|
intfunc x = (intfunc)dlsym(lib_handle, "foo");
|
|
printf("foo func %p\n", x);
|
|
if (p == 0) return 1;
|
|
printf("|%d|\n", x(81234567));
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '|65830|', post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_em_asm(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = '''
|
|
#include <emscripten.h>
|
|
class Foo {
|
|
public:
|
|
Foo() {
|
|
EM_ASM( Module.print("Constructing lib object.") );
|
|
}
|
|
};
|
|
Foo global;
|
|
'''
|
|
filename = 'liblib.cpp'
|
|
self.build(lib_src, self.get_dir(), filename)
|
|
shutil.move(filename + '.o.js', 'liblib.so')
|
|
|
|
self.prep_dlfcn_main()
|
|
src = '''
|
|
#include <emscripten.h>
|
|
#include <dlfcn.h>
|
|
class Bar {
|
|
public:
|
|
Bar() {
|
|
EM_ASM( Module.print("Constructing main object.") );
|
|
}
|
|
};
|
|
Bar global;
|
|
int main() {
|
|
dlopen("liblib.so", RTLD_NOW);
|
|
EM_ASM( Module.print("All done.") );
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, 'Constructing main object.\nConstructing lib object.\nAll done.\n',
|
|
post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_qsort(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
self.prep_dlfcn_lib()
|
|
Settings.EXPORTED_FUNCTIONS = ['_get_cmp']
|
|
lib_src = '''
|
|
int lib_cmp(const void* left, const void* right) {
|
|
const int* a = (const int*) left;
|
|
const int* b = (const int*) right;
|
|
if(*a > *b) return 1;
|
|
else if(*a == *b) return 0;
|
|
else return -1;
|
|
}
|
|
|
|
typedef int (*CMP_TYPE)(const void*, const void*);
|
|
|
|
extern "C" CMP_TYPE get_cmp() {
|
|
return lib_cmp;
|
|
}
|
|
'''
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.cpp')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
|
|
src = '''
|
|
#include <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};
|
|
|
|
qsort((void*)arr, 5, sizeof(int), main_cmp);
|
|
printf("Sort with main comparison: ");
|
|
for (int i = 0; i < 5; i++) {
|
|
printf("%d ", arr[i]);
|
|
}
|
|
printf("\\n");
|
|
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
if (lib_handle == NULL) {
|
|
printf("Could not load lib.\\n");
|
|
return 1;
|
|
}
|
|
getter_ptr = (CMP_TYPE (*)()) dlsym(lib_handle, "get_cmp");
|
|
if (getter_ptr == NULL) {
|
|
printf("Could not find func.\\n");
|
|
return 1;
|
|
}
|
|
lib_cmp_ptr = getter_ptr();
|
|
qsort((void*)arr, 5, sizeof(int), lib_cmp_ptr);
|
|
printf("Sort with lib comparison: ");
|
|
for (int i = 0; i < 5; i++) {
|
|
printf("%d ", arr[i]);
|
|
}
|
|
printf("\\n");
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *',
|
|
output_nicerizer=lambda x, err: x.replace('\n', '*'),
|
|
post_build=self.dlfcn_post_build)
|
|
|
|
if Settings.ASM_JS and SPIDERMONKEY_ENGINE and os.path.exists(SPIDERMONKEY_ENGINE[0]):
|
|
out = run_js('liblib.so', engine=SPIDERMONKEY_ENGINE, full_output=True, stderr=STDOUT)
|
|
if 'asm' in out:
|
|
self.validate_asmjs(out)
|
|
|
|
def test_dlfcn_data_and_fptr(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = '''
|
|
#include <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.EXPORTED_FUNCTIONS = ['_func']
|
|
Settings.EXPORTED_GLOBALS = ['_global']
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
Settings.LINKABLE = 1
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
#include <emscripten.h>
|
|
|
|
typedef void (*FUNCTYPE(int, void(*)()))();
|
|
|
|
FUNCTYPE func;
|
|
|
|
void EMSCRIPTEN_KEEPALIVE parent_func() {
|
|
printf("parent_func called from child\\n");
|
|
}
|
|
|
|
void main_fptr() {
|
|
printf("First calling main_fptr from lib.\\n");
|
|
}
|
|
|
|
int main() {
|
|
void* lib_handle;
|
|
FUNCTYPE* func_fptr;
|
|
|
|
// Test basic lib loading.
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
if (lib_handle == NULL) {
|
|
printf("Could not load lib.\\n");
|
|
return 1;
|
|
}
|
|
|
|
// Test looked up function.
|
|
func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func");
|
|
// Load twice to test cache.
|
|
func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func");
|
|
if (func_fptr == NULL) {
|
|
printf("Could not find func.\\n");
|
|
return 1;
|
|
}
|
|
|
|
// Test passing function pointers across module bounds.
|
|
void (*fptr)() = func_fptr(13, main_fptr);
|
|
fptr();
|
|
|
|
// Test global data.
|
|
int* global = (int*) dlsym(lib_handle, "global");
|
|
if (global == NULL) {
|
|
printf("Could not find global.\\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("Var: %d\\n", *global);
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_main']
|
|
Settings.EXPORTED_GLOBALS = []
|
|
self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*',
|
|
output_nicerizer=lambda x, err: x.replace('\n', '*'),
|
|
post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_varargs(self):
|
|
# this test is not actually valid - it fails natively. the child should fail to be loaded, not load and successfully see the parent print_ints func
|
|
if not self.can_dlfcn(): return
|
|
|
|
Settings.LINKABLE = 1
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = r'''
|
|
void print_ints(int n, ...);
|
|
extern "C" void func() {
|
|
print_ints(2, 13, 42);
|
|
}
|
|
'''
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.cpp')
|
|
Settings.EXPORTED_FUNCTIONS = ['_func']
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
src = r'''
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
#include <assert.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);
|
|
assert(lib_handle);
|
|
fptr = (void (*)())dlsym(lib_handle, "func");
|
|
fptr();
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_main']
|
|
self.do_run(src, '100\n200\n13\n42\n',
|
|
post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_self(self):
|
|
if not self.can_dlfcn(): return
|
|
self.prep_dlfcn_main()
|
|
|
|
def post(filename):
|
|
with open(filename) as f:
|
|
for line in f:
|
|
if 'var NAMED_GLOBALS' in line:
|
|
table = line
|
|
break
|
|
else:
|
|
raise Exception('Could not find symbol table!')
|
|
table = table[table.find('{'):table.find('}')+1]
|
|
# ensure there aren't too many globals; we don't want unnamed_addr
|
|
assert table.count(',') <= 23, table.count(',')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_dlfcn_self')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, post_build=(None, post))
|
|
|
|
def test_dlfcn_unique_sig(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = '''
|
|
#include <stdio.h>
|
|
|
|
int myfunc(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m) {
|
|
return 13;
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_myfunc']
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.c')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
src = '''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
|
|
typedef int (*FUNCTYPE)(int, int, int, int, int, int, int, int, int, int, int, int, int);
|
|
|
|
int main() {
|
|
void *lib_handle;
|
|
FUNCTYPE func_ptr;
|
|
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
assert(lib_handle != NULL);
|
|
|
|
func_ptr = (FUNCTYPE)dlsym(lib_handle, "myfunc");
|
|
assert(func_ptr != NULL);
|
|
assert(func_ptr(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == 13);
|
|
|
|
puts("success");
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
|
|
self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_stacks(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = '''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
int myfunc(const char *input) {
|
|
char bigstack[1024] = { 0 };
|
|
|
|
// make sure we didn't just trample the stack!
|
|
assert(!strcmp(input, "foobar"));
|
|
|
|
snprintf(bigstack, sizeof(bigstack), input);
|
|
return strlen(bigstack);
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_myfunc']
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.c')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
src = '''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
#include <string.h>
|
|
|
|
typedef int (*FUNCTYPE)(const char *);
|
|
|
|
int main() {
|
|
void *lib_handle;
|
|
FUNCTYPE func_ptr;
|
|
char str[128];
|
|
|
|
snprintf(str, sizeof(str), "foobar");
|
|
|
|
// HACK: Use strcmp in the main executable so that it doesn't get optimized out and the dynamic library
|
|
// is able to use it.
|
|
assert(!strcmp(str, "foobar"));
|
|
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
assert(lib_handle != NULL);
|
|
|
|
func_ptr = (FUNCTYPE)dlsym(lib_handle, "myfunc");
|
|
assert(func_ptr != NULL);
|
|
assert(func_ptr(str) == 6);
|
|
|
|
puts("success");
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_strcmp']
|
|
self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_funcs(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = r'''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
typedef void (*voidfunc)();
|
|
typedef void (*intfunc)(int);
|
|
|
|
void callvoid(voidfunc f) { f(); }
|
|
void callint(voidfunc f, int x) { f(x); }
|
|
|
|
void void_0() { printf("void 0\n"); }
|
|
void void_1() { printf("void 1\n"); }
|
|
voidfunc getvoid(int i) {
|
|
switch(i) {
|
|
case 0: return void_0;
|
|
case 1: return void_1;
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
void int_0(int x) { printf("int 0 %d\n", x); }
|
|
void int_1(int x) { printf("int 1 %d\n", x); }
|
|
intfunc getint(int i) {
|
|
switch(i) {
|
|
case 0: return int_0;
|
|
case 1: return int_1;
|
|
default: return NULL;
|
|
}
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_callvoid', '_callint', '_getvoid', '_getint']
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.c')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
src = r'''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
|
|
typedef void (*voidfunc)();
|
|
typedef void (*intfunc)(int);
|
|
|
|
typedef void (*voidcaller)(voidfunc);
|
|
typedef void (*intcaller)(intfunc, int);
|
|
|
|
typedef voidfunc (*voidgetter)(int);
|
|
typedef intfunc (*intgetter)(int);
|
|
|
|
void void_main() { printf("main.\n"); }
|
|
void int_main(int x) { printf("main %d\n", x); }
|
|
|
|
int main() {
|
|
printf("go\n");
|
|
void *lib_handle;
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
assert(lib_handle != NULL);
|
|
|
|
voidcaller callvoid = (voidcaller)dlsym(lib_handle, "callvoid");
|
|
assert(callvoid != NULL);
|
|
callvoid(void_main);
|
|
|
|
intcaller callint = (intcaller)dlsym(lib_handle, "callint");
|
|
assert(callint != NULL);
|
|
callint(int_main, 201);
|
|
|
|
voidgetter getvoid = (voidgetter)dlsym(lib_handle, "getvoid");
|
|
assert(getvoid != NULL);
|
|
callvoid(getvoid(0));
|
|
callvoid(getvoid(1));
|
|
|
|
intgetter getint = (intgetter)dlsym(lib_handle, "getint");
|
|
assert(getint != NULL);
|
|
callint(getint(0), 54);
|
|
callint(getint(1), 9000);
|
|
|
|
assert(getint(1000) == NULL);
|
|
|
|
puts("ok");
|
|
return 0;
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
|
|
self.do_run(src, '''go
|
|
main.
|
|
main 201
|
|
void 0
|
|
void 1
|
|
int 0 54
|
|
int 1 9000
|
|
ok
|
|
''', force_c=True, post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_mallocs(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
Settings.TOTAL_MEMORY = 64*1024*1024 # will be exhausted without functional malloc/free
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = r'''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
void *mallocproxy(int n) { return malloc(n); }
|
|
void freeproxy(void *p) { free(p); }
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_mallocproxy', '_freeproxy']
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.c')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
src = open(path_from_root('tests', 'dlmalloc_proxy.c')).read()
|
|
Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
|
|
self.do_run(src, '''*294,153*''', force_c=True, post_build=self.dlfcn_post_build)
|
|
|
|
def test_dlfcn_longjmp(self):
|
|
if not self.can_dlfcn(): return
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = r'''
|
|
#include <setjmp.h>
|
|
|
|
void jumpy(jmp_buf buf) {
|
|
static int i = 0;
|
|
i++;
|
|
if (i == 10) longjmp(buf, i);
|
|
printf("pre %d\n", i);
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_jumpy']
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.c')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
src = r'''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
#include <setjmp.h>
|
|
|
|
typedef void (*jumpfunc)(jmp_buf);
|
|
|
|
int main() {
|
|
printf("go!\n");
|
|
|
|
void *lib_handle;
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
assert(lib_handle != NULL);
|
|
|
|
jumpfunc jumpy = (jumpfunc)dlsym(lib_handle, "jumpy");
|
|
assert(jumpy);
|
|
|
|
jmp_buf buf;
|
|
int jmpval = setjmp(buf);
|
|
if (jmpval == 0) {
|
|
while (1) jumpy(buf);
|
|
} else {
|
|
printf("out!\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
|
|
self.do_run(src, '''go!
|
|
pre 1
|
|
pre 2
|
|
pre 3
|
|
pre 4
|
|
pre 5
|
|
pre 6
|
|
pre 7
|
|
pre 8
|
|
pre 9
|
|
out!
|
|
''', post_build=self.dlfcn_post_build, force_c=True)
|
|
|
|
def zzztest_dlfcn_exceptions(self): # TODO: make this work. need to forward tempRet0 across modules
|
|
if not self.can_dlfcn(): return
|
|
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 0
|
|
|
|
self.prep_dlfcn_lib()
|
|
lib_src = r'''
|
|
extern "C" {
|
|
int ok() {
|
|
return 65;
|
|
}
|
|
int fail() {
|
|
throw 123;
|
|
}
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_ok', '_fail']
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.cpp')
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
self.prep_dlfcn_main()
|
|
src = r'''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
|
|
typedef int (*intfunc)();
|
|
|
|
int main() {
|
|
printf("go!\n");
|
|
|
|
void *lib_handle;
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
assert(lib_handle != NULL);
|
|
|
|
intfunc okk = (intfunc)dlsym(lib_handle, "ok");
|
|
intfunc faill = (intfunc)dlsym(lib_handle, "fail");
|
|
assert(okk && faill);
|
|
|
|
try {
|
|
printf("ok: %d\n", okk());
|
|
} catch(...) {
|
|
printf("wha\n");
|
|
}
|
|
|
|
try {
|
|
printf("fail: %d\n", faill());
|
|
} catch(int x) {
|
|
printf("int %d\n", x);
|
|
}
|
|
|
|
try {
|
|
printf("fail: %d\n", faill());
|
|
} catch(double x) {
|
|
printf("caught %f\n", x);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
|
|
self.do_run(src, '''go!
|
|
ok: 65
|
|
int 123
|
|
ok
|
|
''', post_build=self.dlfcn_post_build)
|
|
|
|
def dylink_test(self, main, side, expected, header=None, main_emcc_args=[], force_c=False, need_reverse=True, auto_load=True):
|
|
if header:
|
|
open('header.h', 'w').write(header)
|
|
|
|
emcc_args = self.emcc_args[:]
|
|
try:
|
|
# general settings
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 1
|
|
self.emcc_args += ['--memory-init-file', '0']
|
|
|
|
# side settings
|
|
Settings.MAIN_MODULE = 0
|
|
Settings.SIDE_MODULE = 1
|
|
if type(side) == str:
|
|
base = 'liblib.cpp' if not force_c else 'liblib.c'
|
|
try_delete(base + '.o.js')
|
|
self.build(side, self.get_dir(), base)
|
|
if force_c:
|
|
shutil.move(base + '.o.js', 'liblib.cpp.o.js')
|
|
else:
|
|
# side is just a library
|
|
try_delete('liblib.cpp.o.js')
|
|
Popen([PYTHON, EMCC] + side + self.emcc_args + Settings.serialize() + ['-o', os.path.join(self.get_dir(), 'liblib.cpp.o.js')]).communicate()
|
|
if SPIDERMONKEY_ENGINE and os.path.exists(SPIDERMONKEY_ENGINE[0]):
|
|
out = run_js('liblib.cpp.o.js', engine=SPIDERMONKEY_ENGINE, full_output=True, stderr=STDOUT)
|
|
if 'asm' in out:
|
|
self.validate_asmjs(out)
|
|
shutil.move('liblib.cpp.o.js', 'liblib.so')
|
|
|
|
# main settings
|
|
Settings.MAIN_MODULE = 1
|
|
Settings.SIDE_MODULE = 0
|
|
if auto_load:
|
|
open('pre.js', 'w').write('''
|
|
var Module = {
|
|
dynamicLibraries: ['liblib.so'],
|
|
};
|
|
''')
|
|
self.emcc_args += ['--pre-js', 'pre.js'] + main_emcc_args
|
|
|
|
if type(main) == str:
|
|
self.do_run(main, expected, force_c=force_c)
|
|
else:
|
|
# main is just a library
|
|
try_delete('src.cpp.o.js')
|
|
Popen([PYTHON, EMCC] + main + self.emcc_args + Settings.serialize() + ['-o', os.path.join(self.get_dir(), 'src.cpp.o.js')]).communicate()
|
|
self.do_run(None, expected, no_build=True)
|
|
finally:
|
|
self.emcc_args = emcc_args[:]
|
|
|
|
if need_reverse:
|
|
# test the reverse as well
|
|
print 'flip'
|
|
self.dylink_test(side, main, expected, header, main_emcc_args, force_c, need_reverse=False)
|
|
|
|
def test_dylink_basics(self):
|
|
self.dylink_test('''
|
|
#include <stdio.h>
|
|
extern int sidey();
|
|
int main() {
|
|
printf("other says %d.", sidey());
|
|
return 0;
|
|
}
|
|
''', '''
|
|
int sidey() { return 11; }
|
|
''', 'other says 11.')
|
|
|
|
def test_dylink_floats(self):
|
|
self.dylink_test('''
|
|
#include <stdio.h>
|
|
extern float sidey();
|
|
int main() {
|
|
printf("other says %.2f.", sidey()+1);
|
|
return 0;
|
|
}
|
|
''', '''
|
|
float sidey() { return 11.5; }
|
|
''', 'other says 12.50')
|
|
|
|
def test_dylink_printfs(self):
|
|
self.dylink_test(r'''
|
|
#include <stdio.h>
|
|
extern void sidey();
|
|
int main() {
|
|
printf("hello from main\n");
|
|
sidey();
|
|
return 0;
|
|
}
|
|
''', r'''
|
|
#include <stdio.h>
|
|
void sidey() { printf("hello from side\n"); }
|
|
''', 'hello from main\nhello from side\n')
|
|
|
|
def test_dylink_funcpointer(self):
|
|
self.dylink_test(r'''
|
|
#include <stdio.h>
|
|
#include "header.h"
|
|
voidfunc sidey(voidfunc f);
|
|
void a() { printf("hello from funcptr\n"); }
|
|
int main() {
|
|
sidey(a)();
|
|
return 0;
|
|
}
|
|
''', '''
|
|
#include "header.h"
|
|
voidfunc sidey(voidfunc f) { return f; }
|
|
''', 'hello from funcptr\n', header='typedef void (*voidfunc)();')
|
|
|
|
def test_dylink_funcpointers(self):
|
|
self.dylink_test(r'''
|
|
#include <stdio.h>
|
|
#include "header.h"
|
|
int sidey(voidfunc f);
|
|
void areturn0() { printf("hello 0\n"); }
|
|
void areturn1() { printf("hello 1\n"); }
|
|
void areturn2() { printf("hello 2\n"); }
|
|
int main(int argc, char **argv) {
|
|
voidfunc table[3] = { areturn0, areturn1, areturn2 };
|
|
table[sidey(NULL)]();
|
|
return 0;
|
|
}
|
|
''', '''
|
|
#include "header.h"
|
|
int sidey(voidfunc f) { if (f) f(); return 1; }
|
|
''', 'hello 1\n', header='typedef void (*voidfunc)();')
|
|
|
|
def test_dylink_funcpointers2(self):
|
|
self.dylink_test(r'''
|
|
#include "header.h"
|
|
#include <emscripten.h>
|
|
void left1() { printf("left1\n"); }
|
|
void left2() { printf("left2\n"); }
|
|
voidfunc getleft1() { return left1; }
|
|
voidfunc getleft2() { return left2; }
|
|
int main(int argc, char **argv) {
|
|
printf("main\n");
|
|
EM_ASM({
|
|
// make the function table sizes a non-power-of-two
|
|
Runtime.alignFunctionTables();
|
|
Module['FUNCTION_TABLE_v'].push(0, 0, 0, 0, 0);
|
|
var newSize = Runtime.alignFunctionTables();
|
|
//Module.print('new size of function tables: ' + newSize);
|
|
// when masked, the two function pointers 1 and 2 should not happen to fall back to the right place
|
|
assert(((newSize+1) & 3) !== 1 || ((newSize+2) & 3) !== 2);
|
|
Runtime.loadDynamicLibrary('liblib.so');
|
|
});
|
|
volatilevoidfunc f;
|
|
f = (volatilevoidfunc)left1;
|
|
f();
|
|
f = (volatilevoidfunc)left2;
|
|
f();
|
|
f = (volatilevoidfunc)getright1();
|
|
f();
|
|
f = (volatilevoidfunc)getright2();
|
|
f();
|
|
second();
|
|
return 0;
|
|
}
|
|
''', r'''
|
|
#include "header.h"
|
|
void right1() { printf("right1\n"); }
|
|
void right2() { printf("right2\n"); }
|
|
voidfunc getright1() { return right1; }
|
|
voidfunc getright2() { return right2; }
|
|
void second() {
|
|
printf("second\n");
|
|
volatilevoidfunc f;
|
|
f = (volatilevoidfunc)getleft1();
|
|
f();
|
|
f = (volatilevoidfunc)getleft2();
|
|
f();
|
|
f = (volatilevoidfunc)right1;
|
|
f();
|
|
f = (volatilevoidfunc)right2;
|
|
f();
|
|
}
|
|
''', 'main\nleft1\nleft2\nright1\nright2\nsecond\nleft1\nleft2\nright1\nright2\n', header='''
|
|
#include <stdio.h>
|
|
typedef void (*voidfunc)();
|
|
typedef volatile voidfunc volatilevoidfunc;
|
|
voidfunc getleft1();
|
|
voidfunc getleft2();
|
|
voidfunc getright1();
|
|
voidfunc getright2();
|
|
void second();
|
|
''', need_reverse=False, auto_load=False)
|
|
|
|
def test_dylink_funcpointers_wrapper(self):
|
|
self.dylink_test(r'''
|
|
#include <stdio.h>
|
|
#include "header.h"
|
|
int main(int argc, char **argv) {
|
|
volatile charfunc f = emscripten_run_script;
|
|
f("Module.print('one')");
|
|
f = get();
|
|
f("Module.print('two')");
|
|
return 0;
|
|
}
|
|
''', '''
|
|
#include "header.h"
|
|
charfunc get() {
|
|
return emscripten_run_script;
|
|
}
|
|
''', 'one\ntwo\n', header='''
|
|
#include <emscripten.h>
|
|
typedef void (*charfunc)(const char*);
|
|
extern charfunc get();
|
|
''')
|
|
|
|
def test_dylink_funcpointers_float(self):
|
|
self.dylink_test(r'''
|
|
#include <stdio.h>
|
|
#include "header.h"
|
|
int sidey(floatfunc f);
|
|
float areturn0(float f) { printf("hello 0: %f\n", f); return 0; }
|
|
float areturn1(float f) { printf("hello 1: %f\n", f); return 1; }
|
|
float areturn2(float f) { printf("hello 2: %f\n", f); return 2; }
|
|
int main(int argc, char **argv) {
|
|
volatile floatfunc table[3] = { areturn0, areturn1, areturn2 };
|
|
printf("got: %d\n", (int)table[sidey(NULL)](12.34));
|
|
return 0;
|
|
}
|
|
''', '''
|
|
#include "header.h"
|
|
int sidey(floatfunc f) { if (f) f(56.78); return 1; }
|
|
''', 'hello 1: 12.340000\ngot: 1\n', header='typedef float (*floatfunc)(float);')
|
|
|
|
def test_dylink_global_init(self):
|
|
self.dylink_test(r'''
|
|
#include <stdio.h>
|
|
struct Class {
|
|
Class() { printf("a new Class\n"); }
|
|
};
|
|
static Class c;
|
|
int main() {
|
|
return 0;
|
|
}
|
|
''', r'''
|
|
void nothing() {}
|
|
''', 'a new Class\n')
|
|
|
|
def test_dylink_global_inits(self):
|
|
def test():
|
|
self.dylink_test(header=r'''
|
|
#include <stdio.h>
|
|
struct Class {
|
|
Class(const char *name) { printf("new %s\n", name); }
|
|
};
|
|
''', main=r'''
|
|
#include "header.h"
|
|
static Class c("main");
|
|
int main() {
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include "header.h"
|
|
static Class c("side");
|
|
''', expected=['new main\nnew side\n', 'new side\nnew main\n'])
|
|
test()
|
|
|
|
if Settings.ASSERTIONS == 1:
|
|
print 'check warnings'
|
|
Settings.ASSERTIONS = 2
|
|
test()
|
|
full = run_js('src.cpp.o.js', engine=JS_ENGINES[0], full_output=True, stderr=STDOUT)
|
|
self.assertNotContained("trying to dynamically load symbol '__ZN5ClassC2EPKc' (from 'liblib.so') that already exists", full)
|
|
|
|
def test_dylink_i64(self):
|
|
self.dylink_test('''
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
extern int64_t sidey();
|
|
int main() {
|
|
printf("other says %lld.", sidey());
|
|
return 0;
|
|
}
|
|
''', '''
|
|
#include <stdint.h>
|
|
int64_t sidey() {
|
|
volatile int64_t x = 11;
|
|
x = x * x * x * x;
|
|
x += x % 17;
|
|
x += (x * (1 << 30));
|
|
return x;
|
|
}
|
|
''', 'other says 15724949027125.')
|
|
|
|
def test_dylink_i64_b(self):
|
|
self.dylink_test('''
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
extern int64_t sidey();
|
|
int main() {
|
|
printf("other says %lld.", sidey());
|
|
return 0;
|
|
}
|
|
''', '''
|
|
#include <stdint.h>
|
|
int64_t sidey() {
|
|
volatile int64_t x = 0x12345678abcdef12LL;
|
|
x += x % 17;
|
|
x = 18 - x;
|
|
return x;
|
|
}
|
|
''', 'other says -1311768467750121224.')
|
|
|
|
def test_dylink_class(self):
|
|
self.dylink_test(header=r'''
|
|
#include <stdio.h>
|
|
struct Class {
|
|
Class(const char *name);
|
|
};
|
|
''', main=r'''
|
|
#include "header.h"
|
|
int main() {
|
|
Class c("main");
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include "header.h"
|
|
Class::Class(const char *name) { printf("new %s\n", name); }
|
|
''', expected=['new main\n'])
|
|
|
|
def test_dylink_global_var(self):
|
|
self.dylink_test(main=r'''
|
|
#include <stdio.h>
|
|
extern int x;
|
|
int main() {
|
|
printf("extern is %d.\n", x);
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
int x = 123;
|
|
''', expected=['extern is 123.\n'])
|
|
|
|
def test_dylink_global_var_modded(self):
|
|
self.dylink_test(main=r'''
|
|
#include <stdio.h>
|
|
extern int x;
|
|
int main() {
|
|
printf("extern is %d.\n", x);
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
int x = 123;
|
|
struct Initter {
|
|
Initter() { x = 456; }
|
|
};
|
|
Initter initter;
|
|
''', expected=['extern is 456.\n'])
|
|
|
|
def test_dylink_mallocs(self):
|
|
self.dylink_test(header=r'''
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
char *side(const char *data);
|
|
''', main=r'''
|
|
#include <stdio.h>
|
|
#include "header.h"
|
|
int main() {
|
|
char *temp = side("hello through side\n");
|
|
char *ret = (char*)malloc(strlen(temp)+1);
|
|
strcpy(ret, temp);
|
|
temp[1] = 'x';
|
|
puts(ret);
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include "header.h"
|
|
char *side(const char *data) {
|
|
char *ret = (char*)malloc(strlen(data)+1);
|
|
strcpy(ret, data);
|
|
return ret;
|
|
}
|
|
''', expected=['hello through side\n'])
|
|
|
|
def test_dylink_jslib(self):
|
|
open('lib.js', 'w').write(r'''
|
|
mergeInto(LibraryManager.library, {
|
|
test_lib_func: function(x) {
|
|
return x + 17.2;
|
|
}
|
|
});
|
|
''')
|
|
self.dylink_test(header=r'''
|
|
extern "C" { extern double test_lib_func(int input); }
|
|
''', main=r'''
|
|
#include <stdio.h>
|
|
#include "header.h"
|
|
extern double sidey();
|
|
int main2() { return 11; }
|
|
int main() {
|
|
int input = sidey();
|
|
double temp = test_lib_func(input);
|
|
printf("other says %.2f\n", temp);
|
|
printf("more: %.5f, %d\n", temp, input);
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include <stdio.h>
|
|
#include "header.h"
|
|
extern int main2();
|
|
double sidey() {
|
|
int temp = main2();
|
|
printf("main2 sed: %d\n", temp);
|
|
printf("main2 sed: %u, %c\n", temp, temp/2);
|
|
return test_lib_func(temp);
|
|
}
|
|
''', expected='other says 45.2', main_emcc_args=['--js-library', 'lib.js'])
|
|
|
|
def test_dylink_global_var_jslib(self):
|
|
open('lib.js', 'w').write(r'''
|
|
mergeInto(LibraryManager.library, {
|
|
jslib_x: 'allocate(1, "i32*", ALLOC_STATIC)',
|
|
jslib_x__postset: 'HEAP32[_jslib_x>>2] = 148;',
|
|
});
|
|
''')
|
|
self.dylink_test(main=r'''
|
|
#include <stdio.h>
|
|
extern "C" int jslib_x;
|
|
extern void call_side();
|
|
int main() {
|
|
printf("main: jslib_x is %d.\n", jslib_x);
|
|
call_side();
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include <stdio.h>
|
|
extern "C" int jslib_x;
|
|
void call_side() {
|
|
printf("side: jslib_x is %d.\n", jslib_x);
|
|
}
|
|
''', expected=['main: jslib_x is 148.\nside: jslib_x is 148.\n'], main_emcc_args=['--js-library', 'lib.js'])
|
|
|
|
def test_dylink_many_postSets(self):
|
|
NUM = 1234
|
|
self.dylink_test(header=r'''
|
|
#include <stdio.h>
|
|
typedef void (*voidfunc)();
|
|
static void simple() {
|
|
printf("simple.\n");
|
|
}
|
|
static volatile voidfunc funcs[''' + str(NUM) + '] = { ' + ','.join(['simple'] * NUM) + r''' };
|
|
static void test() {
|
|
volatile int i = ''' + str(NUM-1) + r''';
|
|
funcs[i]();
|
|
i = 0;
|
|
funcs[i]();
|
|
}
|
|
extern void more();
|
|
''', main=r'''
|
|
#include "header.h"
|
|
int main() {
|
|
test();
|
|
more();
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include "header.h"
|
|
void more() {
|
|
test();
|
|
}
|
|
''', expected=['simple.\nsimple.\nsimple.\nsimple.\n'])
|
|
|
|
def test_dylink_syslibs(self): # one module uses libcxx, need to force its inclusion when it isn't the main
|
|
def test(syslibs, expect_pass=True, need_reverse=True):
|
|
print 'syslibs', syslibs, Settings.ASSERTIONS
|
|
passed = True
|
|
try:
|
|
os.environ['EMCC_FORCE_STDLIBS'] = syslibs
|
|
self.dylink_test(main=r'''
|
|
void side();
|
|
int main() {
|
|
side();
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include <iostream>
|
|
void side() { std::cout << "cout hello from side"; }
|
|
''', expected=['cout hello from side\n'],
|
|
need_reverse=need_reverse)
|
|
except Exception, e:
|
|
if expect_pass: raise e
|
|
print '(seeing expected fail)'
|
|
passed = False
|
|
assertion = 'build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment'
|
|
if Settings.ASSERTIONS:
|
|
self.assertContained(assertion, str(e))
|
|
else:
|
|
self.assertNotContained(assertion, str(e))
|
|
finally:
|
|
del os.environ['EMCC_FORCE_STDLIBS']
|
|
assert passed == expect_pass, ['saw', passed, 'but expected', expect_pass]
|
|
|
|
test('libcxx')
|
|
test('1')
|
|
if 'ASSERTIONS=1' not in self.emcc_args:
|
|
Settings.ASSERTIONS = 0
|
|
test('', expect_pass=False, need_reverse=False)
|
|
else:
|
|
print '(skip ASSERTIONS == 0 part)'
|
|
Settings.ASSERTIONS = 1
|
|
test('', expect_pass=False, need_reverse=False)
|
|
|
|
def test_dylink_iostream(self):
|
|
try:
|
|
os.environ['EMCC_FORCE_STDLIBS'] = 'libcxx'
|
|
self.dylink_test(header=r'''
|
|
#include <iostream>
|
|
#include <string>
|
|
std::string side();
|
|
''', main=r'''
|
|
#include "header.h"
|
|
int main() {
|
|
std::cout << "hello from main " << side() << std::endl;
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include "header.h"
|
|
std::string side() { return "and hello from side"; }
|
|
''', expected=['hello from main and hello from side\n'])
|
|
finally:
|
|
del os.environ['EMCC_FORCE_STDLIBS']
|
|
|
|
def test_dylink_dynamic_cast(self): # issue 3465
|
|
self.dylink_test(header=r'''
|
|
class Base {
|
|
public:
|
|
virtual void printName();
|
|
};
|
|
|
|
class Derived : public Base {
|
|
public:
|
|
void printName();
|
|
};
|
|
''', main=r'''
|
|
#include "header.h"
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
|
|
int main() {
|
|
cout << "starting main" << endl;
|
|
|
|
Base *base = new Base();
|
|
Base *derived = new Derived();
|
|
base->printName();
|
|
derived->printName();
|
|
|
|
if (dynamic_cast<Derived*>(derived)) {
|
|
cout << "OK" << endl;
|
|
} else {
|
|
cout << "KO" << endl;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include "header.h"
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
|
|
void Base::printName() {
|
|
cout << "Base" << endl;
|
|
}
|
|
|
|
void Derived::printName() {
|
|
cout << "Derived" << endl;
|
|
}
|
|
''', expected=['starting main\nBase\nDerived\nOK'])
|
|
|
|
def test_dylink_hyper_dupe(self):
|
|
Settings.TOTAL_MEMORY = 64*1024*1024
|
|
|
|
if Settings.ASSERTIONS: self.emcc_args += ['-s', 'ASSERTIONS=2']
|
|
|
|
# test hyper-dynamic linking, and test duplicate warnings
|
|
open('third.cpp', 'w').write(r'''
|
|
int sidef() { return 36; }
|
|
int sideg = 49;
|
|
int bsidef() { return 536; }
|
|
''')
|
|
Popen([PYTHON, EMCC, 'third.cpp', '-s', 'SIDE_MODULE=1'] + Building.COMPILER_TEST_OPTS + self.emcc_args + ['-o', 'third.js']).communicate()
|
|
|
|
self.dylink_test(main=r'''
|
|
#include <stdio.h>
|
|
#include <emscripten.h>
|
|
extern int sidef();
|
|
extern int sideg;
|
|
extern int bsidef();
|
|
extern int bsideg;
|
|
int main() {
|
|
EM_ASM({
|
|
Runtime.loadDynamicLibrary('third.js'); // hyper-dynamic! works at least for functions (and consts not used in same block)
|
|
});
|
|
printf("sidef: %d, sideg: %d.\n", sidef(), sideg);
|
|
printf("bsidef: %d.\n", bsidef());
|
|
}
|
|
''', side=r'''
|
|
int sidef() { return 10; } // third.js will try to override these, but fail!
|
|
int sideg = 20;
|
|
''', expected=['sidef: 10, sideg: 20.\nbsidef: 536.\n'])
|
|
|
|
if Settings.ASSERTIONS:
|
|
print 'check warnings'
|
|
full = run_js('src.cpp.o.js', engine=JS_ENGINES[0], full_output=True, stderr=STDOUT)
|
|
#self.assertContained("warning: trying to dynamically load symbol '__Z5sidefv' (from 'third.js') that already exists", full)
|
|
self.assertContained("warning: trying to dynamically load symbol '_sideg' (from 'third.js') that already exists", full)
|
|
|
|
def test_dylink_dot_a(self):
|
|
# .a linking must force all .o files inside it, when in a shared module
|
|
open('third.cpp', 'w').write(r'''
|
|
int sidef() { return 36; }
|
|
''')
|
|
Popen([PYTHON, EMCC, 'third.cpp'] + Building.COMPILER_TEST_OPTS + self.emcc_args + ['-o', 'third.o', '-c']).communicate()
|
|
|
|
open('fourth.cpp', 'w').write(r'''
|
|
int sideg() { return 17; }
|
|
''')
|
|
Popen([PYTHON, EMCC, 'fourth.cpp'] + Building.COMPILER_TEST_OPTS + self.emcc_args + ['-o', 'fourth.o', '-c']).communicate()
|
|
|
|
Popen([PYTHON, EMAR, 'rc', 'libfourth.a', 'fourth.o']).communicate()
|
|
|
|
self.dylink_test(main=r'''
|
|
#include <stdio.h>
|
|
#include <emscripten.h>
|
|
extern int sidef();
|
|
extern int sideg();
|
|
int main() {
|
|
printf("sidef: %d, sideg: %d.\n", sidef(), sideg());
|
|
}
|
|
''', side=['libfourth.a', 'third.o'], # contents of libtwo.a must be included, even if they aren't referred to!
|
|
expected=['sidef: 36, sideg: 17.\n'])
|
|
|
|
def test_dylink_spaghetti(self):
|
|
self.dylink_test(main=r'''
|
|
#include <stdio.h>
|
|
int main_x = 72;
|
|
extern int side_x;
|
|
int adjust = side_x + 10;
|
|
int *ptr = &side_x;
|
|
struct Class {
|
|
Class() {
|
|
printf("main init sees %d, %d, %d.\n", adjust, *ptr, main_x);
|
|
}
|
|
};
|
|
Class cm;
|
|
int main() {
|
|
printf("main main sees %d, %d, %d.\n", adjust, *ptr, main_x);
|
|
return 0;
|
|
}
|
|
''', side=r'''
|
|
#include <stdio.h>
|
|
extern int main_x;
|
|
int side_x = -534;
|
|
int adjust2 = main_x + 10;
|
|
int *ptr2 = &main_x;
|
|
struct Class {
|
|
Class() {
|
|
printf("side init sees %d, %d, %d.\n", adjust2, *ptr2, side_x);
|
|
}
|
|
};
|
|
Class cs;
|
|
''', expected=['side init sees 82, 72, -534.\nmain init sees -524, -534, 72.\nmain main sees -524, -534, 72.',
|
|
'main init sees -524, -534, 72.\nside init sees 82, 72, -534.\nmain main sees -524, -534, 72.'])
|
|
|
|
def test_dylink_zlib(self):
|
|
Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('tests', 'zlib')]
|
|
|
|
Popen([PYTHON, path_from_root('embuilder.py'), 'build' ,'zlib']).communicate()
|
|
zlib = Cache.get_path(os.path.join('ports-builds', 'zlib', 'libz.a'))
|
|
try:
|
|
os.environ['EMCC_FORCE_STDLIBS'] = 'libcextra'
|
|
side = [zlib]
|
|
self.dylink_test(main=open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(),
|
|
side=side,
|
|
expected=open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
|
|
force_c=True)
|
|
finally:
|
|
del os.environ['EMCC_FORCE_STDLIBS']
|
|
|
|
#def test_dylink_bullet(self):
|
|
# Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('tests', 'bullet', 'src')]
|
|
# side = get_bullet_library(self, True)
|
|
# self.dylink_test(main=open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(),
|
|
# side=side,
|
|
# expected=[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()])
|
|
|
|
def test_random(self):
|
|
src = r'''#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
int main()
|
|
{
|
|
srandom(0xdeadbeef);
|
|
printf("%ld", random());
|
|
}
|
|
'''
|
|
self.do_run(src, '956867869')
|
|
|
|
def test_rand(self):
|
|
src = r'''#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
int main()
|
|
{
|
|
// we need RAND_MAX to be a bitmask (power of 2 minus 1). this assertions guarantees
|
|
// if RAND_MAX changes the test failure will focus attention on that issue here.
|
|
assert(RAND_MAX == 0x7fffffff);
|
|
|
|
srand(0xdeadbeef);
|
|
for(int i = 0; i < 10; ++i)
|
|
printf("%d\n", rand());
|
|
|
|
unsigned int seed = 0xdeadbeef;
|
|
for(int i = 0; i < 10; ++i)
|
|
printf("%d\n", rand_r(&seed));
|
|
|
|
bool haveEvenAndOdd = true;
|
|
for(int i = 1; i <= 30; ++i)
|
|
{
|
|
int mask = 1 << i;
|
|
if (mask > RAND_MAX) break;
|
|
bool haveEven = false;
|
|
bool haveOdd = false;
|
|
for(int j = 0; j < 1000 && (!haveEven || !haveOdd); ++j)
|
|
{
|
|
if ((rand() & mask) == 0)
|
|
haveEven = true;
|
|
else
|
|
haveOdd = true;
|
|
}
|
|
haveEvenAndOdd = haveEvenAndOdd && haveEven && haveOdd;
|
|
}
|
|
if (haveEvenAndOdd)
|
|
printf("Have even and odd!\n");
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
expected = '''490242850
|
|
2074599277
|
|
1480056542
|
|
1912638067
|
|
931112055
|
|
2110392489
|
|
2053422194
|
|
1614832492
|
|
216117595
|
|
174823244
|
|
760368382
|
|
602359081
|
|
1121118963
|
|
1291018924
|
|
1608306807
|
|
352705809
|
|
958258461
|
|
1182561381
|
|
114276303
|
|
1481323674
|
|
Have even and odd!
|
|
'''
|
|
self.do_run(src, expected)
|
|
|
|
def test_strtod(self):
|
|
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));
|
|
printf("%g\n", strtod("0x6", &endptr));
|
|
printf("%g\n", strtod("-0x0p+0", &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
|
|
6
|
|
-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):
|
|
test_path = path_from_root('tests', 'core', 'test_strtok')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_parseInt(self):
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_transtrcase')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_printf(self):
|
|
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()
|
|
self.do_run(src, expected)
|
|
|
|
def test_printf_2(self):
|
|
test_path = path_from_root('tests', 'core', 'test_printf_2')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_vprintf(self):
|
|
test_path = path_from_root('tests', 'core', 'test_vprintf')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_vsnprintf(self):
|
|
test_path = path_from_root('tests', 'core', 'test_vsnprintf')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_printf_more(self):
|
|
test_path = path_from_root('tests', 'core', 'test_printf_more')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_perrar(self):
|
|
test_path = path_from_root('tests', 'core', 'test_perrar')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_atoX(self):
|
|
test_path = path_from_root('tests', 'core', 'test_atoX')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_strstr(self):
|
|
test_path = path_from_root('tests', 'core', 'test_strstr')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_fnmatch(self):
|
|
# Run one test without assertions, for additional coverage
|
|
#assert 'asm2m' in test_modes
|
|
if self.run_name == 'asm2m':
|
|
i = self.emcc_args.index('ASSERTIONS=1')
|
|
assert i > 0 and self.emcc_args[i-1] == '-s'
|
|
self.emcc_args[i] = 'ASSERTIONS=0'
|
|
print 'flip assertions off'
|
|
test_path = path_from_root('tests', 'core', 'fnmatch')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_2(self):
|
|
# doubles
|
|
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.789062 123456.789062
|
|
Pass: 123456.789062 123456.789062
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_n')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_whitespace(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_whitespace')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_other_whitespace(self):
|
|
Settings.SAFE_HEAP = 0 # use i16s in printf
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_other_whitespace')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_3(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_3')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_4(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_4')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_5(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_5')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_6(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_6')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_skip(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_skip')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_caps(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_caps')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_hex(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_hex')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_sscanf_float(self):
|
|
test_path = path_from_root('tests', 'core', 'test_sscanf_float')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_langinfo(self):
|
|
src = open(path_from_root('tests', 'langinfo', 'test.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'langinfo', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/langinfo.h'])
|
|
|
|
def test_files(self):
|
|
self.banned_js_engines = [SPIDERMONKEY_ENGINE] # closure can generate variables called 'gc', which pick up js shell stuff
|
|
if '-O2' in self.emcc_args:
|
|
self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff
|
|
self.emcc_args = filter(lambda x: x != '-g', self.emcc_args) # ensure we test --closure 1 --memory-init-file 1 (-g would disable closure)
|
|
|
|
post = '''
|
|
def process(filename):
|
|
src = \'\'\'
|
|
var Module = {
|
|
'noFSInit': true,
|
|
'preRun': function() {
|
|
FS.createLazyFile('/', 'test.file', 'test.file', true, false);
|
|
// Test FS_* exporting
|
|
Module['FS_createDataFile']('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory
|
|
var test_files_input = 'hi there!';
|
|
var test_files_input_index = 0;
|
|
FS.init(function() {
|
|
return test_files_input.charCodeAt(test_files_input_index++) || null;
|
|
});
|
|
}
|
|
};
|
|
\'\'\' + open(filename, 'r').read()
|
|
open(filename, 'w').write(src)
|
|
'''
|
|
other = open(os.path.join(self.get_dir(), 'test.file'), 'w')
|
|
other.write('some data');
|
|
other.close()
|
|
|
|
src = open(path_from_root('tests', 'files.cpp'), 'r').read()
|
|
|
|
mem_file = 'src.cpp.o.js.mem'
|
|
orig_args = self.emcc_args
|
|
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1'], ['-s', 'SYSCALL_DEBUG=1']]:
|
|
print mode
|
|
self.emcc_args = orig_args + mode
|
|
try_delete(mem_file)
|
|
self.do_run(src, map(lambda x: x if 'SYSCALL_DEBUG=1' not in mode else ('syscall! 146,SYS_writev' if self.run_name == 'default' else 'syscall! 146'), ('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\n5 bytes to dev/null: 5\nok.\n \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\n5 bytes to dev/null: 5\nok.\n')),
|
|
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
|
if self.uses_memory_init_file():
|
|
assert os.path.exists(mem_file)
|
|
|
|
def test_files_m(self):
|
|
# Test for Module.stdin etc.
|
|
|
|
post = '''
|
|
def process(filename):
|
|
src = \'\'\'
|
|
var Module = {
|
|
data: [10, 20, 40, 30],
|
|
stdin: function() { return Module.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;
|
|
}
|
|
'''
|
|
def clean(out, err):
|
|
return '\n'.join(filter(lambda line: 'warning' not in line, (out + err).split('\n')))
|
|
self.do_run(src, ('got: 35\ngot: 45\ngot: 25\ngot: 15\n \nisatty? 0,0,1\n', '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, output_nicerizer=clean)
|
|
|
|
def test_mount(self):
|
|
src = open(path_from_root('tests', 'fs', 'test_mount.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_fwrite_0(self):
|
|
test_path = path_from_root('tests', 'core', 'test_fwrite_0')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
orig_args = self.emcc_args
|
|
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
|
|
self.emcc_args = orig_args + mode
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_fgetc_ungetc(self):
|
|
logging.warning('TODO: update this test once the musl ungetc-on-EOF-stream bug is fixed upstream and reaches us')
|
|
Settings.SYSCALL_DEBUG = 1
|
|
self.clear()
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['MEMFS', 'NODEFS']:
|
|
print fs
|
|
src = open(path_from_root('tests', 'stdio', 'test_fgetc_ungetc.c'), 'r').read()
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS])
|
|
|
|
def test_fgetc_unsigned(self):
|
|
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):
|
|
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):
|
|
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_fscanf_2(self):
|
|
open('a.txt', 'w').write('''1/2/3 4/5/6 7/8/9
|
|
''')
|
|
self.emcc_args += ['--embed-file', 'a.txt']
|
|
self.do_run(r'''#include <cstdio>
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
|
|
int
|
|
main( int argv, char ** argc ) {
|
|
cout << "fscanf test" << endl;
|
|
|
|
FILE * file;
|
|
file = fopen("a.txt", "rb");
|
|
int vertexIndex[4];
|
|
int normalIndex[4];
|
|
int uvIndex[4];
|
|
|
|
int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex [1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2], &vertexIndex[3], &uvIndex[3], &normalIndex[3]);
|
|
|
|
cout << matches << endl;
|
|
|
|
return 0;
|
|
}
|
|
''', 'fscanf test\n9\n')
|
|
|
|
def test_fileno(self):
|
|
open(os.path.join(self.get_dir(), 'empty.txt'), 'w').write('')
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
int main()
|
|
{
|
|
FILE* fp = fopen("empty.txt", "r");
|
|
if (fp) {
|
|
printf("%d\n", fileno(fp));
|
|
} else {
|
|
printf("failed to open empty.txt\n");
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
self.emcc_args += ['--embed-file', 'empty.txt']
|
|
self.do_run(src, '3\n')
|
|
|
|
def test_readdir(self):
|
|
self.banned_js_engines = [V8_ENGINE] # stderr printing limitations in v8
|
|
src = open(path_from_root('tests', 'dirent', 'test_readdir.c'), 'r').read()
|
|
self.do_run(src, '''SIGILL: Illegal instruction
|
|
success
|
|
n: 8
|
|
name: tmp
|
|
name: proc
|
|
name: nocanread
|
|
name: home
|
|
name: foobar
|
|
name: dev
|
|
name: ..
|
|
name: .
|
|
''', force_c=True)
|
|
|
|
def test_readdir_empty(self):
|
|
src = open(path_from_root('tests', 'dirent', 'test_readdir_empty.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_stat(self):
|
|
src = open(path_from_root('tests', 'stat', 'test_stat.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_stat_chmod(self):
|
|
src = open(path_from_root('tests', 'stat', 'test_chmod.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_stat_mknod(self):
|
|
src = open(path_from_root('tests', 'stat', 'test_mknod.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_fcntl(self):
|
|
add_pre_run = '''
|
|
def process(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
"FS.createDataFile('/', 'test', 'abcdef', true, true);"
|
|
)
|
|
open(filename, 'w').write(src)
|
|
'''
|
|
src = open(path_from_root('tests', 'fcntl', 'src.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'fcntl', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
|
|
|
def test_fcntl_open(self):
|
|
src = open(path_from_root('tests', 'fcntl-open', 'src.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'fcntl-open', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected, force_c=True, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
|
|
|
def test_fcntl_misc(self):
|
|
add_pre_run = '''
|
|
def process(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
"FS.createDataFile('/', 'test', 'abcdef', true, true);"
|
|
)
|
|
open(filename, 'w').write(src)
|
|
'''
|
|
src = open(path_from_root('tests', 'fcntl-misc', 'src.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'fcntl-misc', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
|
|
|
def test_poll(self):
|
|
add_pre_run = '''
|
|
def process(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
\'\'\'
|
|
var dummy_device = FS.makedev(64, 0);
|
|
FS.registerDevice(dummy_device, {});
|
|
|
|
FS.createDataFile('/', 'file', 'abcdef', true, true);
|
|
FS.mkdev('/device', dummy_device);
|
|
\'\'\'
|
|
)
|
|
open(filename, 'w').write(src)
|
|
'''
|
|
test_path = path_from_root('tests', 'core', 'test_poll')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h,poll.h'])
|
|
|
|
def test_statvfs(self):
|
|
test_path = path_from_root('tests', 'core', 'test_statvfs')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_libgen(self):
|
|
test_path = path_from_root('tests', 'core', 'test_libgen')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
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']
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_utf')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_utf32(self):
|
|
self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.')
|
|
self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.', args=['-fshort-wchar'])
|
|
|
|
def test_utf8(self):
|
|
Building.COMPILER_TEST_OPTS += ['-std=c++11']
|
|
self.do_run(open(path_from_root('tests', 'utf8.cpp')).read(), 'OK.')
|
|
|
|
def test_wprintf(self):
|
|
test_path = path_from_root('tests', 'core', 'test_wprintf')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
orig_args = self.emcc_args
|
|
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
|
|
self.emcc_args = orig_args + mode
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_direct_string_constant_usage(self):
|
|
test_path = path_from_root('tests', 'core', 'test_direct_string_constant_usage')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_std_cout_new(self):
|
|
test_path = path_from_root('tests', 'core', 'test_std_cout_new')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_istream(self):
|
|
test_path = path_from_root('tests', 'core', 'test_istream')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
for linkable in [0]:#, 1]:
|
|
print linkable
|
|
Settings.LINKABLE = linkable # regression check for issue #273
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_fs_base(self):
|
|
Settings.INCLUDE_FULL_LIBRARY = 1
|
|
try:
|
|
addJS = '''
|
|
def process(filename):
|
|
import tools.shared as shared
|
|
src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(shared.path_from_root('tests', 'filesystem', 'src.js'), 'r').read())
|
|
open(filename, 'w').write(src)
|
|
'''
|
|
src = 'int main() {return 0;}\n'
|
|
expected = open(path_from_root('tests', 'filesystem', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected, post_build=addJS, extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'])
|
|
finally:
|
|
Settings.INCLUDE_FULL_LIBRARY = 0
|
|
|
|
def test_fs_nodefs_rw(self):
|
|
Settings.SYSCALL_DEBUG = 1
|
|
src = open(path_from_root('tests', 'fs', 'test_nodefs_rw.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS])
|
|
print 'closure'
|
|
self.emcc_args += ['--closure', '1']
|
|
self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS])
|
|
|
|
def test_fs_trackingdelegate(self):
|
|
src = path_from_root('tests', 'fs', 'test_trackingdelegate.c')
|
|
out = path_from_root('tests', 'fs', 'test_trackingdelegate.out')
|
|
self.do_run_from_file(src, out)
|
|
|
|
def test_fs_writeFile(self):
|
|
self.emcc_args += ['-s', 'DISABLE_EXCEPTION_CATCHING=1'] # see issue 2334
|
|
src = path_from_root('tests', 'fs', 'test_writeFile.cc')
|
|
out = path_from_root('tests', 'fs', 'test_writeFile.out')
|
|
self.do_run_from_file(src, out)
|
|
|
|
def test_fs_emptyPath(self):
|
|
src = path_from_root('tests', 'fs', 'test_emptyPath.c')
|
|
out = path_from_root('tests', 'fs', 'test_emptyPath.out')
|
|
self.do_run_from_file(src, out)
|
|
|
|
def test_fs_append(self):
|
|
src = open(path_from_root('tests', 'fs', 'test_append.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_fs_mmap(self):
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['MEMFS']:
|
|
src = path_from_root('tests', 'fs', 'test_mmap.c')
|
|
out = path_from_root('tests', 'fs', 'test_mmap.out')
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
self.do_run_from_file(src, out)
|
|
|
|
def test_unistd_access(self):
|
|
self.clear()
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['MEMFS', 'NODEFS']:
|
|
src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read()
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
self.do_run(src, expected, js_engines=[NODE_JS])
|
|
|
|
def test_unistd_curdir(self):
|
|
src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_unistd_close(self):
|
|
src = open(path_from_root('tests', 'unistd', 'close.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'close.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_unistd_confstr(self):
|
|
src = open(path_from_root('tests', 'unistd', 'confstr.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'confstr.out'), 'r').read()
|
|
self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/unistd.h'])
|
|
|
|
def test_unistd_ttyname(self):
|
|
src = open(path_from_root('tests', 'unistd', 'ttyname.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_unistd_dup(self):
|
|
src = open(path_from_root('tests', 'unistd', 'dup.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'dup.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_unistd_pathconf(self):
|
|
src = open(path_from_root('tests', 'unistd', 'pathconf.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'pathconf.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_unistd_truncate(self):
|
|
self.clear()
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['MEMFS', 'NODEFS']:
|
|
src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read()
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
self.do_run(src, expected, js_engines=[NODE_JS])
|
|
|
|
def test_unistd_swab(self):
|
|
src = open(path_from_root('tests', 'unistd', 'swab.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'swab.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_unistd_isatty(self):
|
|
src = open(path_from_root('tests', 'unistd', 'isatty.c'), 'r').read()
|
|
self.do_run(src, 'success', force_c=True)
|
|
|
|
def test_unistd_sysconf(self):
|
|
src = open(path_from_root('tests', 'unistd', 'sysconf.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'sysconf.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_unistd_login(self):
|
|
src = open(path_from_root('tests', 'unistd', 'login.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'login.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_unistd_unlink(self):
|
|
self.clear()
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['MEMFS', 'NODEFS']:
|
|
src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read()
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
# symlinks on node.js on Windows require administrative privileges, so skip testing those bits on that combination.
|
|
if WINDOWS and fs == 'NODEFS': Building.COMPILER_TEST_OPTS += ['-DNO_SYMLINK=1']
|
|
self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS])
|
|
|
|
def test_unistd_links(self):
|
|
self.clear()
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['MEMFS', 'NODEFS']:
|
|
if WINDOWS and fs == 'NODEFS':
|
|
print >> sys.stderr, 'Skipping NODEFS part of this test for test_unistd_links on Windows, since it would require administrative privileges.'
|
|
# Also, other detected discrepancies if you do end up running this test on NODEFS:
|
|
# test expects /, but Windows gives \ as path slashes.
|
|
# Calling readlink() on a non-link gives error 22 EINVAL on Unix, but simply error 0 OK on Windows.
|
|
continue
|
|
src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read()
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
self.do_run(src, expected, js_engines=[NODE_JS])
|
|
|
|
def test_unistd_symlink_on_nodefs(self):
|
|
self.clear()
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['NODEFS']:
|
|
if WINDOWS and fs == 'NODEFS':
|
|
print >> sys.stderr, 'Skipping NODEFS part of this test for test_unistd_symlink_on_nodefs on Windows, since it would require administrative privileges.'
|
|
# Also, other detected discrepancies if you do end up running this test on NODEFS:
|
|
# test expects /, but Windows gives \ as path slashes.
|
|
# Calling readlink() on a non-link gives error 22 EINVAL on Unix, but simply error 0 OK on Windows.
|
|
continue
|
|
src = open(path_from_root('tests', 'unistd', 'symlink_on_nodefs.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'symlink_on_nodefs.out'), 'r').read()
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
self.do_run(src, expected, js_engines=[NODE_JS])
|
|
|
|
def test_unistd_sleep(self):
|
|
src = open(path_from_root('tests', 'unistd', 'sleep.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'sleep.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_unistd_io(self):
|
|
self.clear()
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['MEMFS', 'NODEFS']:
|
|
src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read()
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
self.do_run(src, expected, js_engines=[NODE_JS])
|
|
|
|
def test_unistd_misc(self):
|
|
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
|
|
for fs in ['MEMFS', 'NODEFS']:
|
|
src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'misc.out'), 'r').read()
|
|
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs]
|
|
self.do_run(src, expected, js_engines=[NODE_JS])
|
|
|
|
def test_posixtime(self):
|
|
test_path = path_from_root('tests', 'core', 'test_posixtime')
|
|
src, output = (test_path + s for s in ('.c', '.out'))
|
|
self.banned_js_engines = [V8_ENGINE] # v8 lacks monotonic time
|
|
self.do_run_from_file(src, output)
|
|
|
|
if V8_ENGINE in JS_ENGINES:
|
|
self.banned_js_engines = filter(lambda engine: engine != V8_ENGINE, JS_ENGINES)
|
|
self.do_run_from_file(src, test_path + '_no_monotonic.out')
|
|
else:
|
|
print '(no v8, skipping no-monotonic case)'
|
|
|
|
def test_uname(self):
|
|
test_path = path_from_root('tests', 'core', 'test_uname')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
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.replace('{{{ THIS_PROGRAM }}}', os.path.join(self.get_dir(), 'src.cpp.o.js').replace('\\', '/')), # node, can find itself properly
|
|
expected.replace('{{{ THIS_PROGRAM }}}', './this.program') # spidermonkey, v8
|
|
])
|
|
|
|
def test_environ(self):
|
|
src = open(path_from_root('tests', 'env', 'src-mini.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'env', 'output-mini.txt'), 'r').read()
|
|
self.do_run(src, [
|
|
expected.replace('{{{ THIS_PROGRAM }}}', os.path.join(self.get_dir(), 'src.cpp.o.js').replace('\\', '/')), # node, can find itself properly
|
|
expected.replace('{{{ THIS_PROGRAM }}}', './this.program') # spidermonkey, v8
|
|
])
|
|
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_getloadavg')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_nl_types(self):
|
|
test_path = path_from_root('tests', 'core', 'test_nl_types')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
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):
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_strcasecmp')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_atomic(self):
|
|
test_path = path_from_root('tests', 'core', 'test_atomic')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_atomic_cxx(self):
|
|
test_path = path_from_root('tests', 'core', 'test_atomic_cxx')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
Building.COMPILER_TEST_OPTS += ['-std=c++11']
|
|
self.do_run_from_file(src, output)
|
|
|
|
print 'main module'
|
|
Settings.MAIN_MODULE = 1
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_phiundef(self):
|
|
test_path = path_from_root('tests', 'core', 'test_phiundef')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_netinet_in(self):
|
|
src = open(path_from_root('tests', 'netinet', 'in.cpp'), 'r').read()
|
|
expected = open(path_from_root('tests', 'netinet', 'in.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
# libc++ tests
|
|
|
|
def test_iostream(self):
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_stdvec')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_random_device(self):
|
|
Building.COMPILER_TEST_OPTS += ['-std=c++11']
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_random_device')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_reinterpreted_ptrs(self):
|
|
test_path = path_from_root('tests', 'core', 'test_reinterpreted_ptrs')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_jansson(self):
|
|
return self.skip('currently broken')
|
|
|
|
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!')
|
|
|
|
def test_js_libraries(self):
|
|
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
|
|
#include <stdio.h>
|
|
extern "C" {
|
|
extern void printey();
|
|
extern int calcey(int x, int y);
|
|
}
|
|
int main() {
|
|
printey();
|
|
printf("*%d*\\n", calcey(10, 22));
|
|
return 0;
|
|
}
|
|
''')
|
|
open(os.path.join(self.get_dir(), 'mylib1.js'), 'w').write('''
|
|
mergeInto(LibraryManager.library, {
|
|
printey: function() {
|
|
Module.print('hello from lib!');
|
|
}
|
|
});
|
|
''')
|
|
open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write('''
|
|
mergeInto(LibraryManager.library, {
|
|
calcey: function(x, y) {
|
|
return x + y;
|
|
}
|
|
});
|
|
''')
|
|
|
|
self.emcc_args += ['--js-library', os.path.join(self.get_dir(), 'mylib1.js'), '--js-library', os.path.join(self.get_dir(), 'mylib2.js')]
|
|
self.do_run(open(os.path.join(self.get_dir(), 'main.cpp'), 'r').read(), 'hello from lib!\n*32*\n')
|
|
|
|
def test_unicode_js_library(self):
|
|
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
|
|
#include <stdio.h>
|
|
extern "C" {
|
|
extern void printey();
|
|
}
|
|
int main() {
|
|
printey();
|
|
return 0;
|
|
}
|
|
''')
|
|
self.emcc_args += ['--js-library', path_from_root('tests', 'unicode_library.js')]
|
|
self.do_run(open(os.path.join(self.get_dir(), 'main.cpp'), 'r').read(), u'Unicode snowman \u2603 says hello!')
|
|
|
|
def test_constglobalunion(self):
|
|
self.emcc_args += ['-s', 'EXPORT_ALL=1']
|
|
|
|
self.do_run(r'''
|
|
#include <stdio.h>
|
|
|
|
struct one_const {
|
|
long a;
|
|
};
|
|
|
|
struct two_consts {
|
|
long a;
|
|
long b;
|
|
};
|
|
|
|
union some_consts {
|
|
struct one_const one;
|
|
struct two_consts two;
|
|
};
|
|
|
|
union some_consts my_consts = {{
|
|
1
|
|
}};
|
|
|
|
struct one_const addr_of_my_consts = {
|
|
(long)(&my_consts)
|
|
};
|
|
|
|
int main(void) {
|
|
printf("%li\n", !!addr_of_my_consts.a);
|
|
return 0;
|
|
}
|
|
''', '1')
|
|
|
|
### '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):
|
|
# TODO: Should we remove this test?
|
|
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):
|
|
results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''),
|
|
(50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ]
|
|
for precision in [0, 1, 2]:
|
|
Settings.PRECISE_F32 = precision
|
|
for t in ['float', 'double']:
|
|
print precision, t
|
|
src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', t)
|
|
for i, j in results:
|
|
self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
|
|
shutil.copyfile('src.cpp.o.js', '%d_%s.js' % (precision, t))
|
|
|
|
def test_whets(self):
|
|
self.do_run(open(path_from_root('tests', 'whets.cpp')).read(), 'Single Precision C Whetstone Benchmark')
|
|
|
|
def test_dlmalloc(self):
|
|
self.banned_js_engines = [NODE_JS] # slower, and fail on 64-bit
|
|
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):
|
|
# 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 'SAFE_HEAP' 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.
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_dlmalloc_partial_2')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_libcxx(self):
|
|
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):
|
|
test_path = path_from_root('tests', 'core', 'test_typeid')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_static_variable(self):
|
|
test_path = path_from_root('tests', 'core', 'test_static_variable')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_fakestat(self):
|
|
test_path = path_from_root('tests', 'core', 'test_fakestat')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_mmap(self):
|
|
Settings.TOTAL_MEMORY = 128*1024*1024
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_mmap')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
self.do_run_from_file(src, output, force_c=True)
|
|
|
|
def test_mmap_file(self):
|
|
for extra_args in [[], ['--no-heap-copy']]:
|
|
self.emcc_args += ['--embed-file', 'data.dat'] + extra_args
|
|
x = 'data from the file........'
|
|
s = ''
|
|
while len(s) < 9000:
|
|
if len(s) + len(x) < 9000:
|
|
s += x
|
|
continue
|
|
s += '.'
|
|
assert len(s) == 9000
|
|
open(self.in_dir('data.dat'), 'w').write(s)
|
|
src = open(path_from_root('tests', 'mmap_file.c')).read()
|
|
self.do_run(src, '*\n' + s[0:20] + '\n' + s[4096:4096+20] + '\n*\n')
|
|
|
|
def test_cubescript(self):
|
|
assert 'asm3' in test_modes
|
|
if self.run_name == 'asm3':
|
|
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
|
|
|
|
def test():
|
|
self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp')
|
|
|
|
test()
|
|
|
|
assert 'asm1' in test_modes
|
|
if self.run_name == 'asm1':
|
|
print 'verifing postsets'
|
|
generated = open('src.cpp.o.js').read()
|
|
generated = re.sub(r'\n+[ \n]*\n+', '\n', generated)
|
|
main = generated[generated.find('function runPostSets'):]
|
|
main = main[:main.find('\n}')]
|
|
assert main.count('\n') <= 7, ('must not emit too many postSets: %d' % main.count('\n')) + ' : ' + main
|
|
|
|
print 'relocatable'
|
|
assert Settings.RELOCATABLE == Settings.EMULATED_FUNCTION_POINTERS == 0
|
|
Settings.RELOCATABLE = Settings.EMULATED_FUNCTION_POINTERS = 1
|
|
test()
|
|
Settings.RELOCATABLE = Settings.EMULATED_FUNCTION_POINTERS = 0
|
|
|
|
if self.is_emterpreter():
|
|
print 'emterpreter/async/assertions' # extra coverage
|
|
self.emcc_args += ['-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'ASSERTIONS=1']
|
|
test()
|
|
print 'emterpreter/async/assertions/whitelist'
|
|
self.emcc_args += ['-s', 'EMTERPRETIFY_WHITELIST=["_frexpl"]'] # test double call assertions
|
|
test()
|
|
|
|
def test_sse1(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
if 'SAFE_HEAP=1' in self.emcc_args and SPIDERMONKEY_ENGINE in JS_ENGINES:
|
|
self.banned_js_engines += [SPIDERMONKEY_ENGINE]
|
|
print 'Skipping test_sse1 with SAFE_HEAP=1 on SpiderMonkey, since it fails due to NaN canonicalization.'
|
|
Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround
|
|
|
|
orig_args = self.emcc_args
|
|
for mode in [[], ['-s', 'SIMD=1']]:
|
|
self.emcc_args = orig_args + mode + ['-msse']
|
|
self.do_run(open(path_from_root('tests', 'test_sse1.cpp'), 'r').read(), 'Success!')
|
|
|
|
# Tests the full SSE1 API.
|
|
def test_sse1_full(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
Popen([CLANG, path_from_root('tests', 'test_sse1_full.cpp'), '-o', 'test_sse1_full', '-D_CRT_SECURE_NO_WARNINGS=1'] + get_clang_native_args(), stdout=PIPE).communicate()
|
|
native_result, err = Popen('./test_sse1_full', stdout=PIPE).communicate()
|
|
native_result = native_result.replace('\r\n', '\n') # Windows line endings fix
|
|
|
|
Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround
|
|
orig_args = self.emcc_args
|
|
for mode in [[], ['-s', 'SIMD=1']]:
|
|
self.emcc_args = orig_args + mode + ['-I' + path_from_root('tests'), '-msse']
|
|
self.do_run(open(path_from_root('tests', 'test_sse1_full.cpp'), 'r').read(), native_result)
|
|
|
|
# Tests the full SSE2 API.
|
|
def test_sse2_full(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
args = []
|
|
if '-O0' in self.emcc_args: args += ['-D_DEBUG=1']
|
|
Popen([CLANG, path_from_root('tests', 'test_sse2_full.cpp'), '-o', 'test_sse2_full', '-D_CRT_SECURE_NO_WARNINGS=1'] + args + get_clang_native_args(), stdout=PIPE).communicate()
|
|
native_result, err = Popen('./test_sse2_full', stdout=PIPE).communicate()
|
|
native_result = native_result.replace('\r\n', '\n') # Windows line endings fix
|
|
|
|
Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround
|
|
orig_args = self.emcc_args
|
|
for mode in [[], ['-s', 'SIMD=1']]:
|
|
self.emcc_args = orig_args + mode + ['-I' + path_from_root('tests'), '-msse2'] + args
|
|
self.do_run(open(path_from_root('tests', 'test_sse2_full.cpp'), 'r').read(), native_result)
|
|
|
|
def test_simd(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd2(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd2')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd3(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd3')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse2']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd4(self):
|
|
# test_simd4 is to test phi node handling of SIMD path
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd4')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd5(self):
|
|
# test_simd5 is to test shufflevector of SIMD path
|
|
test_path = path_from_root('tests', 'core', 'test_simd5')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd6(self):
|
|
# test_simd6 is to test x86 min and max intrinsics on NaN and -0.0
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd6')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd7(self):
|
|
# test_simd7 is to test negative zero handling: https://github.com/kripken/emscripten/issues/2791
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd7')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd8(self):
|
|
# test_simd8 is to test unaligned load and store
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd8')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd9(self):
|
|
# test_simd9 is to test a bug where _mm_set_ps(0.f) would generate an expression that did not validate as asm.js
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd9')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd10(self):
|
|
# test_simd10 is to test that loading and storing arbitrary bit patterns works in SSE1.
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd10')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd11(self):
|
|
# test_simd11 is to test that _mm_movemask_ps works correctly when handling input floats with 0xFFFFFFFF NaN bit patterns.
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd11')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse2']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd12(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd12')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd13(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd13')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_simd_dyncall(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_simd_dyncall')
|
|
src, output = (test_path + s for s in ('.cpp', '.txt'))
|
|
self.emcc_args = self.emcc_args + ['-msse']
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_gcc_unmangler(self):
|
|
Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('third_party'), '-Wno-warn-absolute-paths']
|
|
|
|
self.do_run(open(path_from_root('third_party', 'gcc_demangler.c')).read(), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'])
|
|
|
|
def test_lua(self):
|
|
if self.emcc_args: self.emcc_args = ['-g1'] + self.emcc_args
|
|
|
|
total_memory = Settings.TOTAL_MEMORY
|
|
|
|
if self.is_emterpreter():
|
|
Settings.PRECISE_F32 = 1
|
|
|
|
for aggro in ([0, 1] if Settings.ASM_JS and '-O2' in self.emcc_args else [0]):
|
|
Settings.AGGRESSIVE_VARIABLE_ELIMINATION = aggro
|
|
Settings.TOTAL_MEMORY = total_memory
|
|
print aggro
|
|
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 WINDOWS: return self.skip('test_freetype uses a ./configure script to build and therefore currently only runs on Linux and OS X.')
|
|
assert 'asm2g' in test_modes
|
|
if self.run_name == 'asm2g':
|
|
Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS # flip for some more coverage here
|
|
|
|
post = '''
|
|
def process(filename):
|
|
import tools.shared as shared
|
|
# Embed the font into the document
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
"FS.createDataFile('/', 'font.ttf', %s, true, false);" % str(
|
|
map(ord, open(shared.path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read())
|
|
)
|
|
)
|
|
open(filename, 'w').write(src)
|
|
'''
|
|
|
|
# Not needed for js, but useful for debugging
|
|
shutil.copyfile(path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), os.path.join(self.get_dir(), 'font.ttf'))
|
|
|
|
# Main
|
|
for outlining in [0, 5000]:
|
|
Settings.OUTLINING_LIMIT = outlining
|
|
print >> sys.stderr, 'outlining:', outlining
|
|
self.do_run(open(path_from_root('tests', 'freetype', 'main.c'), 'r').read(),
|
|
open(path_from_root('tests', 'freetype', 'ref.txt'), 'r').read(),
|
|
['font.ttf', 'test!', '150', '120', '25'],
|
|
libraries=self.get_freetype(),
|
|
includes=[path_from_root('tests', 'freetype', 'include')],
|
|
post_build=post)
|
|
|
|
# github issue 324
|
|
print '[issue 324]'
|
|
self.do_run(open(path_from_root('tests', 'freetype', 'main_2.c'), 'r').read(),
|
|
open(path_from_root('tests', 'freetype', 'ref_2.txt'), 'r').read(),
|
|
['font.ttf', 'w', '32', '32', '25'],
|
|
libraries=self.get_freetype(),
|
|
includes=[path_from_root('tests', 'freetype', 'include')],
|
|
post_build=post)
|
|
|
|
print '[issue 324 case 2]'
|
|
self.do_run(open(path_from_root('tests', 'freetype', 'main_3.c'), 'r').read(),
|
|
open(path_from_root('tests', 'freetype', 'ref_3.txt'), 'r').read(),
|
|
['font.ttf', 'W', '32', '32', '0'],
|
|
libraries=self.get_freetype(),
|
|
includes=[path_from_root('tests', 'freetype', 'include')],
|
|
post_build=post)
|
|
|
|
print '[issue 324 case 3]'
|
|
self.do_run('',
|
|
open(path_from_root('tests', 'freetype', 'ref_4.txt'), 'r').read(),
|
|
['font.ttf', 'ea', '40', '32', '0'],
|
|
no_build=True)
|
|
|
|
def test_sqlite(self):
|
|
# gcc -O3 -I/home/alon/Dev/emscripten/tests/sqlite -ldl src.c
|
|
self.banned_js_engines = [NODE_JS] # OOM in older node
|
|
if '-O' not in str(self.emcc_args):
|
|
self.banned_js_engines += [SPIDERMONKEY_ENGINE] # SM bug 1066759
|
|
|
|
Settings.DISABLE_EXCEPTION_CATCHING = 1
|
|
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
|
|
|
|
#Settings.OUTLINING_LIMIT = 60000
|
|
|
|
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 '-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
|
|
|
|
assert 'asm2g' in test_modes
|
|
if self.run_name == 'asm2g':
|
|
self.emcc_args += ['-g4'] # more source maps coverage
|
|
|
|
use_cmake_configure = WINDOWS
|
|
if use_cmake_configure:
|
|
make_args = []
|
|
configure = [PYTHON, path_from_root('emcmake'), 'cmake', '.', '-DBUILD_SHARED_LIBS=OFF']
|
|
else:
|
|
make_args = ['libz.a']
|
|
configure = ['sh', './configure']
|
|
|
|
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=make_args, configure=configure),
|
|
includes=[path_from_root('tests', 'zlib'), os.path.join(self.get_dir(), 'building', 'zlib')],
|
|
force_c=True)
|
|
|
|
def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long
|
|
Settings.DEAD_FUNCTIONS = ['__ZSt9terminatev']
|
|
|
|
asserts = Settings.ASSERTIONS
|
|
|
|
for use_cmake in [False, True]: # If false, use a configure script to configure Bullet build.
|
|
print 'cmake', use_cmake
|
|
# Windows cannot run configure sh scripts.
|
|
if WINDOWS and not use_cmake:
|
|
continue
|
|
|
|
Settings.ASSERTIONS = 2 if use_cmake else asserts # extra testing for ASSERTIONS == 2
|
|
|
|
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(),
|
|
open(path_from_root('tests', 'bullet', 'output4.txt'), 'r').read()],
|
|
libraries=get_bullet_library(self, use_cmake),
|
|
includes=[path_from_root('tests', 'bullet', 'src')])
|
|
test()
|
|
|
|
# TODO: test only worked in non-fastcomp (well, this section)
|
|
continue
|
|
assert 'asm2g' in test_modes
|
|
if self.run_name == 'asm2g' and not use_cmake:
|
|
# 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 WINDOWS: return self.skip('test_poppler depends on freetype, which uses a ./configure script to build and therefore currently only runs on Linux and OS X.')
|
|
Settings.NO_EXIT_RUNTIME = 1
|
|
|
|
Building.COMPILER_TEST_OPTS += [
|
|
'-I' + path_from_root('tests', 'freetype', 'include'),
|
|
'-I' + path_from_root('tests', 'poppler', 'include'),
|
|
'-Wno-warn-absolute-paths'
|
|
]
|
|
|
|
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(MEMFS.getFileDataAsRegularArray(FS.root.contents['filename-1.ppm']).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):
|
|
Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default
|
|
|
|
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(MEMFS.getFileDataAsRegularArray(FS.analyzePath('image.raw').object)));"
|
|
)
|
|
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
|
|
|
|
Settings.NO_EXIT_RUNTIME = 1
|
|
|
|
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
|
|
assert 'asm2g' in test_modes
|
|
if self.run_name == 'asm2g':
|
|
shutil.copyfile('src.c.o.js', 'release.js')
|
|
try:
|
|
os.environ['EMCC_DEBUG'] = '1'
|
|
print '2'
|
|
do_test()
|
|
shutil.copyfile('src.c.o.js', 'debug1.js')
|
|
os.environ['EMCC_DEBUG'] = '2'
|
|
print '3'
|
|
do_test()
|
|
shutil.copyfile('src.c.o.js', 'debug2.js')
|
|
finally:
|
|
del os.environ['EMCC_DEBUG']
|
|
for debug in [1,2]:
|
|
def clean(text):
|
|
text = text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\n}', '{}')
|
|
return '\n'.join(sorted(text.split('\n')))
|
|
sizes = len(open('release.js').read()), len(open('debug%d.js' % debug).read())
|
|
print >> sys.stderr, debug, 'sizes', sizes
|
|
assert abs(sizes[0] - sizes[1]) < 0.001*sizes[0], sizes # we can't check on identical output, compilation is not 100% deterministic (order of switch elements, etc.), but size should be ~identical
|
|
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'
|
|
|
|
if Settings.ALLOW_MEMORY_GROWTH == 1: # extra testing
|
|
print >> sys.stderr, 'no memory growth'
|
|
Settings.ALLOW_MEMORY_GROWTH = 0
|
|
do_test()
|
|
|
|
def test_python(self):
|
|
Settings.EMULATE_FUNCTION_POINTER_CASTS = 1
|
|
|
|
bitcode = path_from_root('tests', 'python', 'python.bc')
|
|
pyscript = dedent('''\
|
|
print '***'
|
|
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
|
|
print {1: 2}.keys()
|
|
print '***'
|
|
''')
|
|
pyoutput = '***\nhello python world!\n[0, 2, 4, 6]\n5\n22\n5.470000\n[1]\n***'
|
|
|
|
for lto in [0, 1]:
|
|
print 'lto:', lto
|
|
if lto == 1: self.emcc_args += ['--llvm-lto', '1']
|
|
self.do_ll_run(bitcode, pyoutput, args=['-S', '-c', pyscript])
|
|
|
|
def test_lifetime(self):
|
|
self.do_ll_run(path_from_root('tests', 'lifetime.ll'), 'hello, world!\n')
|
|
if '-O1' in self.emcc_args or '-O2' in self.emcc_args:
|
|
assert 'a18' not in open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read(), 'lifetime stuff and their vars must be culled'
|
|
|
|
# Test cases in separate files. Note that these files may contain invalid .ll!
|
|
# They are only valid enough for us to read for test purposes, not for llvm-as
|
|
# to process.
|
|
def test_cases(self):
|
|
if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly")
|
|
|
|
emcc_args = self.emcc_args
|
|
|
|
# The following tests link to libc, and must be run with EMCC_LEAVE_INPUTS_RAW = 0
|
|
need_no_leave_inputs_raw = ['muli33_ta2', 'philoop_ta2', 'uadd_overflow_64_ta2', 'i64toi8star', 'legalizer_ta2', 'inttoptr', 'quotedlabel', 'alignedunaligned', 'sillybitcast', 'invokeundef', 'loadbitcastgep', 'sillybitcast2', 'legalizer_b_ta2', 'emptystruct', 'longjmp_tiny', 'longjmp_tiny_phi', 'longjmp_tiny_phi2', 'longjmp_tiny_keepem', 'longjmp_tiny_keepem_cond', 'longjmp_tiny_invoke', 'longjmp_tiny_invoke_phi', 'entry3']
|
|
|
|
try:
|
|
import random
|
|
names = glob.glob(path_from_root('tests', 'cases', '*.ll'))
|
|
#random.shuffle(names)
|
|
for name in names:
|
|
shortname = name.replace('.ll', '')
|
|
if '' not in shortname: continue
|
|
# TODO: test only worked in non-fastcomp (well, these cases)
|
|
if os.path.basename(shortname) in [
|
|
'aliasbitcast', 'structparam', 'issue_39', 'phinonexist', 'oob_ta2', 'phiself', 'invokebitcast', # invalid ir
|
|
'structphiparam', 'callwithstructural_ta2', 'callwithstructural64_ta2', 'structinparam', # pnacl limitations in ExpandStructRegs
|
|
'2xi40', # pnacl limitations in ExpandGetElementPtr
|
|
'quoted', # current fastcomp limitations FIXME
|
|
'atomicrmw_unaligned', # TODO XXX
|
|
]: continue
|
|
if self.is_emterpreter() and os.path.basename(shortname) in ['funcptr']: continue # test writes to memory we store out bytecode! test is invalid
|
|
|
|
if os.path.basename(shortname) in need_no_leave_inputs_raw:
|
|
if 'EMCC_LEAVE_INPUTS_RAW' in os.environ: del os.environ['EMCC_LEAVE_INPUTS_RAW']
|
|
Settings.NO_FILESYSTEM = 0
|
|
else:
|
|
os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1'
|
|
Settings.NO_FILESYSTEM = 1 # no libc is linked in; with NO_FILESYSTEM we have a chance at printfing anyhow
|
|
|
|
if '_noasm' in shortname and Settings.ASM_JS:
|
|
print self.skip('case "%s" not relevant for asm.js' % shortname)
|
|
continue
|
|
self.emcc_args = emcc_args
|
|
if os.path.exists(shortname + '.emcc'):
|
|
if not self.emcc_args: continue
|
|
self.emcc_args = self.emcc_args + json.loads(open(shortname + '.emcc').read())
|
|
print >> sys.stderr, "Testing case '%s'..." % shortname
|
|
output_file = path_from_root('tests', 'cases', shortname + '.txt')
|
|
if 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:
|
|
if 'EMCC_LEAVE_INPUTS_RAW' in os.environ: del os.environ['EMCC_LEAVE_INPUTS_RAW']
|
|
self.emcc_args = emcc_args
|
|
|
|
def test_fuzz(self):
|
|
Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('tests', 'fuzz', 'include'), '-Wno-warn-absolute-paths']
|
|
|
|
def run_all(x):
|
|
print x
|
|
for name in glob.glob(path_from_root('tests', 'fuzz', '*.c')) + glob.glob(path_from_root('tests', 'fuzz', '*.cpp')):
|
|
#if os.path.basename(name) != '4.c': continue
|
|
if 'newfail' in name: continue
|
|
if os.path.basename(name).startswith('temp_fuzzcode'): continue
|
|
if x == 'lto' and self.run_name in ['default', 'asm2f'] and os.path.basename(name) in [
|
|
'8.c' # pnacl legalization issue, see https://code.google.com/p/nativeclient/issues/detail?id=4027
|
|
]: continue
|
|
if x == 'lto' and self.run_name == 'default' and os.path.basename(name) in [
|
|
'19.c', '18.cpp', # LLVM LTO bug
|
|
'23.cpp', # puts exists before LTO, but is not used; LTO cleans it out, but then creates uses to it (printf=>puts) XXX https://llvm.org/bugs/show_bug.cgi?id=23814
|
|
]: continue
|
|
if x == 'lto' and os.path.basename(name) in [
|
|
'21.c'
|
|
]: continue # LLVM LTO bug
|
|
|
|
print name
|
|
self.do_run(open(path_from_root('tests', 'fuzz', name)).read(),
|
|
open(path_from_root('tests', 'fuzz', name + '.txt')).read(), force_c=name.endswith('.c'))
|
|
|
|
run_all('normal')
|
|
|
|
self.emcc_args += ['--llvm-lto', '1']
|
|
|
|
run_all('lto')
|
|
|
|
# Autodebug the code
|
|
def do_autodebug(self, filename):
|
|
Building.llvm_dis(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
|
|
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, build_ll_hook=lambda x: False) # add an ll hook, to force ll generation
|
|
|
|
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)
|
|
|
|
### Integration tests
|
|
|
|
def test_ccall(self):
|
|
post = '''
|
|
def process(filename):
|
|
src = \'\'\'
|
|
var Module = { 'noInitialRun': true };
|
|
\'\'\' + open(filename, 'r').read() + \'\'\'
|
|
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 noThirdParam = Module['cwrap']('get_int', 'number');
|
|
Module.print(noThirdParam());
|
|
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 and check it's restored correctly
|
|
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.');
|
|
ccall('call_ccall_again', null);
|
|
});
|
|
Module.callMain();
|
|
\'\'\'
|
|
open(filename, 'w').write(src)
|
|
'''
|
|
|
|
Settings.EXPORTED_FUNCTIONS += ['_get_int', '_get_float', '_get_string', '_print_int', '_print_float', '_print_string', '_multi', '_pointer', '_call_ccall_again', '_malloc']
|
|
test_path = path_from_root('tests', 'core', 'test_ccall')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output, post_build=post)
|
|
|
|
if '-O2' in self.emcc_args or self.is_emterpreter():
|
|
print 'with closure'
|
|
self.emcc_args += ['--closure', '1']
|
|
self.do_run_from_file(src, output, post_build=post)
|
|
|
|
def test_dead_functions(self):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
extern "C" {
|
|
__attribute__((noinline)) int unused(int x) {
|
|
return x;
|
|
}
|
|
}
|
|
int main(int argc, char **argv) {
|
|
printf("*%d*\n", argc > 1 ? unused(1) : 2);
|
|
return 0;
|
|
}
|
|
'''
|
|
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('*1*', ['x'])
|
|
test('*2*', no_build=True)
|
|
if self.run_name in ['default', 'asm1', 'asm2g']: assert 'function _unused($' in js
|
|
|
|
# Kill off the dead function, and check a code path using it aborts
|
|
Settings.DEAD_FUNCTIONS = ['_unused']
|
|
test('*2*')
|
|
test('abort(-1) at', args=['x'], no_build=True)
|
|
|
|
# Kill off a library function, check code aborts
|
|
Settings.DEAD_FUNCTIONS = ['_printf']
|
|
test('abort(-1) at')
|
|
test('abort(-1) at', args=['x'], no_build=True)
|
|
|
|
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='[\"_free\",\"_unused\"]'")
|
|
Settings.PGO = 0
|
|
|
|
# Kill off the dead function, still works and it is not emitted
|
|
Settings.DEAD_FUNCTIONS = ['_unused']
|
|
js = test('*9*')
|
|
assert 'function _unused($' not in js # no compiled code
|
|
assert 'function _unused(' in js # lib-generated stub
|
|
Settings.DEAD_FUNCTIONS = []
|
|
|
|
# Run the same code with argc that uses the dead function, see abort
|
|
test(('dead function: unused'), args=['a', 'b'], no_build=True)
|
|
|
|
# Normal stuff
|
|
run_all('normal', r'''
|
|
#include <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;
|
|
}
|
|
''')
|
|
|
|
# TODO: test only worked in non-fastcomp
|
|
def test_asm_pgo(self):
|
|
return self.skip('non-fastcomp is deprecated and fails in 3.5')
|
|
|
|
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):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <emscripten.h>
|
|
|
|
extern "C" {
|
|
int other_function() { return 5; }
|
|
}
|
|
|
|
int main() {
|
|
int x = EM_ASM_INT_V({ return Module._other_function() });
|
|
emscripten_run_script_string(""); // Add a reference to a symbol that exists in src/deps_info.json to uncover issue #2836 in the test suite.
|
|
printf("waka %d!\n", x);
|
|
return 0;
|
|
}
|
|
'''
|
|
open('exps', 'w').write('["_main","_other_function"]')
|
|
|
|
self.emcc_args += ['-s', 'EXPORTED_FUNCTIONS=@exps']
|
|
self.do_run(src, '''waka 5!''')
|
|
assert 'other_function' in open('src.cpp.o.js').read()
|
|
|
|
def test_large_exported_response(self):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <emscripten.h>
|
|
|
|
extern "C" {
|
|
'''
|
|
|
|
js_funcs = []
|
|
num_exports = 5000
|
|
count = 0
|
|
while count < num_exports:
|
|
src += 'int exported_func_from_response_file_%d () { return %d;}\n' % (count, count)
|
|
js_funcs.append('_exported_func_from_response_file_%d' % count)
|
|
count += 1
|
|
|
|
src += r'''
|
|
}
|
|
|
|
int main() {
|
|
int x = EM_ASM_INT_V({ return Module._exported_func_from_response_file_4999() });
|
|
emscripten_run_script_string(""); // Add a reference to a symbol that exists in src/deps_info.json to uncover issue #2836 in the test suite.
|
|
printf("waka %d!\n", x);
|
|
return 0;
|
|
}
|
|
'''
|
|
|
|
js_funcs.append('_main')
|
|
exported_func_json_file = os.path.join(self.get_dir(), 'large_exported_response.json')
|
|
open(exported_func_json_file, 'wb').write(json.dumps(js_funcs))
|
|
|
|
self.emcc_args += ['-s', 'EXPORTED_FUNCTIONS=@' + exported_func_json_file]
|
|
self.do_run(src, '''waka 4999!''')
|
|
assert '_exported_func_from_response_file_1' in open('src.cpp.o.js').read()
|
|
|
|
def test_add_function(self):
|
|
Settings.INVOKE_RUN = 0
|
|
Settings.RESERVED_FUNCTION_POINTERS = 1
|
|
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <emscripten.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);
|
|
EM_ASM_(Module['Runtime']['removeFunction']($0), f);
|
|
printf("ok\n");
|
|
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()]);
|
|
''')
|
|
|
|
expected = '''Hello 7 from JS!\nok\n'''
|
|
self.emcc_args += ['--post-js', 'post.js']
|
|
self.do_run(src, expected)
|
|
|
|
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, expected)
|
|
|
|
assert 'asm2' in test_modes
|
|
if self.run_name == 'asm2':
|
|
print 'closure'
|
|
self.banned_js_engines = [NODE_JS] # weird global handling in node
|
|
self.emcc_args += ['--closure', '1']
|
|
self.do_run(src, expected)
|
|
|
|
print 'function pointer emulation'
|
|
Settings.RESERVED_FUNCTION_POINTERS = 0
|
|
Settings.EMULATED_FUNCTION_POINTERS = 1 # with emulation, we don't need to reserve
|
|
self.do_run(src, expected)
|
|
|
|
def test_getFuncWrapper_sig_alias(self):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <emscripten.h>
|
|
|
|
void func1(int a) {
|
|
printf("func1\n");
|
|
}
|
|
void func2(int a, int b) {
|
|
printf("func2\n");
|
|
}
|
|
|
|
int main() {
|
|
EM_ASM_INT({
|
|
Runtime.getFuncWrapper($0, 'vi')(0);
|
|
Runtime.getFuncWrapper($1, 'vii')(0, 0);
|
|
}, func1, func2);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, 'func1\nfunc2\n')
|
|
|
|
def test_emulate_function_pointer_casts(self):
|
|
Settings.EMULATE_FUNCTION_POINTER_CASTS = 1
|
|
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
|
|
typedef double (*ddd)(double x, double unused);
|
|
typedef int (*iii)(int x, int unused);
|
|
|
|
int main() {
|
|
volatile ddd d = (ddd)acos;
|
|
volatile iii i = (iii)acos;
|
|
printf("|%.3f,%d|\n", d(0.3, 0.6), i(0, 0));
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '|1.266,1|\n')
|
|
|
|
def test_demangle_stacks(self):
|
|
if Settings.ASM_JS: return self.skip('spidermonkey has stack trace issues')
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_demangle_stacks')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_tracing(self):
|
|
Building.COMPILER_TEST_OPTS += ['--tracing']
|
|
|
|
test_path = path_from_root('tests', 'core', 'test_tracing')
|
|
src, output = (test_path + s for s in ('.in', '.out'))
|
|
|
|
self.do_run_from_file(src, output)
|
|
|
|
def test_embind(self):
|
|
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):
|
|
Settings.NO_EXIT_RUNTIME = 1 # we emit some post.js that we need to see
|
|
Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js']
|
|
open('post.js', 'w').write('''
|
|
Module.print('lerp ' + Module.lerp(100, 200, 66) + '.');
|
|
''')
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <emscripten/bind.h>
|
|
using namespace emscripten;
|
|
int lerp(int a, int b, int t) {
|
|
return (100 - t) * a + t * b;
|
|
}
|
|
EMSCRIPTEN_BINDINGS(my_module) {
|
|
function("lerp", &lerp);
|
|
}
|
|
int main(int argc, char **argv) {
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, 'lerp 166');
|
|
|
|
def test_scriptaclass(self):
|
|
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 '-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
|
|
|
|
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):
|
|
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_webidl(self):
|
|
assert 'asm2' in test_modes
|
|
if self.run_name == 'asm2':
|
|
self.emcc_args += ['--closure', '1', '-g1'] # extra testing
|
|
Settings.MODULARIZE = 1 # avoid closure minified names competing with our test code in the global name space
|
|
|
|
def do_test_in_mode(mode):
|
|
print 'testing mode', mode
|
|
# Force IDL checks mode
|
|
os.environ['IDL_CHECKS'] = mode
|
|
|
|
output = Popen([PYTHON, path_from_root('tools', 'webidl_binder.py'),
|
|
path_from_root('tests', 'webidl', 'test.idl'),
|
|
'glue']).communicate()[0]
|
|
assert os.path.exists('glue.cpp')
|
|
assert os.path.exists('glue.js')
|
|
|
|
# Export things on "TheModule". This matches the typical use pattern of the bound library
|
|
# being used as Box2D.* or Ammo.*, and we cannot rely on "Module" being always present (closure may remove it).
|
|
open('export.js', 'w').write('''
|
|
// test purposes: remove printErr output, whose order is unpredictable when compared to print
|
|
Module.printErr = Module['printErr'] = function(){};
|
|
''')
|
|
self.emcc_args += ['--post-js', 'glue.js', '--post-js', 'export.js']
|
|
shutil.copyfile(path_from_root('tests', 'webidl', 'test.h'), self.in_dir('test.h'))
|
|
shutil.copyfile(path_from_root('tests', 'webidl', 'test.cpp'), self.in_dir('test.cpp'))
|
|
src = open('test.cpp').read()
|
|
def post(filename):
|
|
src = open(filename, 'a')
|
|
src.write('\n\n')
|
|
if self.run_name == 'asm2':
|
|
src.write('var TheModule = Module();\n')
|
|
else:
|
|
src.write('var TheModule = Module;\n')
|
|
src.write('\n\n')
|
|
src.write(open(path_from_root('tests', 'webidl', 'post.js')).read())
|
|
src.write('\n\n')
|
|
src.close()
|
|
self.do_run(src, open(path_from_root('tests', 'webidl', "output_%s.txt" % mode)).read(), post_build=(None, post),
|
|
output_nicerizer=(lambda out, err: out))
|
|
|
|
do_test_in_mode('ALL')
|
|
do_test_in_mode('FAST')
|
|
do_test_in_mode('DEFAULT')
|
|
|
|
### Tests for tools
|
|
|
|
def test_safe_heap(self):
|
|
if not Settings.SAFE_HEAP: return self.skip('We need SAFE_HEAP to test SAFE_HEAP')
|
|
# TODO: Should we remove this test?
|
|
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*', assert_returncode=None)
|
|
except Exception, e:
|
|
# This test *should* fail, by throwing this exception
|
|
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
|
|
|
|
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*', assert_returncode=None)
|
|
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_source_map(self):
|
|
if self.is_emterpreter(): return self.skip('todo')
|
|
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)
|
|
# 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.
|
|
map_filename = out_filename + '.map'
|
|
data = json.load(open(map_filename, 'r'))
|
|
self.assertPathsIdentical(out_filename, data['file'])
|
|
assert len(data['sources']) == 1, data['sources']
|
|
self.assertPathsIdentical(src_filename, data['sources'][0])
|
|
self.assertTextDataIdentical(src, data['sourcesContent'][0])
|
|
mappings = json.loads(jsrun.run_js(
|
|
path_from_root('tools', 'source-maps', 'sourcemap2json.js'),
|
|
tools.shared.NODE_JS, [map_filename]))
|
|
seen_lines = set()
|
|
for m in mappings:
|
|
self.assertPathsIdentical(src_filename, m['source'])
|
|
seen_lines.add(m['originalLine'])
|
|
# ensure that all the 'meaningful' lines in the original code get mapped
|
|
assert seen_lines.issuperset([6, 7, 11, 12])
|
|
|
|
build_and_check()
|
|
|
|
assert 'asm2g' in test_modes
|
|
if self.run_name == 'asm2g':
|
|
# 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:
|
|
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 self.is_emterpreter(): return self.skip('todo')
|
|
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
|
|
}
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
int main() {
|
|
std::string x = "ok"; // add libc++ stuff to make this big, test for #2410
|
|
int i;
|
|
scanf("%d", &i);
|
|
foo(i);
|
|
std::cout << x << std::endl;
|
|
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']-1]: # -1 to fix 0-start vs 1-start
|
|
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_emscripten_log(self):
|
|
if self.is_emterpreter():
|
|
self.emcc_args += ['--profiling-funcs'] # without this, stack traces are not useful (we jump emterpret=>emterpret)
|
|
Building.COMPILER_TEST_OPTS += ['-DEMTERPRETER'] # even so, we get extra emterpret() calls on the stack
|
|
if Settings.ASM_JS:
|
|
# XXX Does not work in SpiderMonkey since callstacks cannot be captured when running in asm.js, see https://bugzilla.mozilla.org/show_bug.cgi?id=947996
|
|
self.banned_js_engines = [SPIDERMONKEY_ENGINE]
|
|
if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g')
|
|
Building.COMPILER_TEST_OPTS += ['-DRUN_FROM_JS_SHELL']
|
|
self.do_run(open(path_from_root('tests', 'emscripten_log', 'emscripten_log.cpp')).read(), '''test print 123
|
|
|
|
12.345679 9.123457 1.353180
|
|
|
|
12345678 9123456 1353179
|
|
|
|
12.345679 9123456 1353179
|
|
|
|
12345678 9.123457 1353179
|
|
|
|
12345678 9123456 1.353180
|
|
|
|
12345678 9.123457 1.353180
|
|
|
|
12.345679 9123456 1.353180
|
|
|
|
12.345679 9.123457 1353179
|
|
|
|
Success!
|
|
''')
|
|
|
|
def test_float_literals(self):
|
|
self.do_run_from_file(path_from_root('tests', 'test_float_literals.cpp'), path_from_root('tests', 'test_float_literals.out'))
|
|
|
|
def test_exit_status(self):
|
|
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!\ncleanup\nI see exit status: 118')
|
|
|
|
def test_noexitruntime(self):
|
|
src = r'''
|
|
#include <emscripten.h>
|
|
#include <stdio.h>
|
|
static int testPre = TEST_PRE;
|
|
struct Global {
|
|
Global() {
|
|
printf("in Global()\n");
|
|
if (testPre) { EM_ASM(Module['noExitRuntime'] = true;); }
|
|
}
|
|
~Global() { printf("ERROR: in ~Global()\n"); }
|
|
} global;
|
|
int main() {
|
|
if (!testPre) { EM_ASM(Module['noExitRuntime'] = true;); }
|
|
printf("in main()\n");
|
|
}
|
|
'''
|
|
self.do_run(src.replace('TEST_PRE', '0'), 'in Global()\nin main()')
|
|
self.do_run(src.replace('TEST_PRE', '1'), 'in Global()\nin main()')
|
|
|
|
def test_minmax(self):
|
|
self.do_run(open(path_from_root('tests', 'test_minmax.c')).read(), 'NAN != NAN\nSuccess!')
|
|
|
|
def test_locale(self):
|
|
self.do_run_from_file(path_from_root('tests', 'test_locale.c'), path_from_root('tests', 'test_locale.out'))
|
|
|
|
def test_async(self):
|
|
self.banned_js_engines = [SPIDERMONKEY_ENGINE, V8_ENGINE] # needs setTimeout which only node has
|
|
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <emscripten.h>
|
|
void f(void *p) {
|
|
*(int*)p = 99;
|
|
printf("!");
|
|
}
|
|
int main() {
|
|
int i = 0;
|
|
printf("Hello");
|
|
emscripten_async_call(f, &i, 1);
|
|
printf("World");
|
|
emscripten_%s(100);
|
|
printf("%%d\n", i);
|
|
}
|
|
''' % ('sleep_with_yield' if self.is_emterpreter() else 'sleep')
|
|
|
|
if not self.is_emterpreter():
|
|
Settings.ASYNCIFY = 1
|
|
else:
|
|
Settings.EMTERPRETIFY_ASYNC = 1
|
|
|
|
self.do_run(src, 'HelloWorld!99');
|
|
|
|
if self.is_emterpreter():
|
|
print 'check bad ccall use'
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <emscripten.h>
|
|
int main() {
|
|
printf("Hello");
|
|
emscripten_sleep(100);
|
|
printf("World\n");
|
|
}
|
|
'''
|
|
Settings.ASSERTIONS = 1
|
|
Settings.INVOKE_RUN = 0
|
|
open('post.js', 'w').write('''
|
|
try {
|
|
Module['ccall']('main', 'number', ['number', 'string'], [2, 'waka']);
|
|
var never = true;
|
|
} catch(e) {
|
|
Module.print(e);
|
|
assert(!never);
|
|
}
|
|
''')
|
|
self.emcc_args += ['--post-js', 'post.js']
|
|
self.do_run(src, 'cannot start async op with normal JS');
|
|
|
|
print 'check reasonable ccall use'
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <emscripten.h>
|
|
int main() {
|
|
printf("Hello");
|
|
emscripten_sleep(100);
|
|
printf("World\n");
|
|
}
|
|
'''
|
|
open('post.js', 'w').write('''
|
|
Module['ccall']('main', null, ['number', 'string'], [2, 'waka'], { async: true });
|
|
''')
|
|
self.do_run(src, 'HelloWorld');
|
|
|
|
def test_async_returnvalue(self):
|
|
if not self.is_emterpreter(): return self.skip('emterpreter-only test')
|
|
|
|
Settings.EMTERPRETIFY_ASYNC = 1
|
|
self.banned_js_engines = [SPIDERMONKEY_ENGINE, V8_ENGINE] # needs setTimeout which only node has
|
|
|
|
open('lib.js', 'w').write(r'''
|
|
mergeInto(LibraryManager.library, {
|
|
sleep_with_return__deps: ['$EmterpreterAsync'],
|
|
sleep_with_return: function(ms) {
|
|
return EmterpreterAsync.handle(function(resume) {
|
|
var startTime = Date.now();
|
|
setTimeout(function() {
|
|
if (ABORT) return; // do this manually; we can't call into Browser.safeSetTimeout, because that is paused/resumed!
|
|
resume(function() {
|
|
return Date.now() - startTime;
|
|
});
|
|
}, ms);
|
|
});
|
|
}
|
|
});
|
|
''')
|
|
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <emscripten.h>
|
|
|
|
extern "C" {
|
|
extern int sleep_with_return(int ms);
|
|
}
|
|
|
|
int main() {
|
|
int ms = sleep_with_return(1000);
|
|
assert(ms >= 900);
|
|
printf("napped for %d ms\n", ms);
|
|
}
|
|
'''
|
|
self.emcc_args += ['--js-library', 'lib.js']
|
|
|
|
self.do_run(src, 'napped');
|
|
|
|
def test_async_exit(self):
|
|
if not self.is_emterpreter(): return self.skip('emterpreter-only test')
|
|
|
|
Settings.EMTERPRETIFY_ASYNC = 1
|
|
self.banned_js_engines = [SPIDERMONKEY_ENGINE, V8_ENGINE] # needs setTimeout which only node has
|
|
|
|
self.do_run(r'''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <emscripten.h>
|
|
|
|
void f()
|
|
{
|
|
printf("f\n");
|
|
emscripten_sleep(1);
|
|
printf("hello\n");
|
|
static int i = 0;
|
|
i++;
|
|
if(i == 5) {
|
|
printf("exit\n");
|
|
exit(0);
|
|
printf("world\n");
|
|
i = 0;
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
while(1) {
|
|
f();
|
|
}
|
|
return 0;
|
|
}
|
|
''', 'f\nhello\nf\nhello\nf\nhello\nf\nhello\nf\nhello\nexit\n')
|
|
|
|
def test_coroutine(self):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <emscripten.h>
|
|
void fib(void * arg) {
|
|
int * p = (int*)arg;
|
|
int cur = 1;
|
|
int next = 1;
|
|
for(int i = 0; i < 9; ++i) {
|
|
*p = cur;
|
|
emscripten_yield();
|
|
int next2 = cur + next;
|
|
cur = next;
|
|
next = next2;
|
|
}
|
|
}
|
|
void f(void * arg) {
|
|
int * p = (int*)arg;
|
|
*p = 0;
|
|
emscripten_yield();
|
|
fib(arg); // emscripten_yield in fib() can `pass through` f() back to main(), and then we can assume inside fib()
|
|
}
|
|
void g(void * arg) {
|
|
int * p = (int*)arg;
|
|
for(int i = 0; i < 10; ++i) {
|
|
*p = 100+i;
|
|
emscripten_yield();
|
|
}
|
|
}
|
|
int main(int argc, char **argv) {
|
|
int i;
|
|
emscripten_coroutine co = emscripten_coroutine_create(f, (void*)&i, 0);
|
|
emscripten_coroutine co2 = emscripten_coroutine_create(g, (void*)&i, 0);
|
|
printf("*");
|
|
while(emscripten_coroutine_next(co)) {
|
|
printf("%d-", i);
|
|
emscripten_coroutine_next(co2);
|
|
printf("%d-", i);
|
|
}
|
|
printf("*");
|
|
return 0;
|
|
}
|
|
'''
|
|
Settings.ASYNCIFY = 1;
|
|
self.do_run(src, '*0-100-1-101-1-102-2-103-3-104-5-105-8-106-13-107-21-108-34-109-*');
|
|
|
|
def test_cxx_self_assign(self):
|
|
# See https://github.com/kripken/emscripten/pull/2688 and http://llvm.org/bugs/show_bug.cgi?id=18735
|
|
open('src.cpp', 'w').write(r'''
|
|
#include <map>
|
|
#include <stdio.h>
|
|
|
|
int main() {
|
|
std::map<int, int> m;
|
|
m[0] = 1;
|
|
m = m;
|
|
// size should still be one after self assignment
|
|
if (m.size() == 1) {
|
|
printf("ok.\n");
|
|
}
|
|
}
|
|
''')
|
|
Popen([PYTHON, EMCC, 'src.cpp']).communicate()
|
|
self.assertContained('ok.', run_js('a.out.js', args=['C']))
|
|
|
|
def test_memprof_requirements(self):
|
|
# This test checks for the global variables required to run the memory
|
|
# profiler. It would fail if these variables were made no longer global
|
|
# or if their identifiers were changed.
|
|
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
|
|
extern "C" {
|
|
void check_memprof_requirements();
|
|
}
|
|
int main() {
|
|
check_memprof_requirements();
|
|
return 0;
|
|
}
|
|
''')
|
|
open(os.path.join(self.get_dir(), 'lib.js'), 'w').write('''
|
|
mergeInto(LibraryManager.library, {
|
|
check_memprof_requirements: function() {
|
|
if (typeof TOTAL_MEMORY === 'number' &&
|
|
typeof STATIC_BASE === 'number' &&
|
|
typeof STATICTOP === 'number' &&
|
|
typeof STACK_BASE === 'number' &&
|
|
typeof STACK_MAX === 'number' &&
|
|
typeof STACKTOP === 'number' &&
|
|
typeof DYNAMIC_BASE === 'number' &&
|
|
typeof DYNAMICTOP === 'number') {
|
|
Module.print('able to run memprof');
|
|
} else {
|
|
Module.print('missing the required variables to run memprof');
|
|
}
|
|
}
|
|
});
|
|
''')
|
|
self.emcc_args += ['--js-library', os.path.join(self.get_dir(), 'lib.js')]
|
|
self.do_run(open(os.path.join(self.get_dir(), 'main.cpp'), 'r').read(), 'able to run memprof')
|
|
|
|
def test_fs_dict(self):
|
|
open(self.in_dir('pre.js'), 'w').write('''
|
|
var Module = {};
|
|
Module['preRun'] = function() {
|
|
console.log(typeof FS.filesystems['MEMFS']);
|
|
console.log(typeof FS.filesystems['IDBFS']);
|
|
console.log(typeof FS.filesystems['NODEFS']);
|
|
};
|
|
''')
|
|
self.emcc_args += ['--pre-js', 'pre.js']
|
|
self.do_run('', 'object\nobject\nobject')
|
|
|
|
# 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):
|
|
try:
|
|
super(TT, self).tearDown()
|
|
finally:
|
|
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
|
|
|
|
os.chdir(self.get_dir()) # Ensure the directory exists and go there
|
|
Building.COMPILER = compiler
|
|
|
|
assert emcc_args is not None
|
|
self.emcc_args = emcc_args[:]
|
|
Settings.load(self.emcc_args)
|
|
Building.LLVM_OPTS = 0
|
|
|
|
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
|
|
|
|
TT.setUp = setUp
|
|
|
|
return TT
|
|
|
|
# Main test modes
|
|
default = make_run("default", compiler=CLANG, emcc_args=["-s", "ASM_JS=2"])
|
|
asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"])
|
|
asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])
|
|
asm3 = make_run("asm3", compiler=CLANG, emcc_args=["-O3"])
|
|
asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-Oz", "-s", "PRECISE_F32=1", "-s", "ALLOW_MEMORY_GROWTH=1"])
|
|
asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "-s", "SAFE_HEAP=1"])
|
|
asm2i = make_run("asm2i", compiler=CLANG, emcc_args=["-O2", '-s', 'EMTERPRETIFY=1'])
|
|
#asm2m = make_run("asm2m", compiler=CLANG, emcc_args=["-O2", "--memory-init-file", "0", "-s", "MEM_INIT_METHOD=2", "-s", "ASSERTIONS=1"])
|
|
|
|
# Legacy test modes -
|
|
asm2nn = make_run("asm2nn", compiler=CLANG, emcc_args=["-O2"], env={"EMCC_NATIVE_OPTIMIZER": "0"})
|
|
|
|
del T # T is just a shape for the specific subclasses, we don't test it itself
|
|
|