emscripten/tests/runner.py

5896 строки
206 KiB
Python
Исходник Обычный вид История

2010-08-26 08:01:10 +04:00
'''
Simple test runner
See settings.py file for options&params. Edit as needed.
These tests can be run in parallel using nose, for example
nosetests --processes=4 -v -s tests/runner.py
will use 4 processes. To install nose do something like
|pip install nose| or |sudo apt-get install python-nose|.
2010-08-26 08:01:10 +04:00
'''
from subprocess import Popen, PIPE, STDOUT
import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, tempfile, re, difflib, webbrowser
2010-08-26 08:01:10 +04:00
# Setup
2010-08-26 08:01:10 +04:00
2011-10-05 21:50:13 +04:00
__rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
def path_from_root(*pathelems):
2011-10-05 21:50:13 +04:00
return os.path.join(__rootpath__, *pathelems)
exec(open(path_from_root('tools', 'shared.py'), 'r').read())
2010-08-26 08:01:10 +04:00
2011-12-21 09:22:05 +04:00
sys.path += [path_from_root('')]
# Sanity check for config
2010-09-10 07:03:24 +04:00
try:
2011-03-19 20:00:57 +03:00
assert COMPILER_OPTS != None
except:
raise Exception('Cannot find "COMPILER_OPTS" definition. Is ~/.emscripten set up properly? You may need to copy the template from settings.py into it.')
2010-08-26 08:01:10 +04:00
# Core test runner class, shared between normal tests and benchmarks
2010-10-10 03:54:23 +04:00
class RunnerCore(unittest.TestCase):
save_dir = os.environ.get('EM_SAVE_DIR')
2011-10-23 00:13:51 +04:00
save_JS = 0
stderr_redirect = STDOUT # This avoids cluttering the test runner output, which is stderr too, with compiler warnings etc.
# Change this to None to get stderr reporting, for debugging purposes
2011-10-23 00:13:51 +04:00
def setUp(self):
Settings.reset()
self.banned_js_engines = []
2011-10-23 00:13:51 +04:00
if not self.save_dir:
dirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR)
else:
dirname = EMSCRIPTEN_TEMP_DIR
if not os.path.exists(dirname):
os.makedirs(dirname)
self.working_dir = dirname
2011-11-20 01:37:26 +04:00
os.chdir(dirname)
def tearDown(self):
2011-10-23 00:13:51 +04:00
if self.save_JS:
for name in os.listdir(self.get_dir()):
2011-04-25 04:57:01 +04:00
if name.endswith(('.o.js', '.cc.js')):
suff = '.'.join(name.split('.')[-2:])
shutil.copy(os.path.join(self.get_dir(), name),
2011-04-25 04:57:01 +04:00
os.path.join(TEMP_DIR, self.id().replace('__main__.', '').replace('.test_', '.')+'.'+suff))
2011-10-23 00:13:51 +04:00
if not self.save_dir:
shutil.rmtree(self.get_dir())
def skip(self, why):
print >> sys.stderr, '<skipping: %s> ' % why,
2011-04-23 00:23:37 +04:00
2010-10-10 03:54:23 +04:00
def get_dir(self):
return self.working_dir
2010-10-10 03:54:23 +04:00
2011-11-17 22:16:42 +04:00
def get_stdout_path(self):
return os.path.join(self.get_dir(), 'stdout')
def prep_ll_run(self, filename, ll_file, force_recompile=False, build_ll_hook=None):
2011-04-25 04:57:01 +04:00
if ll_file.endswith(('.bc', '.o')):
if ll_file != filename + '.o':
shutil.copy(ll_file, filename + '.o')
2011-10-27 07:21:11 +04:00
Building.llvm_dis(filename)
2011-04-25 04:57:01 +04:00
else:
shutil.copy(ll_file, filename + '.o.ll')
#force_recompile = force_recompile or os.stat(filename + '.o.ll').st_size > 50000 # if the file is big, recompile just to get ll_opts # Recompiling just for dfe in ll_opts is too costly
2011-04-25 04:57:01 +04:00
2011-10-27 07:41:51 +04:00
if Building.LLVM_OPTS or force_recompile or build_ll_hook:
2011-10-27 07:21:11 +04:00
Building.ll_opts(filename)
2011-04-25 04:57:01 +04:00
if build_ll_hook:
need_post = build_ll_hook(filename)
Building.llvm_as(filename)
shutil.move(filename + '.o.ll', filename + '.o.ll.pre') # for comparisons later
2011-12-14 00:20:45 +04:00
if Building.LLVM_OPTS:
Building.llvm_opts(filename)
2011-10-27 07:21:11 +04:00
Building.llvm_dis(filename)
if build_ll_hook and need_post:
build_ll_hook(filename)
Building.llvm_as(filename)
shutil.move(filename + '.o.ll', filename + '.o.ll.post') # for comparisons later
Building.llvm_dis(filename)
2011-04-25 04:57:01 +04:00
# Generate JS from ll, and optionally modify the generated JS with a post_build function. Note
# that post_build is called on unoptimized JS, so we send it to emcc (otherwise, if run after
# emcc, it would not apply on the optimized/minified JS)
def ll_to_js(self, filename, extra_emscripten_args, post_build):
if self.emcc_args is None:
Building.emscripten(filename, append_ext=True, extra_args=extra_emscripten_args)
if post_build is not None:
exec post_build in locals()
shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js')
process(filename + '.o.js')
else:
2012-01-05 02:36:02 +04:00
transform_args = []
if post_build:
transform_filename = os.path.join(self.get_dir(), 'transform.py')
transform = open(transform_filename, 'w')
transform.write('''
import sys
sys.path += ['%s']
''' % path_from_root(''))
2012-01-05 02:36:02 +04:00
transform.write(post_build)
transform.write('''
process(sys.argv[1])
''')
transform.close()
transform_args = ['--js-transform', "python %s" % transform_filename]
Building.emcc(filename + '.o.ll', Settings.serialize() + self.emcc_args + transform_args, filename + '.o.js')
# Build JavaScript code from source code
def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[], post_build=None):
2010-10-10 03:54:23 +04:00
# Copy over necessary files for compiling the source
if main_file is None:
f = open(filename, 'w')
f.write(src)
f.close()
assert len(additional_files) == 0
2010-10-10 03:54:23 +04:00
else:
# copy whole directory, and use a specific main .cpp file
shutil.rmtree(dirname)
shutil.copytree(src, dirname)
2010-10-10 03:54:23 +04:00
shutil.move(os.path.join(dirname, main_file), filename)
# the additional files were copied; alter additional_files to point to their full paths now
additional_files = map(lambda f: os.path.join(dirname, f), additional_files)
2010-10-10 03:54:23 +04:00
# C++ => LLVM binary
os.chdir(dirname)
cwd = os.getcwd()
2011-09-04 21:52:59 +04:00
for f in [filename] + additional_files:
try:
# Make sure we notice if compilation steps failed
os.remove(f + '.o')
except:
pass
args = [Building.COMPILER, '-emit-llvm'] + COMPILER_OPTS + Building.COMPILER_TEST_OPTS + \
['-I', dirname, '-I', os.path.join(dirname, 'include')] + \
map(lambda include: '-I' + include, includes) + \
['-c', f, '-o', f + '.o']
output = Popen(args, stdout=PIPE, stderr=self.stderr_redirect).communicate()[0]
2011-02-28 03:55:53 +03:00
assert os.path.exists(f + '.o'), 'Source compilation error: ' + output
2010-10-10 03:54:23 +04:00
os.chdir(cwd)
# Link all files
2011-01-24 05:23:44 +03:00
if len(additional_files) + len(libraries) > 0:
shutil.move(filename + '.o', filename + '.o.alone')
2011-10-27 07:16:22 +04:00
Building.link([filename + '.o.alone'] + map(lambda f: f + '.o', additional_files) + libraries,
2011-10-25 02:20:01 +04:00
filename + '.o')
if not os.path.exists(filename + '.o'):
print "Failed to link LLVM binaries:\n\n", output
raise Exception("Linkage error");
# Finalize
self.prep_ll_run(filename, filename + '.o', build_ll_hook=build_ll_hook)
2011-03-03 18:39:33 +03:00
# BC => JS
self.ll_to_js(filename, extra_emscripten_args, post_build)
if output_processor is not None:
output_processor(open(filename + '.o.js').read())
2010-10-10 03:54:23 +04:00
def run_generated_code(self, engine, filename, args=[], check_timeout=True):
2011-02-28 03:54:21 +03:00
stdout = os.path.join(self.get_dir(), 'stdout') # use files, as PIPE can get too full and hang us
stderr = os.path.join(self.get_dir(), 'stderr')
try:
cwd = os.getcwd()
except:
cwd = None
os.chdir(self.get_dir())
run_js(filename, engine, args, check_timeout, stdout=open(stdout, 'w'), stderr=open(stderr, 'w'))
if cwd is not None:
os.chdir(cwd)
2011-02-28 03:54:21 +03:00
ret = open(stdout, 'r').read() + open(stderr, 'r').read()
assert 'strict warning:' not in ret, 'We should pass all strict mode checks: ' + ret
2011-02-21 06:03:14 +03:00
return ret
2010-10-10 03:54:23 +04:00
2011-03-03 06:12:13 +03:00
def run_llvm_interpreter(self, args):
return Popen([EXEC_LLVM] + args, stdout=PIPE, stderr=self.stderr_redirect).communicate()[0]
2011-03-03 06:12:13 +03:00
def build_native(self, filename):
2011-11-18 02:44:42 +04:00
Popen([CLANG, '-O2', filename, '-o', filename+'.native'], stdout=PIPE).communicate()[0]
def run_native(self, filename, args):
2011-11-18 02:44:42 +04:00
Popen([filename+'.native'] + args, stdout=PIPE).communicate()[0]
def assertIdentical(self, x, y):
if x != y:
raise Exception("Expected to have '%s' == '%s', diff:\n\n%s" % (
limit_size(x), limit_size(y),
limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')]))
))
2011-12-11 23:54:22 +04:00
def assertContained(self, values, string, additional_info=''):
if type(values) not in [list, tuple]: values = [values]
for value in values:
if type(string) is not str: string = string()
if value in string: return # success
2011-12-11 23:54:22 +04:00
raise Exception("Expected to find '%s' in '%s', diff:\n\n%s\n%s" % (
limit_size(values[0]), limit_size(string),
2011-12-11 23:54:22 +04:00
limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(values[0].split('\n'), string.split('\n'), fromfile='expected', tofile='actual')])),
additional_info
))
2010-10-15 10:07:23 +04:00
def assertNotContained(self, value, string):
if type(value) is not str: value = value() # lazy loading
if type(string) is not str: string = string()
2010-10-15 10:07:23 +04:00
if value in string:
raise Exception("Expected to NOT find '%s' in '%s', diff:\n\n%s" % (
limit_size(value), limit_size(string),
limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(value.split('\n'), string.split('\n'), fromfile='expected', tofile='actual')]))
))
2010-10-15 10:07:23 +04:00
2011-10-27 07:16:22 +04:00
library_cache = {}
def get_library(self, name, generated_libs, configure=['./configure'], configure_args=[], make=['make'], make_args=['-j', '2'], cache=True):
build_dir = self.get_build_dir()
output_dir = self.get_dir()
2011-10-27 07:41:51 +04:00
cache_name = name + '|' + Building.COMPILER
2011-10-27 07:16:22 +04:00
if self.library_cache is not None:
if cache and self.library_cache.get(cache_name):
print >> sys.stderr, '<load build from cache> ',
bc_file = os.path.join(output_dir, 'lib' + name + '.bc')
f = open(bc_file, 'wb')
f.write(self.library_cache[cache_name])
f.close()
return bc_file
print >> sys.stderr, '<building and saving into cache> ',
2011-10-27 07:41:51 +04:00
return Building.build_library(name, build_dir, output_dir, generated_libs, configure, configure_args, make, make_args, self.library_cache, cache_name,
copy_project=True)
2011-10-27 07:16:22 +04:00
2011-01-02 09:59:55 +03:00
###################################################################################################
sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv)
2011-12-18 23:34:19 +04:00
if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
2011-01-02 09:59:55 +03:00
# Tests
print "Running Emscripten tests..."
2010-10-10 03:54:23 +04:00
class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline
## Does a complete test - builds, runs, checks output, etc.
def do_run(self, src, expected_output=None, args=[], output_nicerizer=None, output_processor=None, no_build=False, main_file=None, additional_files=[], js_engines=None, post_build=None, basename='src.cpp', libraries=[], includes=[], force_c=False, build_ll_hook=None, extra_emscripten_args=[]):
2011-01-31 18:43:01 +03:00
if force_c or (main_file is not None and main_file[-2:]) == '.c':
basename = 'src.c'
2011-10-27 07:41:51 +04:00
Building.COMPILER = to_cc(Building.COMPILER)
2011-01-31 18:43:01 +03:00
2010-10-10 03:54:23 +04:00
dirname = self.get_dir()
filename = os.path.join(dirname, basename)
2010-08-26 08:01:10 +04:00
if not no_build:
2011-03-03 18:39:33 +03:00
self.build(src, dirname, filename, main_file=main_file, additional_files=additional_files, libraries=libraries, includes=includes,
build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args, post_build=post_build)
2010-11-06 06:48:19 +03:00
2011-03-03 06:12:13 +03:00
# If not provided with expected output, then generate it right now, using lli
if expected_output is None:
expected_output = self.run_llvm_interpreter([filename + '.o'])
2011-11-03 02:36:36 +04:00
print '[autogenerated expected output: %20s]' % (expected_output[0:30].replace('\n', '|')+'...')
2011-03-03 06:12:13 +03:00
# Run in both JavaScript engines, if optimizing - significant differences there (typed arrays)
2010-10-27 06:13:01 +04:00
if js_engines is None:
js_engines = JS_ENGINES
if Settings.USE_TYPED_ARRAYS:
js_engines = filter(lambda engine: engine != V8_ENGINE, js_engines) # V8 issue 1822
js_engines = filter(lambda engine: engine not in self.banned_js_engines, js_engines)
if len(js_engines) == 0: return self.skip('No JS engine present to run this test with. Check ~/.emscripten and settings.py and the paths therein.')
2010-10-27 06:13:01 +04:00
for engine in js_engines:
js_output = self.run_generated_code(engine, filename + '.o.js', args)
if output_nicerizer is not None:
js_output = output_nicerizer(js_output)
self.assertContained(expected_output, js_output)
self.assertNotContained('ERROR', js_output)
2010-09-24 07:21:03 +04:00
2010-10-02 23:03:07 +04:00
#shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging
2010-08-26 08:01:10 +04:00
2011-01-24 05:23:44 +03:00
# No building - just process an existing .ll file (or .bc, which we turn into .ll)
def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None, output_nicerizer=None, post_build=None, force_recompile=False, build_ll_hook=None, extra_emscripten_args=[]):
2011-01-24 05:23:44 +03:00
filename = os.path.join(self.get_dir(), 'src.cpp')
self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook)
self.ll_to_js(filename, extra_emscripten_args, post_build)
self.do_run(None,
2011-03-03 06:12:13 +03:00
expected_output,
2010-11-21 05:38:44 +03:00
args,
2010-11-21 02:43:17 +03:00
no_build=True,
js_engines=js_engines,
2011-01-17 10:22:57 +03:00
output_nicerizer=output_nicerizer,
post_build=None) # post_build was already done in ll_to_js, this do_run call is just to test the output
2010-11-21 02:43:17 +03:00
2010-08-26 08:01:10 +04:00
def test_hello_world(self):
src = '''
#include <stdio.h>
int main()
{
printf("hello, world!\\n");
return 0;
}
'''
self.do_run(src, 'hello, world!')
2010-08-26 08:01:10 +04:00
def test_intvars(self):
src = '''
#include <stdio.h>
2010-09-04 10:04:23 +04:00
int global = 20;
2010-09-04 10:18:37 +04:00
int *far;
2010-08-26 08:01:10 +04:00
int main()
{
int x = 5;
int y = x+17;
int z = (y-1)/2; // Should stay an integer after division!
y += 1;
int w = x*3+4;
int k = w < 15 ? 99 : 101;
2010-09-04 10:18:37 +04:00
far = &k;
*far += global;
2010-08-26 08:01:10 +04:00
int i = k > 100; // Should be an int, not a bool!
2010-09-03 07:05:14 +04:00
int j = i << 6;
j >>= 1;
2010-09-03 07:27:29 +04:00
j = j ^ 5;
2010-09-03 07:37:30 +04:00
int h = 1;
h |= 0;
int p = h;
p &= 0;
printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", x, y, z, w, k, i, j, h, p);
2010-12-05 07:26:28 +03:00
long hash = -1;
size_t perturb;
int ii = 0;
for (perturb = hash; ; perturb >>= 5) {
printf("%d:%d", ii, perturb);
ii++;
if (ii == 9) break;
printf(",");
}
printf("*\\n");
printf("*%.1d,%.2d*\\n", 56, 9);
2011-02-05 07:58:35 +03:00
2011-05-24 18:33:53 +04:00
// Fixed-point math on 64-bit ints. Tricky to support since we have no 64-bit shifts in JS
{
struct Fixed {
static int Mult(int a, int b) {
return ((long long)a * (long long)b) >> 16;
}
};
printf("fixed:%d\\n", Fixed::Mult(150000, 140000));
}
2010-12-12 05:39:03 +03:00
printf("*%ld*%p\\n", (long)21, &hash); // The %p should not enter an infinite loop!
2010-08-26 08:01:10 +04:00
return 0;
}
'''
self.do_run(src, '*5,23,10,19,121,1,37,1,0*\n0:-1,1:134217727,2:4194303,3:131071,4:4095,5:127,6:3,7:0,8:0*\n*56,09*\nfixed:320434\n*21*')
2010-08-26 08:01:10 +04:00
2011-02-06 07:06:11 +03:00
def test_sintvars(self):
Settings.CORRECT_SIGNS = 1 # Relevant to this test
2011-02-06 07:06:11 +03:00
src = '''
#include <stdio.h>
struct S {
char *match_start;
char *strstart;
};
int main()
{
struct S _s;
struct S *s = &_s;
unsigned short int sh;
s->match_start = (char*)32522;
s->strstart = (char*)(32780);
printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start));
sh = s->strstart - s->match_start;
printf("*%d,%d*\\n", sh, sh>>7);
s->match_start = (char*)32999;
s->strstart = (char*)(32780);
printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start));
sh = s->strstart - s->match_start;
printf("*%d,%d*\\n", sh, sh>>7);
}
'''
output = '*32780,32522,258*\n*258,2*\n*32780,32999,-219*\n*65317,510*'
Settings.CORRECT_OVERFLOWS = 0 # We should not need overflow correction to get this right
self.do_run(src, output, force_c=True)
2011-02-06 07:06:11 +03:00
2011-11-10 05:48:15 +04:00
def test_i64(self):
2011-11-11 01:01:44 +04:00
for i64_mode in [0,1]:
if i64_mode == 0 and Settings.USE_TYPED_ARRAYS != 0: continue # Typed arrays truncate i64
2011-11-13 22:34:00 +04:00
if i64_mode == 1 and Settings.QUANTUM_SIZE == 1: continue # TODO: i64 mode 1 for q1
2011-11-10 05:48:15 +04:00
Settings.I64_MODE = i64_mode
src = '''
#include <stdio.h>
int main()
{
long long x = 0x0000def123450789ULL; // any bigger than this, and we
long long y = 0x00020ef123456089ULL; // start to run into the double precision limit!
printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2);
printf("*");
long long z = 13;
int n = 0;
while (z > 1) {
printf("%.2f,", (float)z); // these must be integers!
z = z >> 1;
n++;
}
printf("*%d*\\n", n);
return 0;
}
2011-11-10 05:48:15 +04:00
'''
self.do_run(src, '*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*')
2011-11-13 22:34:00 +04:00
if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: i64 mode 1 for q1')
2011-11-10 05:48:15 +04:00
# Stuff that only works in i64_mode = 1
2011-11-11 01:01:44 +04:00
Settings.I64_MODE = 1
2011-11-11 01:26:35 +04:00
src = r'''
2011-11-12 03:30:21 +04:00
#include <time.h>
2011-11-11 01:01:44 +04:00
#include <stdio.h>
#include <stdint.h>
2011-11-11 01:26:35 +04:00
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;
}
2011-11-11 01:01:44 +04:00
2011-11-11 01:36:13 +04:00
void modifier1(int64_t t) {
t |= 12;
printf("m1: %Ld\n", t);
}
void modifier2(int64_t &t) {
t |= 12;
}
2011-11-12 03:30:21 +04:00
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[] = {
2011-11-27 09:52:01 +04:00
{ 55, 17179869201 },
{ 122, 25769803837 },
};
2011-11-11 01:01:44 +04:00
int main()
{
int64_t x1 = 0x1234def123450789ULL;
int64_t x2 = 0x1234def123450788ULL;
int64_t x3 = 0x1234def123450789ULL;
2011-11-11 01:26:35 +04:00
printf("*%Ld\n%d,%d,%d,%d,%d\n%d,%d,%d,%d,%d*\n", x1, x1==x2, x1<x2, x1<=x2, x1>x2, x1>=x2, // note: some rounding in the printing!
x1==x3, x1<x3, x1<=x3, x1>x3, x1>=x3);
printf("*%Ld*\n", returner1());
2011-11-11 01:36:13 +04:00
printf("*%Ld*\n", returner2(30));
2011-11-11 03:30:01 +04:00
uint64_t maxx = -1ULL;
printf("*%Lu*\n*%Lu*\n", maxx, maxx >> 5);
2011-11-11 02:46:47 +04:00
// Make sure params are not modified if they shouldn't be
2011-11-11 01:36:13 +04:00
int64_t t = 123;
modifier1(t);
printf("*%Ld*\n", t);
modifier2(t);
printf("*%Ld*\n", t);
2011-11-11 02:46:47 +04:00
// global structs with i64s
printf("*%d,%Ld*\n*%d,%Ld*\n", iub[0].c, iub[0].d, iub[1].c, iub[1].d);
// Math mixtures with doubles
{
uint64_t a = 5;
double b = 6.8;
uint64_t c = a * b;
printf("*prod:%llu*\n*%d,%d,%d*", c, (int)&a, (int)&b, (int)&c); // printing addresses prevents optimizations
}
2011-11-13 07:59:42 +04:00
// Basic (rounded, for now) math. Just check compilation.
2011-11-12 03:30:21 +04:00
int64_t a = 0x1234def123450789ULL;
a--; if (truthy()) a--; // confuse optimizer
int64_t b = 0x1234000000450789ULL;
b++; if (truthy()) b--; // confuse optimizer
2011-11-13 07:59:42 +04:00
printf("*%Ld,%Ld,%Ld,%Ld*\n", (a+b)/5000, (a-b)/5000, (a*3)/5000, (a/5)/5000);
2011-11-12 03:30:21 +04:00
2011-11-11 01:01:44 +04:00
return 0;
}
'''
2011-11-11 03:30:01 +04:00
self.do_run(src, '*1311918518731868200\n0,0,0,1,1\n1,0,1,0,1*\n*245127260211081*\n*245127260209443*\n' +
'*18446744073709552000*\n*576460752303423500*\n' +
'm1: 127\n*123*\n*127*\n' +
'*55,17179869201*\n*122,25769803837*\n' +
'*prod:34*\n')
2011-05-25 18:16:26 +04:00
Settings.CORRECT_SIGNS = 1
src = r'''
#include <stdio.h>
#include <stdint.h>
int main()
{
// i32 vs i64
int32_t small = -1;
int64_t large = -1;
printf("*%d*\n", small == large);
small++;
printf("*%d*\n", small == large);
uint32_t usmall = -1;
uint64_t ularge = -1;
printf("*%d*\n", usmall == ularge);
return 0;
}
'''
self.do_run(src, '*1*\n*0*\n*0*\n')
def test_unaligned(self):
if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1')
2011-12-07 03:16:09 +04:00
src = r'''
#include<stdio.h>
struct S {
double x;
int y;
};
int main() {
// the 64-bit value here will not always be 8-byte aligned
S s[3] = { {0x12a751f430142, 22}, {0x17a5c85bad144, 98}, {1, 1}};
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
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('No meaning to unaligned addresses without t2')
src = r'''
#include <stdio.h>
int main()
{
int x[10];
char *p = (char*)&x[0];
p++;
short *q = (short*)p;
*q = 300;
printf("*%d:%d*\n", *q, ((int)q)%2);
int *r = (int*)p;
*r = 515559;
printf("*%d*\n", *r);
long long *t = (long long*)p;
*t = 42949672960;
printf("*%Ld*\n", *t);
return 0;
}
'''
Settings.EMULATE_UNALIGNED_ACCESSES = 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
# XXX TODO Settings.EMULATE_UNALIGNED_ACCESSES = 1
#self.do_run(src, '*300:1*\n*515559*\n*42949672960*\n') # but succeeds with it
def test_unsigned(self):
Settings.CORRECT_SIGNS = 1 # We test for exactly this sort of thing here
Settings.CHECK_SIGNS = 0
src = '''
#include <stdio.h>
const signed char cvals[2] = { -1, -2 }; // compiler can store this is a string, so -1 becomes \FF, and needs re-signing
int main()
{
2011-09-15 05:08:52 +04:00
{
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);
2011-03-05 19:02:38 +03:00
// 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*')
2011-06-09 06:11:25 +04:00
# Now let's see some code that should just work in USE_TYPED_ARRAYS == 2, but requires
# corrections otherwise
if Settings.USE_TYPED_ARRAYS == 2:
Settings.CORRECT_SIGNS = 0
Settings.CHECK_SIGNS = 1
2011-06-09 06:11:25 +04:00
else:
Settings.CORRECT_SIGNS = 1
Settings.CHECK_SIGNS = 0
2011-06-09 06:11:25 +04:00
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);
}
2011-06-10 02:53:55 +04:00
/*{ // This case is not checked. The hint for unsignedness is just the %u in printf, and we do not analyze that
2011-06-09 06:11:25 +04:00
unsigned int x;
unsigned int *y = &x;
*y = -1;
printf("*%u*\\n", x);
2011-06-10 02:53:55 +04:00
}*/
2011-06-09 06:11:25 +04:00
{
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*')
2011-06-09 06:11:25 +04:00
2010-12-10 07:09:11 +03:00
def test_bitfields(self):
Settings.SAFE_HEAP = 0 # bitfields do loads on invalid areas, by design
2010-12-10 07:09:11 +03:00
src = '''
#include <stdio.h>
struct bitty {
unsigned x : 1;
unsigned y : 1;
unsigned z : 1;
};
int main()
{
bitty b;
printf("*");
for (int i = 0; i <= 1; i++)
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++) {
b.x = i;
b.y = j;
b.z = k;
printf("%d,%d,%d,", b.x, b.y, b.z);
}
printf("*\\n");
return 0;
}
'''
self.do_run(src, '*0,0,0,0,0,1,0,1,0,0,1,1,1,0,0,1,0,1,1,1,0,1,1,1,*')
2010-12-10 07:09:11 +03:00
def test_floatvars(self):
src = '''
#include <stdio.h>
int main()
{
2011-04-21 21:45:57 +04:00
float x = 1.234, y = 3.5, q = 0.00000001;
y *= 3;
2010-08-29 05:24:52 +04:00
int z = x < y;
2011-04-21 21:45:57 +04:00
printf("*%d,%d,%.1f,%d,%.4f,%.2f*\\n", z, int(y), y, (int)x, x, q);
2011-02-28 07:36:30 +03:00
/*
// Rounding behavior
float fs[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 };
double ds[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 };
for (int i = 0; i < 6; i++)
printf("*int(%.2f)=%d,%d*\\n", fs[i], int(fs[i]), int(ds[i]));
*/
return 0;
}
'''
self.do_run(src, '*1,10,10.5,1,1.2340,0.00*')
2010-09-25 07:04:29 +04:00
def test_math(self):
src = '''
#include <stdio.h>
#include <cmath>
int main()
{
2011-12-07 04:32:03 +04:00
printf("*%.2f,%.2f,%d", M_PI, -M_PI, (1/0.0) > 1e300); // could end up as infinity, or just a very very big number
printf(",%d", finite(NAN) != 0);
printf(",%d", finite(INFINITY) != 0);
printf(",%d", finite(-INFINITY) != 0);
printf(",%d", finite(12.3) != 0);
printf(",%d", isinf(NAN) != 0);
printf(",%d", isinf(INFINITY) != 0);
printf(",%d", isinf(-INFINITY) != 0);
printf(",%d", isinf(12.3) != 0);
printf("*\\n");
2010-09-25 07:04:29 +04:00
return 0;
}
'''
2011-12-07 04:32:03 +04:00
self.do_run(src, '*3.14,-3.14,1,0,0,0,1,0,1,1,0*')
2010-09-25 07:04:29 +04:00
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_getgep(self):
# Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP
src = '''
#include <stdio.h>
struct {
int y[10];
int z[10];
} commonblock;
int main()
{
for (int i = 0; i < 10; ++i) {
commonblock.y[i] = 1;
commonblock.z[i] = 2;
}
printf("*%d %d*\\n", commonblock.y[0], commonblock.z[0]);
return 0;
}
'''
self.do_run(src, '*1 2*')
2010-08-26 08:01:10 +04:00
def test_if(self):
src = '''
#include <stdio.h>
int main()
{
int x = 5;
if (x > 3) {
printf("*yes*\\n");
}
return 0;
}
'''
self.do_run(src, '*yes*')
2010-08-26 08:01:10 +04:00
2010-10-17 01:38:27 +04:00
def test_if_else(self):
src = '''
#include <stdio.h>
int main()
{
int x = 5;
if (x > 10) {
printf("*yes*\\n");
} else {
printf("*no*\\n");
}
return 0;
}
'''
self.do_run(src, '*no*')
2010-10-17 01:38:27 +04:00
2010-08-26 08:01:10 +04:00
def test_loop(self):
src = '''
#include <stdio.h>
int main()
{
int x = 5;
2012-01-02 03:46:33 +04:00
for (int i = 0; i < 6; i++) {
2010-08-26 08:01:10 +04:00
x += x*i;
2012-01-02 03:46:33 +04:00
if (x > 1000) {
if (x % 7 == 0) printf("cheez\\n");
x /= 2;
break;
}
}
2010-08-26 08:01:10 +04:00
printf("*%d*\\n", x);
return 0;
}
'''
2012-01-02 03:46:33 +04:00
self.do_run(src)
generated = open('src.cpp.o.js', 'r').read()
assert '__label__ ==' not in generated, 'We should hoist into the loop'
2010-08-26 08:01:10 +04:00
2010-09-29 06:58:22 +04:00
def test_stack(self):
src = '''
#include <stdio.h>
int test(int i) {
int x = 10;
if (i > 0) {
return test(i-1);
}
return int(&x); // both for the number, and forces x to not be nativized
2010-09-29 06:58:22 +04:00
}
int main()
{
// We should get the same value for the first and last - stack has unwound
int x1 = test(0);
int x2 = test(100);
int x3 = test(0);
printf("*%d,%d*\\n", x3-x1, x2 != x1);
return 0;
}
'''
self.do_run(src, '*0,1*')
2010-09-29 06:58:22 +04:00
2010-08-26 08:01:10 +04:00
def test_strings(self):
src = '''
#include <stdio.h>
#include <stdlib.h>
2010-09-11 08:15:40 +04:00
#include <string.h>
2010-08-26 08:01:10 +04:00
int main(int argc, char **argv)
{
2010-12-29 06:52:41 +03:00
int x = 5, y = 9, magic = 7; // fool compiler with magic
memmove(&x, &y, magic-7); // 0 should not crash us
2011-01-17 10:22:57 +03:00
int xx, yy, zz;
2011-08-28 20:47:53 +04:00
char s[32];
int cc = sscanf("abc_10.b1_xyz_543_defg", "abc_%d.%2x_xyz_%3d_%3s", &xx, &yy, &zz, s);
printf("%d:%d,%d,%d,%s\\n", cc, xx, yy, zz, s);
2011-01-17 10:22:57 +03:00
printf("%d\\n", argc);
2010-08-26 08:01:10 +04:00
puts(argv[1]);
puts(argv[2]);
2010-09-24 07:21:03 +04:00
printf("%d\\n", atoi(argv[3])+2);
2010-09-11 08:15:40 +04:00
const char *foolingthecompiler = "\\rabcd";
2010-09-24 07:21:03 +04:00
printf("%d\\n", strlen(foolingthecompiler)); // Tests parsing /0D in llvm - should not be a 0 (end string) then a D!
printf("%s\\n", NULL); // Should print '(null)', not the string at address 0, which is a real address for us!
printf("/* a comment */\\n"); // Should not break the generated code!
printf("// another\\n"); // Should not break the generated code!
char* strdup_val = strdup("test");
printf("%s\\n", strdup_val);
free(strdup_val);
2010-08-26 08:01:10 +04:00
return 0;
}
'''
self.do_run(src, '4:10,177,543,def\n4\nwowie\ntoo\n76\n5\n(null)\n/* a comment */\n// another\ntest\n', ['wowie', 'too', '74'])
2010-08-26 08:01:10 +04:00
2011-09-10 00:51:14 +04:00
def test_errar(self):
src = r'''
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
char* err;
char buffer[200];
err = strerror(EDOM);
strerror_r(EWOULDBLOCK, buffer, 200);
printf("<%s>\n", err);
printf("<%s>\n", buffer);
printf("<%d>\n", strerror_r(EWOULDBLOCK, buffer, 0));
errno = 123;
printf("<%d>\n", errno);
return 0;
}
'''
expected = '''
<Numerical argument out of domain>
<Resource temporarily unavailable>
<34>
<123>
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
def test_mainenv(self):
src = '''
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
printf("*%p*\\n", envp);
return 0;
}
'''
self.do_run(src, '*(nil)*')
2010-08-26 08:01:10 +04:00
def test_funcs(self):
src = '''
#include <stdio.h>
int funcy(int x)
{
return x*9;
}
int main()
{
printf("*%d,%d*\\n", funcy(8), funcy(10));
return 0;
}
'''
self.do_run(src, '*72,90*')
2010-08-26 08:01:10 +04:00
def test_structs(self):
src = '''
#include <stdio.h>
struct S
{
int x, y;
};
int main()
{
S a, b;
a.x = 5; a.y = 6;
b.x = 101; b.y = 7009;
S *c, *d;
c = &a;
c->x *= 2;
c = &b;
c->y -= 1;
d = c;
d->y += 10;
printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n", a.x, a.y, b.x, b.y, c->x, c->y, d->x, d->y);
return 0;
}
'''
self.do_run(src, '*10,6,101,7018,101,7018,101,7018*')
2010-08-26 08:01:10 +04:00
gen_struct_src = '''
#include <stdio.h>
#include <stdlib.h>
2010-09-08 06:23:00 +04:00
#include "emscripten.h"
2010-08-26 08:01:10 +04:00
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*')
2010-08-26 08:01:10 +04:00
def test_newstruct(self):
self.do_run(self.gen_struct_src.replace('{{gen_struct}}', 'new S').replace('{{del_struct}}', 'delete'), '*51,62*')
2010-08-26 08:01:10 +04:00
def test_addr_of_stacked(self):
src = '''
#include <stdio.h>
void alter(int *y)
{
*y += 5;
}
int main()
{
int x = 2;
alter(&x);
printf("*%d*\\n", x);
return 0;
}
'''
self.do_run(src, '*7*')
2010-08-26 08:01:10 +04:00
def test_globals(self):
src = '''
#include <stdio.h>
char cache[256], *next = cache;
int main()
{
cache[10] = 25;
next[20] = 51;
printf("*%d,%d*\\n", next[10], cache[20]);
return 0;
}
'''
self.do_run(src, '*25,51*')
2010-08-26 08:01:10 +04:00
def test_linked_list(self):
src = '''
#include <stdio.h>
struct worker_args {
int value;
struct worker_args *next;
};
int main()
{
worker_args a;
worker_args b;
a.value = 60;
a.next = &b;
b.value = 900;
b.next = NULL;
worker_args* c = &a;
int total = 0;
while (c) {
total += c->value;
c = c->next;
}
// Chunk of em
worker_args chunk[10];
for (int i = 0; i < 9; i++) {
chunk[i].value = i*10;
chunk[i].next = &chunk[i+1];
}
chunk[9].value = 90;
chunk[9].next = &chunk[0];
c = chunk;
do {
total += c->value;
c = c->next;
} while (c != chunk);
printf("*%d,%d*\\n", total, b.next);
// NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.)
2010-08-26 08:01:10 +04:00
return 0;
}
'''
self.do_run(src, '*1410,0*')
def test_sup(self):
src = '''
#include <stdio.h>
struct S4 { int x; }; // size: 4
struct S4_2 { short x, y; }; // size: 4, but for alignment purposes, 2
struct S6 { short x, y, z; }; // size: 6
struct S6w { char x[6]; }; // size: 6 also
struct S6z { int x; short y; }; // size: 8, since we align to a multiple of the biggest - 4
struct C___ { S6 a, b, c; int later; };
struct Carr { S6 a[3]; int later; }; // essentially the same, but differently defined
struct C__w { S6 a; S6w b; S6 c; int later; }; // same size, different struct
struct Cp1_ { int pre; short a; S6 b, c; int later; }; // fillers for a
struct Cp2_ { int a; short pre; S6 b, c; int later; }; // fillers for a (get addr of the other filler)
struct Cint { S6 a; int b; S6 c; int later; }; // An int (different size) for b
struct C4__ { S6 a; S4 b; S6 c; int later; }; // Same size as int from before, but a struct
struct C4_2 { S6 a; S4_2 b; S6 c; int later; }; // Same size as int from before, but a struct with max element size 2
struct C__z { S6 a; S6z b; S6 c; int later; }; // different size, 8 instead of 6
int main()
{
#define TEST(struc) \\
{ \\
struc *s = 0; \\
printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a), (int)&(s->b), (int)&(s->c), (int)&(s->later), sizeof(struc)); \\
}
#define TEST_ARR(struc) \\
{ \\
struc *s = 0; \\
printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a[0]), (int)&(s->a[1]), (int)&(s->a[2]), (int)&(s->later), sizeof(struc)); \\
}
printf("sizeofs:%d,%d\\n", sizeof(S6), sizeof(S6z));
TEST(C___);
TEST_ARR(Carr);
TEST(C__w);
TEST(Cp1_);
TEST(Cp2_);
TEST(Cint);
TEST(C4__);
TEST(C4_2);
TEST(C__z);
return 1;
}
'''
if Settings.QUANTUM_SIZE == 1:
self.do_run(src, 'sizeofs:6,8\n*C___: 0,3,6,9<24*\n*Carr: 0,3,6,9<24*\n*C__w: 0,3,9,12<24*\n*Cp1_: 1,2,5,8<24*\n*Cp2_: 0,2,5,8<24*\n*Cint: 0,3,4,7<24*\n*C4__: 0,3,4,7<24*\n*C4_2: 0,3,5,8<20*\n*C__z: 0,3,5,8<28*')
2011-04-22 18:53:31 +04:00
else:
self.do_run(src, 'sizeofs:6,8\n*C___: 0,6,12,20<24*\n*Carr: 0,6,12,20<24*\n*C__w: 0,6,12,20<24*\n*Cp1_: 4,6,12,20<24*\n*Cp2_: 0,6,12,20<24*\n*Cint: 0,8,12,20<24*\n*C4__: 0,8,12,20<24*\n*C4_2: 0,6,10,16<20*\n*C__z: 0,8,16,24<28*')
def test_assert(self):
src = '''
#include <stdio.h>
#include <assert.h>
int main() {
assert(1 == true); // pass
assert(1 == false); // fail
return 1;
}
'''
self.do_run(src, 'Assertion failed: 1 == false')
2010-08-26 08:01:10 +04:00
2010-11-21 02:19:01 +03:00
def test_exceptions(self):
self.banned_js_engines = [NODE_JS] # node issue 1669, exception causes stdout not to be flushed
2011-12-22 05:13:04 +04:00
Settings.DISABLE_EXCEPTION_CATCHING = 0
2010-11-21 02:19:01 +03:00
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 1;
}
'''
self.do_run(src, '*throw...caught!infunc...done!*')
2010-11-21 02:19:01 +03:00
Settings.DISABLE_EXCEPTION_CATCHING = 1
self.do_run(src, 'Compiled code throwing an exception')
2011-08-15 08:53:34 +04:00
2011-09-13 03:07:59 +04:00
def test_typed_exceptions(self):
return self.skip('TODO: fix this for llvm 3.0')
Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access.
Settings.EXCEPTION_DEBUG = 0 # Messes up expected output.
2011-09-13 03:07:59 +04:00
src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read()
expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read()
self.do_run(src, expected)
2011-09-13 03:07:59 +04:00
2010-08-26 08:01:10 +04:00
def test_class(self):
src = '''
#include <stdio.h>
struct Random {
enum { IM = 139968, IA = 3877, IC = 29573 };
Random() : last(42) {}
float get( float max = 1.0f ) {
last = ( last * IA + IC ) % IM;
return max * last / IM;
}
protected:
unsigned int last;
} rng1;
int main()
{
Random rng2;
int count = 0;
for (int i = 0; i < 100; i++) {
float x1 = rng1.get();
float x2 = rng2.get();
printf("%f, %f\\n", x1, x2);
if (x1 != x2) count += 1;
}
printf("*%d*\\n", count);
return 0;
}
'''
self.do_run(src, '*0*')
2010-08-26 08:01:10 +04:00
def test_inherit(self):
src = '''
#include <stdio.h>
struct Parent {
int x1, x2;
};
struct Child : Parent {
int y;
};
int main()
{
Parent a;
a.x1 = 50;
a.x2 = 87;
Child b;
b.x1 = 78;
b.x2 = 550;
b.y = 101;
Child* c = (Child*)&a;
c->x1 ++;
c = &b;
c->y --;
printf("*%d,%d,%d,%d,%d,%d,%d*\\n", a.x1, a.x2, b.x1, b.x2, b.y, c->x1, c->x2);
return 0;
}
'''
self.do_run(src, '*51,87,78,550,100,78,550*')
2010-08-26 08:01:10 +04:00
def test_polymorph(self):
src = '''
#include <stdio.h>
2010-10-24 07:37:49 +04:00
struct Pure {
virtual int implme() = 0;
};
struct Parent : Pure {
2010-08-26 08:01:10 +04:00
virtual int getit() { return 11; };
2010-10-24 07:37:49 +04:00
int implme() { return 32; }
2010-08-26 08:01:10 +04:00
};
struct Child : Parent {
int getit() { return 74; }
2010-10-24 07:37:49 +04:00
int implme() { return 1012; }
2010-08-26 08:01:10 +04:00
};
struct Other {
int one() { return 11; }
int two() { return 22; }
};
2010-08-26 08:01:10 +04:00
int main()
{
Parent *x = new Parent();
Parent *y = new Child();
2010-10-24 07:37:49 +04:00
printf("*%d,%d,%d,%d*\\n", x->getit(), y->getit(), x->implme(), y->implme());
Other *o = new Other;
int (Other::*Ls)() = &Other::one;
printf("*%d*\\n", (o->*(Ls))());
Ls = &Other::two;
printf("*%d*\\n", (o->*(Ls))());
2010-08-26 08:01:10 +04:00
return 0;
}
'''
self.do_run(src, '*11,74,32,1012*\n*11*\n*22*')
2010-08-26 08:01:10 +04:00
2011-11-08 06:22:16 +04:00
def test_dynamic_cast(self):
src = '''
#include <stdio.h>
class CBase { virtual void dummy() {} };
class CDerived : public CBase { int a; };
class CDerivedest : public CDerived { float b; };
int main ()
{
CBase *pa = new CBase;
CBase *pb = new CDerived;
CBase *pc = new CDerivedest;
printf("a1: %d\\n", dynamic_cast<CDerivedest*>(pa) != NULL);
printf("a2: %d\\n", dynamic_cast<CDerived*>(pa) != NULL);
printf("a3: %d\\n", dynamic_cast<CBase*>(pa) != NULL);
printf("b1: %d\\n", dynamic_cast<CDerivedest*>(pb) != NULL);
printf("b2: %d\\n", dynamic_cast<CDerived*>(pb) != NULL);
printf("b3: %d\\n", dynamic_cast<CBase*>(pb) != NULL);
printf("c1: %d\\n", dynamic_cast<CDerivedest*>(pc) != NULL);
printf("c2: %d\\n", dynamic_cast<CDerived*>(pc) != NULL);
printf("c3: %d\\n", dynamic_cast<CBase*>(pc) != NULL);
return 0;
}
'''
self.do_run(src, 'a1: 0\na2: 0\na3: 1\nb1: 0\nb2: 1\nb3: 1\nc1: 1\nc2: 1\nc3: 1\n')
def test_funcptr(self):
src = '''
#include <stdio.h>
int calc1() { return 26; }
int calc2() { return 90; }
typedef int (*fp_t)();
fp_t globally1 = calc1;
fp_t globally2 = calc2;
2011-05-15 19:04:34 +04:00
int nothing(const char *str) { return 0; }
int main()
{
fp_t fp = calc1;
void *vp = (void*)fp;
fp_t fpb = (fp_t)vp;
fp_t fp2 = calc2;
void *vp2 = (void*)fp2;
fp_t fpb2 = (fp_t)vp2;
printf("*%d,%d,%d,%d,%d,%d*\\n", fp(), fpb(), fp2(), fpb2(), globally1(), globally2());
2010-12-06 04:30:45 +03:00
fp_t t = calc1;
printf("*%d,%d", t == calc1, t == calc2);
t = calc2;
printf(",%d,%d*\\n", t == calc1, t == calc2);
2011-05-15 19:04:34 +04:00
int (*other)(const char *str);
other = nothing;
other("*hello!*");
other = puts;
other("*goodbye!*");
return 0;
}
'''
self.do_run(src, '*26,26,90,90,26,90*\n*1,0,0,1*\n*goodbye!*')
def test_mathfuncptr(self):
src = '''
#include <math.h>
#include <stdio.h>
int
main(void) {
float (*fn)(float) = &sqrtf;
float (*fn2)(float) = &fabsf;
printf("fn2(-5) = %d, fn(10) = %f\\n", (int)fn2(-5), fn(10));
return 0;
}
'''
self.do_run(src, 'fn2(-5) = 5, fn(10) = 3.16')
def test_emptyclass(self):
src = '''
#include <stdio.h>
struct Randomized {
Randomized(int x) {
printf("*zzcheezzz*\\n");
}
};
int main( int argc, const char *argv[] ) {
new Randomized(55);
return 0;
}
'''
self.do_run(src, '*zzcheezzz*')
2011-05-27 23:46:32 +04:00
def test_alloca(self):
src = '''
#include <stdio.h>
#include <stdlib.h>
2011-05-27 23:46:32 +04:00
int main() {
char *pc;
pc = (char *)alloca(5);
printf("z:%d*%d*\\n", pc > 0, (int)pc);
return 0;
}
'''
self.do_run(src, 'z:1*', force_c=True)
2011-05-27 23:46:32 +04:00
if self.emcc_args is not None: # too slow in other modes
# We should not blow up the stack with numerous allocas
src = '''
#include <stdio.h>
#include <stdlib.h>
func(int i) {
char *pc = (char *)alloca(100);
*pc = i;
(*pc)++;
return (*pc) % 10;
}
int main() {
int total = 0;
for (int i = 0; i < 1024*1024; i++)
total += func(i);
printf("ok:%d*\\n", total);
return 0;
}
'''
self.do_run(src, 'ok:-32768*', force_c=True)
# We should also not blow up the stack with byval arguments
src = r'''
#include<stdio.h>
struct vec {
int x, y, z;
vec(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {}
static vec add(vec a, vec b) {
return vec(a.x+b.x, a.y+b.y, a.z+b.z);
}
};
int main() {
int total = 0;
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
vec c(i+i%10, j*2, i%255);
vec d(j*2, j%255, i%120);
vec f = vec::add(c, d);
total += (f.x + f.y + f.z) % 100;
total %= 10240;
}
}
printf("sum:%d*\n", total);
return 1;
}
'''
self.do_run(src, 'sum:9780*')
2010-09-26 07:57:52 +04:00
def test_array2(self):
src = '''
#include <stdio.h>
static const double grid[4][2] = {
{-3/3.,-1/3.},{+1/3.,-3/3.},
{-1/3.,+3/3.},{+3/3.,+1/3.}
2010-09-26 07:57:52 +04:00
};
int main() {
for (int i = 0; i < 4; i++)
printf("%d:%.2f,%.2f ", i, grid[i][0], grid[i][1]);
printf("\\n");
return 0;
}
'''
self.do_run(src, '0:-1.00,-0.33 1:0.33,-1.00 2:-0.33,1.00 3:1.00,0.33')
2010-09-26 07:57:52 +04:00
def test_array2b(self):
src = '''
#include <stdio.h>
static const struct {
unsigned char left;
unsigned char right;
} prioritah[] = {
{6, 6}, {6, 6}, {7, 95}, {7, 7}
};
int main() {
printf("*%d,%d\\n", prioritah[1].left, prioritah[1].right);
printf("%d,%d*\\n", prioritah[2].left, prioritah[2].right);
return 0;
}
'''
self.do_run(src, '*6,6\n7,95*')
def test_constglobalstructs(self):
2010-08-26 08:01:10 +04:00
src = '''
#include <stdio.h>
struct IUB {
int c;
double p;
unsigned int pi;
};
IUB iub[] = {
{ 'a', 0.27, 5 },
{ 'c', 0.15, 4 },
{ 'g', 0.12, 3 },
{ 't', 0.27, 2 },
};
const unsigned char faceedgesidx[6][4] =
{
{ 4, 5, 8, 10 },
{ 6, 7, 9, 11 },
{ 0, 2, 8, 9 },
{ 1, 3, 10,11 },
{ 0, 1, 4, 6 },
{ 2, 3, 5, 7 },
};
2010-08-26 08:01:10 +04:00
int main( int argc, const char *argv[] ) {
printf("*%d,%d,%d,%d*\\n", iub[0].c, int(iub[1].p*100), iub[2].pi, faceedgesidx[3][2]);
2010-08-26 08:01:10 +04:00
return 0;
}
'''
self.do_run(src, '*97,15,3,10*')
2010-08-26 08:01:10 +04:00
def test_conststructs(self):
src = '''
#include <stdio.h>
struct IUB {
int c;
double p;
unsigned int pi;
};
int main( int argc, const char *argv[] ) {
int before = 70;
2010-08-26 08:01:10 +04:00
IUB iub[] = {
{ 'a', 0.3029549426680, 5 },
2010-08-26 08:01:10 +04:00
{ 'c', 0.15, 4 },
{ 'g', 0.12, 3 },
{ 't', 0.27, 2 },
};
int after = 90;
printf("*%d,%d,%d,%d,%d,%d*\\n", before, iub[0].c, int(iub[1].p*100), iub[2].pi, int(iub[0].p*10000), after);
2010-08-26 08:01:10 +04:00
return 0;
}
'''
self.do_run(src, '*70,97,15,3,3029,90*')
2010-08-26 08:01:10 +04:00
def test_mod_globalstruct(self):
src = '''
#include <stdio.h>
struct malloc_params {
size_t magic, page_size;
};
malloc_params mparams;
#define SIZE_T_ONE ((size_t)1)
#define page_align(S) (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE))
int main()
{
mparams.page_size = 4096;
printf("*%d,%d,%d,%d*\\n", mparams.page_size, page_align(1000), page_align(6000), page_align(66474));
return 0;
}
'''
self.do_run(src, '*4096,4096,8192,69632*')
def test_pystruct(self):
src = '''
#include <stdio.h>
// Based on CPython code
union PyGC_Head {
struct {
union PyGC_Head *gc_next;
union PyGC_Head *gc_prev;
size_t gc_refs;
} gc;
long double dummy; /* force worst-case alignment */
} ;
struct gc_generation {
PyGC_Head head;
int threshold; /* collection threshold */
int count; /* count of allocations or collections of younger
generations */
};
#define NUM_GENERATIONS 3
#define GEN_HEAD(n) (&generations[n].head)
/* linked lists of container objects */
static struct gc_generation generations[NUM_GENERATIONS] = {
/* PyGC_Head, threshold, count */
{{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0},
{{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0},
{{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0},
};
int main()
{
gc_generation *n = NULL;
printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n",
(int)(&n[0]),
(int)(&n[0].head),
(int)(&n[0].head.gc.gc_next),
(int)(&n[0].head.gc.gc_prev),
(int)(&n[0].head.gc.gc_refs),
(int)(&n[0].threshold), (int)(&n[0].count), (int)(&n[1])
);
printf("*%d,%d,%d*\\n",
(int)(&generations[0]) ==
(int)(&generations[0].head.gc.gc_next),
(int)(&generations[0]) ==
(int)(&generations[0].head.gc.gc_prev),
(int)(&generations[0]) ==
(int)(&generations[1])
);
int x1 = (int)(&generations[0]);
int x2 = (int)(&generations[1]);
printf("*%d*\\n", x1 == x2);
for (int i = 0; i < NUM_GENERATIONS; i++) {
PyGC_Head *list = GEN_HEAD(i);
printf("%d:%d,%d\\n", i, (int)list == (int)(list->gc.gc_prev), (int)list ==(int)(list->gc.gc_next));
}
2011-02-12 05:37:04 +03:00
printf("*%d,%d,%d*\\n", sizeof(PyGC_Head), sizeof(gc_generation), int(GEN_HEAD(2)) - int(GEN_HEAD(1)));
}
'''
if Settings.QUANTUM_SIZE == 1:
2011-04-22 18:53:31 +04:00
# Compressed memory. Note that sizeof() does give the fat sizes, however!
self.do_run(src, '*0,0,0,1,2,3,4,5*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,5*')
2011-04-22 18:53:31 +04:00
else:
self.do_run(src, '*0,0,0,4,8,12,16,20*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,20*')
def test_ptrtoint(self):
src = '''
#include <stdio.h>
int main( int argc, const char *argv[] ) {
char *a = new char[10];
char *a0 = a+0;
char *a5 = a+5;
int *b = new int[10];
int *b0 = b+0;
int *b5 = b+5;
int c = (int)b5-(int)b0; // Emscripten should warn!
int d = (int)b5-(int)b0; // Emscripten should warn!
printf("*%d*\\n", (int)a5-(int)a0);
return 0;
}
'''
runner = self
def check_warnings(output):
runner.assertEquals(filter(lambda line: 'Warning' in line, output.split('\n')).__len__(), 4)
self.do_run(src, '*5*', output_processor=check_warnings)
2010-08-26 08:01:10 +04:00
2010-09-08 06:23:00 +04:00
def test_sizeof(self):
2010-11-26 00:06:31 +03:00
# Has invalid writes between printouts
Settings.SAFE_HEAP = 0
2010-11-26 00:06:31 +03:00
2010-08-26 08:01:10 +04:00
src = '''
#include <stdio.h>
#include <string.h>
2010-09-08 06:23:00 +04:00
#include "emscripten.h"
struct A { int x, y; };
2010-08-26 08:01:10 +04:00
int main( int argc, const char *argv[] ) {
int *a = new int[10];
int *b = new int[1];
int *c = new int[10];
for (int i = 0; i < 10; i++)
a[i] = 2;
*b = 5;
for (int i = 0; i < 10; i++)
c[i] = 8;
printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]);
// Should overwrite a, but not touch b!
2011-04-22 18:53:31 +04:00
memcpy(a, c, 10*sizeof(int));
2010-08-26 08:01:10 +04:00
printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]);
2010-09-08 06:23:00 +04:00
// Part 2
2010-09-09 06:56:06 +04:00
A as[3] = { { 5, 12 }, { 6, 990 }, { 7, 2 } };
2011-04-22 18:53:31 +04:00
memcpy(&as[0], &as[2], sizeof(A));
2010-09-08 06:23:00 +04:00
printf("*%d,%d,%d,%d,%d,%d*\\n", as[0].x, as[0].y, as[1].x, as[1].y, as[2].x, as[2].y);
2010-08-26 08:01:10 +04:00
return 0;
}
'''
self.do_run(src, '*2,2,5,8,8***8,8,5,8,8***7,2,6,990,7,2*', [], lambda x: x.replace('\n', '*'))
2010-08-26 08:01:10 +04:00
2011-02-13 06:35:45 +03:00
def test_emscripten_api(self):
2011-11-18 22:02:45 +04:00
#if Settings.MICRO_OPTS or Settings.RELOOP or Building.LLVM_OPTS: return self.skip('FIXME')
2011-12-07 01:27:43 +04:00
src = r'''
2011-02-13 06:35:45 +03:00
#include <stdio.h>
#include "emscripten.h"
int main() {
// EMSCRIPTEN_COMMENT("hello from the source");
2011-02-13 06:35:45 +03:00
emscripten_run_script("print('hello world' + '!')");
2011-12-07 01:27:43 +04:00
printf("*%d*\n", emscripten_run_script_int("5*20"));
2011-02-13 06:35:45 +03:00
return 0;
}
'''
2011-10-11 06:58:29 +04:00
check = '''
def process(filename):
src = open(filename, 'r').read()
# TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src
2011-12-21 09:28:53 +04:00
'''
2011-10-11 06:58:29 +04:00
2011-12-07 01:27:43 +04:00
self.do_run(src, 'hello world!\n*100*', post_build=check)
def test_memorygrowth(self):
# 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()
{
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);
2011-12-07 01:27:43 +04:00
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;
}
'''
self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*')
2011-02-13 06:35:45 +03:00
2011-04-22 18:53:31 +04:00
def test_ssr(self): # struct self-ref
src = '''
#include <stdio.h>
// see related things in openjpeg
typedef struct opj_mqc_state {
unsigned int qeval;
int mps;
struct opj_mqc_state *nmps;
struct opj_mqc_state *nlps;
} opj_mqc_state_t;
static opj_mqc_state_t mqc_states[2] = {
{0x5600, 0, &mqc_states[2], &mqc_states[3]},
{0x5602, 1, &mqc_states[3], &mqc_states[2]},
};
int main() {
printf("*%d*\\n", (int)(mqc_states+1)-(int)mqc_states);
for (int i = 0; i < 2; i++)
printf("%d:%d,%d,%d,%d\\n", i, mqc_states[i].qeval, mqc_states[i].mps,
(int)mqc_states[i].nmps-(int)mqc_states, (int)mqc_states[i].nlps-(int)mqc_states);
return 0;
}
'''
if Settings.QUANTUM_SIZE == 1:
self.do_run(src, '''*4*\n0:22016,0,8,12\n1:22018,1,12,8\n''')
2011-04-22 18:53:31 +04:00
else:
self.do_run(src, '''*16*\n0:22016,0,32,48\n1:22018,1,48,32\n''')
2011-04-22 18:53:31 +04:00
2010-10-24 04:48:34 +04:00
def test_tinyfuncstr(self):
src = '''
#include <stdio.h>
struct Class {
static char *name1() { return "nameA"; }
char *name2() { return "nameB"; }
};
int main() {
printf("*%s,%s*\\n", Class::name1(), (new Class())->name2());
return 0;
}
'''
self.do_run(src, '*nameA,nameB*')
2010-10-24 04:48:34 +04:00
2010-08-30 03:25:06 +04:00
def test_llvmswitch(self):
src = '''
#include <stdio.h>
#include <string.h>
int switcher(int p)
{
switch(p) {
case 'a':
case 'b':
case 'c':
return p-1;
case 'd':
return p+1;
}
return p;
}
int main( int argc, const char *argv[] ) {
printf("*%d,%d,%d,%d,%d*\\n", switcher('a'), switcher('b'), switcher('c'), switcher('d'), switcher('e'));
return 0;
}
'''
self.do_run(src, '*96,97,98,101,101*')
2010-08-30 03:25:06 +04:00
def test_indirectbr(self):
2011-11-10 05:48:15 +04:00
if Settings.USE_TYPED_ARRAYS == 2:
Settings.I64_MODE = 1 # Unsafe optimizations use 64-bit load/store on two i32s
src = '''
#include <stdio.h>
int main(void) {
const void *addrs[2] = { &&FOO, &&BAR };
// confuse the optimizer so it doesn't hardcode the jump and avoid generating an |indirectbr| instruction
int which = 0;
for (int x = 0; x < 1000; x++) which = (which + x*x) % 7;
which = (which % 2) + 1;
goto *addrs[which];
FOO:
printf("bad\\n");
return 1;
BAR:
printf("good\\n");
const void *addr = &&FOO;
goto *addr;
}
'''
self.do_run(src, 'good\nbad')
2011-04-17 03:35:08 +04:00
def test_pack(self):
src = '''
#include <stdio.h>
#include <string.h>
#pragma pack(push,1)
typedef struct header
{
unsigned char id;
unsigned short colour;
unsigned char desc;
} header;
#pragma pack(pop)
typedef struct fatheader
{
unsigned char id;
unsigned short colour;
unsigned char desc;
} fatheader;
int main( int argc, const char *argv[] ) {
header h, *ph = 0;
fatheader fh, *pfh = 0;
printf("*%d,%d,%d*\\n", sizeof(header), (int)((int)&h.desc - (int)&h.id), (int)(&ph[1])-(int)(&ph[0]));
printf("*%d,%d,%d*\\n", sizeof(fatheader), (int)((int)&fh.desc - (int)&fh.id), (int)(&pfh[1])-(int)(&pfh[0]));
return 0;
}
'''
if Settings.QUANTUM_SIZE == 1:
self.do_run(src, '*4,2,3*\n*6,2,3*')
2011-04-22 18:53:31 +04:00
else:
self.do_run(src, '*4,3,4*\n*6,4,6*')
2011-04-17 03:35:08 +04:00
2010-09-05 05:05:18 +04:00
def test_varargs(self):
if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this')
2011-04-23 00:23:37 +04:00
2010-09-05 03:46:11 +04:00
src = '''
#include <stdio.h>
2010-11-27 03:55:02 +03:00
#include <stdarg.h>
2010-09-05 03:46:11 +04:00
void vary(const char *s, ...)
{
va_list v;
va_start(v, s);
char d[20];
vsnprintf(d, 20, s, v);
puts(d);
2010-12-05 02:33:29 +03:00
// Try it with copying
va_list tempva;
__va_copy(tempva, v);
vsnprintf(d, 20, s, tempva);
puts(d);
2010-09-05 03:46:11 +04:00
va_end(v);
}
void vary2(char color, const char *s, ...)
{
va_list v;
va_start(v, s);
char d[21];
d[0] = color;
vsnprintf(d+1, 20, s, v);
puts(d);
va_end(v);
}
2010-11-27 03:55:02 +03:00
#define GETMAX(pref, type) \
type getMax##pref(int num, ...) \
{ \
va_list vv; \
va_start(vv, num); \
type maxx = va_arg(vv, type); \
for (int i = 1; i < num; i++) \
{ \
type curr = va_arg(vv, type); \
maxx = curr > maxx ? curr : maxx; \
} \
va_end(vv); \
return maxx; \
}
GETMAX(i, int);
GETMAX(D, double);
2010-09-05 03:46:11 +04:00
int main() {
2010-09-12 01:15:46 +04:00
vary("*cheez: %d+%d*", 0, 24); // Also tests that '0' is not special as an array ender
vary("*albeit*"); // Should not fail with no var args in vararg function
vary2('Q', "%d*", 85);
2010-11-27 03:55:02 +03:00
int maxxi = getMaxi(6, 2, 5, 21, 4, -10, 19);
printf("maxxi:%d*\\n", maxxi);
double maxxD = getMaxD(6, (double)2.1, (double)5.1, (double)22.1, (double)4.1, (double)-10.1, (double)19.1);
printf("maxxD:%.2f*\\n", (float)maxxD);
2011-11-25 21:51:02 +04:00
// And, as a function pointer
void (*vfp)(const char *s, ...) = vary;
vfp("*vfp:%d,%d*", 22, 199);
2010-09-05 03:46:11 +04:00
return 0;
}
'''
2011-11-25 21:51:02 +04:00
self.do_run(src, '*cheez: 0+24*\n*cheez: 0+24*\n*albeit*\n*albeit*\nQ85*\nmaxxi:21*\nmaxxD:22.10*\n*vfp:22,199*\n*vfp:22,199*\n')
def test_structbyval(self):
# 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
2011-11-25 21:51:02 +04:00
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) {
2011-11-25 22:11:20 +04:00
p.x++; // should not modify
p.y++; // anything in the caller!
2011-11-25 21:51:02 +04:00
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
2011-11-25 21:51:02 +04:00
extern "C" {
#endif
void dump(struct point p);
#ifdef __cplusplus
2011-11-25 21:51:02 +04:00
}
#endif
2011-11-25 21:51:02 +04:00
int main( int argc, const char *argv[] ) {
struct point p = { 54, 2 };
2011-11-25 21:51:02 +04:00
printf("pre: %d,%d\n", p.x, p.y);
dump(p);
void (*dp)(struct point p) = dump; // And, as a function pointer
2011-11-25 22:11:20 +04:00
dp(p);
printf("post: %d,%d\n", p.x, p.y);
2011-11-25 21:51:02 +04:00
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)
2011-11-25 21:51:02 +04:00
all_name = os.path.join(self.get_dir(), 'all.bc')
Building.link([supp_name + '.o', main_name + '.o'], all_name)
try:
# This will fail! See explanation near the warning we check for, in the compiler source code
self.do_ll_run(all_name, 'pre: 54,2\ndump: 55,3\ndump: 55,3\npost: 54,2')
except Exception, e:
# Check for warning in the generated code
generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read()
2011-12-22 05:45:33 +04:00
if self.emcc_args is None or self.emcc_args == []: # Optimized code is missing the warning comments
assert 'Casting a function pointer type to another with a different number of arguments.' in generated, 'Missing expected warning'
assert 'void (i32, i32)* ==> void (%struct.point.0*)*' in generated, 'Missing expected warning details'
return
raise Exception('We should not have gotten to here!')
2010-09-05 03:46:11 +04:00
def test_stdlibs(self):
if Settings.USE_TYPED_ARRAYS == 2:
# Typed arrays = 2 + safe heap prints a warning that messes up our output.
Settings.SAFE_HEAP = 0
2010-09-05 08:39:06 +04:00
src = '''
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
2010-09-05 08:39:06 +04:00
void clean()
{
printf("*cleaned*\\n");
}
2010-12-05 00:52:23 +03:00
int comparer(const void *a, const void *b) {
int aa = *((int*)a);
int bb = *((int*)b);
return aa - bb;
}
2010-09-05 08:39:06 +04:00
int main() {
2010-12-05 00:52:23 +03:00
// timeofday
timeval t;
gettimeofday(&t, NULL);
2010-12-05 00:52:23 +03:00
printf("*%d,%d\\n", int(t.tv_sec), int(t.tv_usec)); // should not crash
// atexit
2010-09-05 08:39:06 +04:00
atexit(clean);
2010-12-05 00:52:23 +03:00
// 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]);
2010-12-11 09:59:38 +03:00
printf("*stdin==0:%d*\\n", stdin == 0); // check that external values are at least not NULL
2010-12-12 08:29:03 +03:00
printf("*%%*\\n");
printf("*%.1ld*\\n", 5);
2010-12-11 09:59:38 +03:00
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));
2011-11-17 08:51:15 +04:00
printf("*malloc(0)!=0:%d*\\n", malloc(0) != 0); // We should not fail horribly
2010-09-05 08:39:06 +04:00
return 0;
}
'''
2011-11-17 08:51:15 +04:00
self.do_run(src, '*1,2,3,5,5,6*\n*stdin==0:0*\n*%*\n*5*\n*66.0*\n*10*\n*0*\n*-10*\n*18*\n*10*\n*0*\n*4294967286*\n*malloc(0)!=0:1*\n*cleaned*')
2010-09-05 08:39:06 +04:00
2011-12-21 08:53:27 +04:00
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)
2011-07-03 08:36:10 +04:00
def test_time(self):
2011-11-13 23:18:51 +04:00
# XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again.
2011-07-03 08:36:10 +04:00
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,
2011-10-06 21:27:55 +04:00
extra_emscripten_args=['-H', 'libc/time.h'])
#extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'])
2011-07-03 08:36:10 +04:00
2010-09-09 07:49:49 +04:00
def test_statics(self):
# static initializers save i16 but load i8 for some reason
if Settings.SAFE_HEAP:
Settings.SAFE_HEAP = 3
Settings.SAFE_HEAP_LINES = ['src.cpp:19', 'src.cpp:26']
2010-09-09 07:49:49 +04:00
src = '''
#include <stdio.h>
#include <string.h>
#define CONSTRLEN 32
void conoutfv(const char *fmt)
{
static char buf[CONSTRLEN];
strcpy(buf, fmt);
puts(buf);
}
2010-10-25 02:43:08 +04:00
struct XYZ {
float x, y, z;
XYZ(float a, float b, float c) : x(a), y(b), z(c) { }
static const XYZ& getIdentity()
{
static XYZ iT(1,2,3);
return iT;
}
};
struct S {
static const XYZ& getIdentity()
{
static const XYZ iT(XYZ::getIdentity());
return iT;
}
};
2010-09-09 07:49:49 +04:00
int main() {
conoutfv("*staticccz*");
2010-10-25 02:43:08 +04:00
printf("*%.2f,%.2f,%.2f*\\n", S::getIdentity().x, S::getIdentity().y, S::getIdentity().z);
2010-09-09 07:49:49 +04:00
return 0;
}
'''
self.do_run(src, '*staticccz*\n*1.00,2.00,3.00*')
2010-09-09 07:49:49 +04:00
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.
src = '''
#include <stdio.h>
#include <math.h>
2011-04-22 18:53:31 +04:00
#include <string.h>
struct vec {
double x,y,z;
vec() : x(0), y(0), z(0) { };
vec(const double a, const double b, const double c) : x(a), y(b), z(c) { };
};
struct basis {
vec a, b, c;
basis(const vec& v) {
a=v; // should not touch b!
printf("*%.2f,%.2f,%.2f*\\n", b.x, b.y, b.z);
}
};
int main() {
basis B(vec(1,0,0));
2011-04-22 18:53:31 +04:00
// Part 2: similar problem with memset and memmove
int x = 1, y = 77, z = 2;
memset((void*)&x, 0, sizeof(int));
memset((void*)&z, 0, sizeof(int));
printf("*%d,%d,%d*\\n", x, y, z);
memcpy((void*)&x, (void*)&z, sizeof(int));
memcpy((void*)&z, (void*)&x, sizeof(int));
printf("*%d,%d,%d*\\n", x, y, z);
memmove((void*)&x, (void*)&z, sizeof(int));
memmove((void*)&z, (void*)&x, sizeof(int));
printf("*%d,%d,%d*\\n", x, y, z);
return 0;
}
'''
self.do_run(src, '*0.00,0.00,0.00*\n*0,77,0*\n*0,77,0*\n*0,77,0*')
def test_memcpy(self):
src = '''
#include <stdio.h>
#include <string.h>
#define MAXX 48
void reset(unsigned char *buffer) {
for (int i = 0; i < MAXX; i++) buffer[i] = i+1;
}
void dump(unsigned char *buffer) {
for (int i = 0; i < MAXX-1; i++) printf("%2d,", buffer[i]);
printf("%d\\n", buffer[MAXX-1]);
}
int main() {
unsigned char buffer[MAXX];
for (int i = MAXX/4; i < MAXX-MAXX/4; i++) {
for (int j = MAXX/4; j < MAXX-MAXX/4; j++) {
for (int k = 1; k < MAXX/4; k++) {
if (i == j) continue;
if (i < j && i+k > j) continue;
if (j < i && j+k > i) continue;
printf("[%d,%d,%d] ", i, j, k);
reset(buffer);
memcpy(buffer+i, buffer+j, k);
dump(buffer);
}
}
}
return 0;
}
'''
self.do_run(src)
2011-12-08 23:44:30 +04:00
def test_bsearch(self):
2011-12-24 06:35:19 +04:00
if Settings.QUANTUM_SIZE == 1: return self.skip('Test cannot work with q1')
2011-12-08 23:44:30 +04:00
src = '''
#include <stdlib.h>
#include <stdio.h>
int cmp(const void* key, const void* member) {
return *(int *)key - *(int *)member;
}
void printResult(int* needle, int* haystack, unsigned int len) {
void *result = bsearch(needle, haystack, len, sizeof(unsigned int), cmp);
if (result == NULL) {
printf("null\\n");
} else {
printf("%d\\n", *(unsigned int *)result);
}
}
int main() {
int a[] = { -2, -1, 0, 6, 7, 9 };
int b[] = { 0, 1 };
/* Find all keys that exist. */
for(int i = 0; i < 6; i++) {
int val = a[i];
printResult(&val, a, 6);
}
/* Keys that are covered by the range of the array but aren't in
* the array cannot be found.
*/
int v1 = 3;
int v2 = 8;
printResult(&v1, a, 6);
printResult(&v2, a, 6);
/* Keys outside the range of the array cannot be found. */
int v3 = -1;
int v4 = 2;
printResult(&v3, b, 2);
printResult(&v4, b, 2);
return 0;
}
'''
self.do_run(src, '-2\n-1\n0\n6\n7\n9\nnull\nnull\nnull\nnull')
def test_nestedstructs(self):
src = '''
#include <stdio.h>
#include "emscripten.h"
struct base {
int x;
float y;
union {
int a;
float b;
};
char c;
};
struct hashtableentry {
int key;
base data;
};
struct hashset {
typedef hashtableentry entry;
struct chain { entry elem; chain *next; };
// struct chainchunk { chain chains[100]; chainchunk *next; };
};
struct hashtable : hashset {
hashtable() {
base *b = NULL;
entry *e = NULL;
chain *c = NULL;
printf("*%d,%d,%d,%d,%d,%d|%d,%d,%d,%d,%d,%d,%d,%d|%d,%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n",
2011-04-22 18:53:31 +04:00
sizeof(base),
int(&(b->x)), int(&(b->y)), int(&(b->a)), int(&(b->b)), int(&(b->c)),
2011-04-22 18:53:31 +04:00
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)),
2011-04-22 18:53:31 +04:00
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])),
2011-04-22 18:53:31 +04:00
int(&(b->last)), int(&(b->laster)), int(&(b->laster2)), sizeof(B));
// Part 3 - bitfields, and small structures
Bits *b2 = NULL;
2011-04-22 18:53:31 +04:00
printf("*%d*\\n", sizeof(Bits));
return 0;
}
'''
if Settings.QUANTUM_SIZE == 1:
2011-04-22 18:53:31 +04:00
# Compressed memory. Note that sizeof() does give the fat sizes, however!
self.do_run(src, '*16,0,1,2,2,3|20,0,1,1,2,3,3,4|24,0,5,0,1,1,2,3,3,4*\n*0,0,0,1,2,62,63,64,72*\n*2*')
else:
# Bloated memory; same layout as C/C++
self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*')
def test_runtimelink(self):
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')
self.banned_js_engines = [NODE_JS] # node's global scope behaves differently than everything else, needs investigation FIXME
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"
extern void mainFunc(int x);
extern int mainInt;
void suppFunc(struct point &p) {
printf("supp: %d,%d\n", p.x, p.y);
mainFunc(p.x+p.y);
printf("supp see: %d\n", mainInt);
}
int suppInt = 76;
'''
supp_name = os.path.join(self.get_dir(), 'supp.c')
open(supp_name, 'w').write(supp)
main = r'''
#include <stdio.h>
#include "header.h"
extern void suppFunc(struct point &p);
extern int suppInt;
void mainFunc(int x) {
printf("main: %d\n", x);
}
int mainInt = 543;
int main( int argc, const char *argv[] ) {
struct point p = { 54, 2 };
suppFunc(p);
printf("main see: %d\nok.\n", suppInt);
return 0;
}
'''
Settings.BUILD_AS_SHARED_LIB = 2
dirname = self.get_dir()
self.build(supp, dirname, supp_name)
shutil.move(supp_name + '.o.js', os.path.join(dirname, '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.')
2011-06-26 05:57:06 +04:00
def test_dlfcn_basic(self):
lib_src = '''
#include <cstdio>
class Foo {
public:
Foo() {
printf("Constructing lib object.\\n");
}
};
Foo global;
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
Settings.BUILD_AS_SHARED_LIB = 1
2011-06-26 05:57:06 +04:00
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
2011-06-26 05:57:06 +04:00
src = '''
#include <cstdio>
#include <dlfcn.h>
class Bar {
public:
Bar() {
printf("Constructing main object.\\n");
}
};
Bar global;
int main() {
dlopen("liblib.so", RTLD_NOW);
return 0;
}
'''
Settings.BUILD_AS_SHARED_LIB = 0
add_pre_run_and_checks = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
"FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
)
open(filename, 'w').write(src)
'''
self.do_run(src, 'Constructing main object.\nConstructing lib object.\n',
post_build=add_pre_run_and_checks)
2011-06-26 05:57:06 +04:00
def test_dlfcn_qsort(self):
2011-11-12 07:07:55 +04:00
if Settings.USE_TYPED_ARRAYS == 2:
Settings.CORRECT_SIGNS = 1 # Needed for unsafe optimizations
2011-06-26 05:57:06 +04:00
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() {
2011-06-26 05:57:06 +04:00
return lib_cmp;
}
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
Settings.BUILD_AS_SHARED_LIB = 1
Settings.EXPORTED_FUNCTIONS = ['_get_cmp']
2011-06-26 05:57:06 +04:00
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
2011-06-26 05:57:06 +04:00
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)();
2011-06-26 06:39:05 +04:00
CMP_TYPE lib_cmp_ptr;
2011-06-26 05:57:06 +04:00
int arr[5] = {4, 2, 5, 1, 3};
lib_handle = dlopen("liblib.so", RTLD_NOW);
if (lib_handle == NULL) {
printf("Could not load lib.\\n");
return 1;
}
getter_ptr = (CMP_TYPE (*)()) dlsym(lib_handle, "get_cmp");
2011-06-26 05:57:06 +04:00
if (getter_ptr == NULL) {
printf("Could not find func.\\n");
return 1;
}
2011-06-26 06:39:05 +04:00
lib_cmp_ptr = getter_ptr();
2011-06-26 05:57:06 +04:00
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");
2011-06-26 06:39:05 +04:00
qsort((void*)arr, 5, sizeof(int), lib_cmp_ptr);
2011-06-26 05:57:06 +04:00
printf("Sort with lib comparison: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\\n");
return 0;
}
'''
Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
add_pre_run_and_checks = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
"FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
)
open(filename, 'w').write(src)
'''
self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *',
output_nicerizer=lambda x: x.replace('\n', '*'),
post_build=add_pre_run_and_checks)
2011-06-26 05:57:06 +04:00
def test_dlfcn_data_and_fptr(self):
2011-10-27 07:41:51 +04:00
if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
2011-06-26 05:57:06 +04:00
lib_src = '''
#include <stdio.h>
int global = 42;
extern void parent_func(); // a function that is defined in the parent
2011-06-26 05:57:06 +04:00
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();
2011-06-26 05:57:06 +04:00
}
extern "C" void (*func(int x, void(*fptr)()))() {
2011-06-26 05:57:06 +04:00
printf("In func: %d\\n", x);
fptr();
return lib_fptr;
}
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
Settings.BUILD_AS_SHARED_LIB = 1
Settings.EXPORTED_FUNCTIONS = ['_func']
Settings.EXPORTED_GLOBALS = ['_global']
2011-06-26 05:57:06 +04:00
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
2011-06-26 05:57:06 +04:00
src = '''
#include <stdio.h>
#include <dlfcn.h>
typedef void (*FUNCTYPE(int, void(*)()))();
FUNCTYPE func;
void parent_func() {
printf("parent_func called from child\\n");
}
2011-06-26 05:57:06 +04:00
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");
2011-08-02 11:23:41 +04:00
// Load twice to test cache.
func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func");
2011-06-26 05:57:06 +04:00
if (func_fptr == NULL) {
printf("Could not find func.\\n");
return 1;
}
// Test passing function pointers across module bounds.
void (*fptr)() = func_fptr(13, main_fptr);
fptr();
// Test global data.
int* global = (int*) dlsym(lib_handle, "global");
if (global == NULL) {
printf("Could not find global.\\n");
return 1;
}
printf("Var: %d\\n", *global);
return 0;
}
'''
Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
Settings.EXPORTED_GLOBALS = []
add_pre_run_and_checks = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
"FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
)
open(filename, 'w').write(src)
'''
self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*',
output_nicerizer=lambda x: x.replace('\n', '*'),
post_build=add_pre_run_and_checks)
2011-06-26 05:57:06 +04:00
def test_dlfcn_alias(self):
if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize away stuff we expect from the shared library')
lib_src = r'''
#include <stdio.h>
extern int parent_global;
extern "C" void func() {
printf("Parent global: %d.\n", parent_global);
}
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
Settings.BUILD_AS_SHARED_LIB = 1
Settings.EXPORTED_FUNCTIONS = ['_func']
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
src = r'''
#include <dlfcn.h>
int parent_global = 123;
int main() {
void* lib_handle;
void (*fptr)();
lib_handle = dlopen("liblib.so", RTLD_NOW);
fptr = (void (*)())dlsym(lib_handle, "func");
fptr();
parent_global = 456;
fptr();
return 0;
}
'''
Settings.BUILD_AS_SHARED_LIB = 0
Settings.INCLUDE_FULL_LIBRARY = 1
Settings.EXPORTED_FUNCTIONS = ['_main']
add_pre_run_and_checks = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
"FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
)
open(filename, 'w').write(src)
'''
self.do_run(src, 'Parent global: 123.*Parent global: 456.*',
output_nicerizer=lambda x: x.replace('\n', '*'),
post_build=add_pre_run_and_checks,
extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h'])
Settings.INCLUDE_FULL_LIBRARY = 0
def test_dlfcn_varargs(self):
if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize things that prevent shared objects from working')
if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this')
lib_src = r'''
void print_ints(int n, ...);
extern "C" void func() {
print_ints(2, 13, 42);
}
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
Settings.BUILD_AS_SHARED_LIB = 1
Settings.EXPORTED_FUNCTIONS = ['_func']
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
src = r'''
#include <stdarg.h>
#include <stdio.h>
#include <dlfcn.h>
void print_ints(int n, ...) {
va_list args;
va_start(args, n);
for (int i = 0; i < n; i++) {
printf("%d\n", va_arg(args, int));
}
va_end(args);
}
int main() {
void* lib_handle;
void (*fptr)();
print_ints(2, 100, 200);
lib_handle = dlopen("liblib.so", RTLD_NOW);
fptr = (void (*)())dlsym(lib_handle, "func");
fptr();
return 0;
}
'''
Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
add_pre_run_and_checks = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-21 09:28:08 +04:00
"FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
)
open(filename, 'w').write(src)
'''
self.do_run(src, '100\n200\n13\n42\n',
post_build=add_pre_run_and_checks)
def test_rand(self):
src = r'''
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("%d\n", rand());
printf("%d\n", rand());
srand(123);
printf("%d\n", rand());
printf("%d\n", rand());
srand(123);
printf("%d\n", rand());
printf("%d\n", rand());
unsigned state = 0;
int r;
r = rand_r(&state);
printf("%d, %u\n", r, state);
r = rand_r(&state);
printf("%d, %u\n", r, state);
state = 0;
r = rand_r(&state);
printf("%d, %u\n", r, state);
return 0;
}
'''
expected = '''
1250496027
1116302336
440917656
1476150784
440917656
1476150784
12345, 12345
1406932606, 3554416254
12345, 12345
'''
self.do_run(src, re.sub(r'(^|\n)\s+', r'\1', expected))
def test_strtod(self):
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("1", &endptr));
printf("%g\n", strtod("1.", &endptr));
printf("%g\n", strtod("1.0", &endptr));
printf("%g\n", strtod("123", &endptr));
printf("%g\n", strtod("123.456", &endptr));
printf("%g\n", strtod("-123.456", &endptr));
printf("%g\n", strtod("1234567891234567890", &endptr));
printf("%g\n", strtod("1234567891234567890e+50", &endptr));
printf("%g\n", strtod("84e+220", &endptr));
printf("%g\n", strtod("123e-50", &endptr));
printf("%g\n", strtod("123e-250", &endptr));
printf("%g\n", strtod("123e-450", &endptr));
char str[] = " 12.34e56end";
printf("%g\n", strtod(str, &endptr));
printf("%d\n", endptr - str);
2011-12-08 02:19:19 +04:00
printf("%g\n", strtod("84e+420", &endptr));
return 0;
}
'''
expected = '''
0
0
0
1
1
1
123
123.456
-123.456
1.23457e+18
1.23457e+68
8.4e+221
1.23e-48
1.23e-248
0
1.234e+57
10
2011-12-08 02:19:19 +04:00
inf
'''
2011-12-08 02:19:19 +04:00
self.do_run(src, re.sub(r'\n\s+', '\n', expected))
def test_strtok(self):
src = r'''
#include<stdio.h>
#include<string.h>
int main() {
char test[80], blah[80];
char *sep = "\\/:;=-";
char *word, *phrase, *brkt, *brkb;
strcpy(test, "This;is.a:test:of=the/string\\tokenizer-function.");
for (word = strtok_r(test, sep, &brkt); word; word = strtok_r(NULL, sep, &brkt)) {
strcpy(blah, "blah:blat:blab:blag");
for (phrase = strtok_r(blah, sep, &brkb); phrase; phrase = strtok_r(NULL, sep, &brkb)) {
printf("at %s:%s\n", word, phrase);
}
}
return 1;
}
'''
expected = '''at This:blah
at This:blat
at This:blab
at This:blag
at is.a:blah
at is.a:blat
at is.a:blab
at is.a:blag
at test:blah
at test:blat
at test:blab
at test:blag
at of:blah
at of:blat
at of:blab
at of:blag
at the:blah
at the:blat
at the:blab
at the:blag
at string:blah
at string:blat
at string:blab
at string:blag
at tokenizer:blah
at tokenizer:blat
at tokenizer:blab
at tokenizer:blag
at function.:blah
at function.:blat
at function.:blab
at function.:blag
'''
self.do_run(src, expected)
def test_parseInt(self):
2011-12-10 09:20:19 +04:00
if Settings.QUANTUM_SIZE == 1: return self.skip('Q1 and I64_1 do not mix well yet')
Settings.I64_MODE = 1 # Necessary to prevent i64s being truncated into i32s, but we do still get doubling
# FIXME: The output here is wrong, due to double rounding of i64s!
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_printf(self):
2011-12-10 09:20:19 +04:00
if Settings.QUANTUM_SIZE == 1: return self.skip('Q1 and I64_1 do not mix well yet')
Settings.I64_MODE = 1
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()
2011-12-10 06:51:38 +04:00
expected = [open(path_from_root('tests', 'printf', 'output.txt'), 'r').read(),
open(path_from_root('tests', 'printf', 'output_i64_1.txt'), 'r').read()]
self.do_run(src, expected)
def test_printf_types(self):
src = r'''
#include <stdio.h>
int main() {
char c = '1';
short s = 2;
int i = 3;
long long l = 4;
float f = 5.5;
double d = 6.6;
printf("%c,%hd,%d,%lld,%.1f,%.1llf\n", c, s, i, l, f, d);
return 0;
}
'''
self.do_run(src, '1,2,3,4,5.5,6.6\n')
def test_vprintf(self):
src = r'''
#include <stdio.h>
#include <stdarg.h>
void print(char* format, ...) {
va_list args;
va_start (args, format);
vprintf (format, args);
va_end (args);
}
int main () {
print("Call with %d variable argument.\n", 1);
print("Call with %d variable %s.\n", 2, "arguments");
return 0;
}
'''
expected = '''
Call with 1 variable argument.
Call with 2 variable arguments.
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
2011-11-03 05:21:55 +04:00
def test_atoi(self):
src = r'''
#include <stdio.h>
#include <stdlib.h>
int main () {
printf("%d\n", atoi(""));
printf("%d\n", atoi("a"));
printf("%d\n", atoi(" b"));
printf("%d\n", atoi(" c "));
printf("%d\n", atoi("6"));
printf("%d\n", atoi(" 5"));
printf("%d\n", atoi("4 "));
printf("%d\n", atoi("3 6"));
printf("%d\n", atoi(" 3 7"));
printf("%d\n", atoi("9 d"));
printf("%d\n", atoi(" 8 e"));
return 0;
}
'''
self.do_run(src)
2011-11-03 02:36:36 +04:00
def test_sscanf(self):
src = r'''
#include <stdio.h>
#include <string.h>
2011-12-16 09:58:59 +04:00
#include <stdlib.h>
2011-11-03 02:36:36 +04:00
int main () {
#define CHECK(str) \
{ \
char name[1000]; \
memset(name, 0, 1000); \
int prio = 99; \
sscanf(str, "%s %d", name, &prio); \
printf("%s : %d\n", name, prio); \
}
CHECK("en-us 2");
CHECK("en-r");
CHECK("en 3");
2011-12-16 09:58:59 +04:00
printf("%f, %f\n", atof("1.234567"), atof("cheez"));
2011-11-03 02:36:36 +04:00
return 0;
}
'''
self.do_run(src)
# Part 2: doubles (issue 148)
if Settings.USE_TYPED_ARRAYS == 2:
for ftype in ['float', 'double']:
src = r'''
#include <stdio.h>
int main(){
char strval1[] = "1.2345678901";
char strval2[] = "1.23456789e5";
char strval3[] = "1.23456789E5";
char strval4[] = "1.2345678e-5";
char strval5[] = "1.2345678E-5";
double dblval = 1.2345678901;
double tstval;
sscanf(strval1, "%lf", &tstval);
if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval);
else printf("Pass: %lf %lf\n", tstval, dblval);
sscanf(strval2, "%lf", &tstval);
dblval = 123456.789;
if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval);
else printf("Pass: %lf %lf\n", tstval, dblval);
sscanf(strval3, "%lf", &tstval);
dblval = 123456.789;
if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval);
else printf("Pass: %lf %lf\n", tstval, dblval);
sscanf(strval4, "%lf", &tstval);
dblval = 0.000012345678;
if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval);
else printf("Pass: %lf %lf\n", tstval, dblval);
sscanf(strval5, "%lf", &tstval);
dblval = 0.000012345678;
if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval);
else printf("Pass: %lf %lf\n", tstval, dblval);
return 0;
}
'''
if ftype == 'float':
self.do_run(src.replace('%lf', '%f').replace('double', 'float'), '''Pass: 1.234568 1.234568
Pass: 123456.789063 123456.789063
Pass: 123456.789063 123456.789063
Pass: 0.000012 0.000012
Pass: 0.000012 0.000012''')
else:
self.do_run(src)
2011-08-17 08:44:30 +04:00
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'])
2011-08-17 08:44:30 +04:00
def test_files(self):
if self.emcc_args is not None and '-O2' in self.emcc_args:
self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff
Settings.CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both.
post = '''
def process(filename):
src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours
'// {{PRE_RUN_ADDITIONS}}',
\'\'\'
FS.createDataFile('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory
FS.createLazyFile('/', 'test.file', 'test.file', true, false);
FS.root.write = true;
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, '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()
2011-12-21 07:53:06 +04:00
self.do_run(src, 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\n',
2011-10-06 04:41:21 +04:00
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_folders(self):
add_pre_run = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
\'\'\'
FS.createFolder('/', 'test', true, false);
FS.createPath('/', 'test/hello/world/', true, false);
FS.createPath('/test', 'goodbye/world/', true, false);
FS.createPath('/test/goodbye', 'noentry', false, false);
FS.createDataFile('/test', 'freeforall.ext', 'abc', true, true);
FS.createDataFile('/test', 'restricted.ext', 'def', false, false);
\'\'\'
)
open(filename, 'w').write(src)
'''
src = r'''
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
int main() {
struct dirent *e;
// Basic correct behaviour.
DIR* d = opendir("/test");
printf("--E: %d\n", errno);
while ((e = readdir(d))) puts(e->d_name);
printf("--E: %d\n", errno);
// Empty folder; tell/seek.
puts("****");
d = opendir("/test/hello/world/");
e = readdir(d);
puts(e->d_name);
int pos = telldir(d);
e = readdir(d);
puts(e->d_name);
seekdir(d, pos);
e = readdir(d);
puts(e->d_name);
// Errors.
puts("****");
printf("--E: %d\n", errno);
d = opendir("/test/goodbye/noentry");
printf("--E: %d, D: %d\n", errno, d);
d = opendir("/i/dont/exist");
printf("--E: %d, D: %d\n", errno, d);
d = opendir("/test/freeforall.ext");
printf("--E: %d, D: %d\n", errno, d);
while ((e = readdir(d))) puts(e->d_name);
printf("--E: %d\n", errno);
return 0;
}
'''
expected = '''
--E: 0
.
..
hello
goodbye
freeforall.ext
restricted.ext
--E: 0
****
.
..
..
****
--E: 0
--E: 13, D: 0
--E: 2, D: 0
--E: 20, D: 0
--E: 9
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run)
2011-07-18 03:19:28 +04:00
def test_stat(self):
add_pre_run = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
\'\'\'
var f1 = FS.createFolder('/', 'test', true, true);
var f2 = FS.createDataFile(f1, 'file', 'abcdef', true, true);
var f3 = FS.createLink(f1, 'link', 'file', true, true);
var f4 = FS.createDevice(f1, 'device', function(){}, function(){});
f1.timestamp = f2.timestamp = f3.timestamp = f4.timestamp = new Date(1200000000000);
\'\'\'
)
open(filename, 'w').write(src)
'''
2011-07-18 03:19:28 +04:00
src = open(path_from_root('tests', 'stat', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'stat', 'output.txt'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
2011-07-20 11:20:09 +04:00
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)
'''
2011-07-20 11:20:09 +04:00
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'])
2011-07-20 11:20:09 +04:00
def test_fcntl_open(self):
add_pre_run = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
\'\'\'
FS.createDataFile('/', 'test-file', 'abcdef', true, true);
FS.createFolder('/', 'test-folder', true, true);
FS.root.write = true;
\'\'\'
)
open(filename, 'w').write(src)
'''
2011-07-20 11:20:09 +04:00
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, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
2011-07-20 11:20:09 +04:00
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)
'''
2011-07-20 11:20:09 +04:00
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'])
2011-07-18 03:19:28 +04:00
2011-07-21 05:08:42 +04:00
def test_poll(self):
add_pre_run = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
\'\'\'
FS.createDataFile('/', 'file', 'abcdef', true, true);
FS.createDevice('/', 'device', function() {}, function() {});
\'\'\'
)
open(filename, 'w').write(src)
'''
2011-07-21 05:08:42 +04:00
src = r'''
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
int main() {
struct pollfd multi[5];
multi[0].fd = open("/file", O_RDONLY, 0777);
multi[1].fd = open("/device", O_RDONLY, 0777);
multi[2].fd = 123;
multi[3].fd = open("/file", O_RDONLY, 0777);
multi[4].fd = open("/file", O_RDONLY, 0777);
multi[0].events = POLLIN | POLLOUT | POLLNVAL | POLLERR;
multi[1].events = POLLIN | POLLOUT | POLLNVAL | POLLERR;
multi[2].events = POLLIN | POLLOUT | POLLNVAL | POLLERR;
multi[3].events = 0x00;
multi[4].events = POLLOUT | POLLNVAL | POLLERR;
printf("ret: %d\n", poll(multi, 5, 123));
printf("errno: %d\n", errno);
2011-10-02 23:48:08 +04:00
printf("multi[0].revents: %d\n", multi[0].revents == (POLLIN | POLLOUT));
printf("multi[1].revents: %d\n", multi[1].revents == (POLLIN | POLLOUT));
printf("multi[2].revents: %d\n", multi[2].revents == POLLNVAL);
printf("multi[3].revents: %d\n", multi[3].revents == 0);
printf("multi[4].revents: %d\n", multi[4].revents == POLLOUT);
2011-07-21 05:08:42 +04:00
return 0;
}
'''
expected = r'''
ret: 4
errno: 0
2011-10-02 23:48:08 +04:00
multi[0].revents: 1
multi[1].revents: 1
multi[2].revents: 1
multi[3].revents: 1
multi[4].revents: 1
2011-07-21 05:08:42 +04:00
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h,poll.h'])
2011-07-21 05:08:42 +04:00
2011-07-19 22:55:58 +04:00
def test_statvfs(self):
src = r'''
#include <stdio.h>
#include <errno.h>
#include <sys/statvfs.h>
int main() {
struct statvfs s;
printf("result: %d\n", statvfs("/test", &s));
printf("errno: %d\n", errno);
printf("f_bsize: %lu\n", s.f_bsize);
printf("f_frsize: %lu\n", s.f_frsize);
printf("f_blocks: %lu\n", s.f_blocks);
printf("f_bfree: %lu\n", s.f_bfree);
printf("f_bavail: %lu\n", s.f_bavail);
printf("f_files: %lu\n", s.f_files);
printf("f_ffree: %lu\n", s.f_ffree);
printf("f_favail: %lu\n", s.f_favail);
printf("f_fsid: %lu\n", s.f_fsid);
printf("f_flag: %lu\n", s.f_flag);
printf("f_namemax: %lu\n", s.f_namemax);
return 0;
}
'''
expected = r'''
result: 0
errno: 0
f_bsize: 4096
f_frsize: 4096
f_blocks: 1000000
f_bfree: 500000
f_bavail: 500000
f_files: 8
2011-07-19 22:55:58 +04:00
f_ffree: 1000000
f_favail: 1000000
f_fsid: 42
f_flag: 2
f_namemax: 255
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
2011-07-19 22:55:58 +04:00
def test_libgen(self):
src = r'''
#include <stdio.h>
#include <libgen.h>
int main() {
char p1[16] = "/usr/lib", p1x[16] = "/usr/lib";
printf("%s -> ", p1);
printf("%s : %s\n", dirname(p1x), basename(p1));
char p2[16] = "/usr", p2x[16] = "/usr";
printf("%s -> ", p2);
printf("%s : %s\n", dirname(p2x), basename(p2));
char p3[16] = "/usr/", p3x[16] = "/usr/";
printf("%s -> ", p3);
printf("%s : %s\n", dirname(p3x), basename(p3));
char p4[16] = "/usr/lib///", p4x[16] = "/usr/lib///";
printf("%s -> ", p4);
printf("%s : %s\n", dirname(p4x), basename(p4));
char p5[16] = "/", p5x[16] = "/";
printf("%s -> ", p5);
printf("%s : %s\n", dirname(p5x), basename(p5));
char p6[16] = "///", p6x[16] = "///";
printf("%s -> ", p6);
printf("%s : %s\n", dirname(p6x), basename(p6));
char p7[16] = "/usr/../lib/..", p7x[16] = "/usr/../lib/..";
printf("%s -> ", p7);
printf("%s : %s\n", dirname(p7x), basename(p7));
char p8[16] = "", p8x[16] = "";
printf("(empty) -> %s : %s\n", dirname(p8x), basename(p8));
printf("(null) -> %s : %s\n", dirname(0), basename(0));
return 0;
}
'''
expected = '''
/usr/lib -> /usr : lib
/usr -> / : usr
/usr/ -> / : usr
/usr/lib/// -> /usr : lib
/ -> / : /
/// -> / : /
/usr/../lib/.. -> /usr/../lib : ..
(empty) -> . : .
(null) -> . : .
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
2011-07-16 16:01:43 +04:00
def test_utime(self):
add_pre_run_and_checks = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
\'\'\'
var TEST_F1 = FS.createFolder('/', 'writeable', true, true);
var TEST_F2 = FS.createFolder('/', 'unwriteable', true, false);
\'\'\'
).replace(
'// {{POST_RUN_ADDITIONS}}',
\'\'\'
print('first changed: ' + (TEST_F1.timestamp == 1200000000000));
print('second changed: ' + (TEST_F2.timestamp == 1200000000000));
\'\'\'
)
open(filename, 'w').write(src)
'''
2011-07-16 16:01:43 +04:00
src = r'''
#include <stdio.h>
#include <errno.h>
#include <utime.h>
int main() {
struct utimbuf t = {1000000000, 1200000000};
char* writeable = "/writeable";
char* unwriteable = "/unwriteable";
utime(writeable, &t);
printf("writeable errno: %d\n", errno);
utime(unwriteable, &t);
printf("unwriteable errno: %d\n", errno);
return 0;
}
'''
expected = '''
writeable errno: 0
unwriteable errno: 1
first changed: true
second changed: false
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run_and_checks)
2011-07-16 16:01:43 +04:00
def test_fs_base(self):
Settings.INCLUDE_FULL_LIBRARY = 1
try:
addJS = '''
def process(filename):
2011-12-21 09:42:12 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours
'// {{PRE_RUN_ADDITIONS}}',
2011-12-21 09:42:12 +04:00
open(shared.path_from_root('tests', 'filesystem', 'src.js'), 'r').read())
open(filename, 'w').write(src)
'''
src = 'int main() {return 0;}\n'
expected = open(path_from_root('tests', 'filesystem', 'output.txt'), 'r').read()
self.do_run(src, expected, post_build=addJS, extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'])
finally:
Settings.INCLUDE_FULL_LIBRARY = 0
def test_unistd_access(self):
add_pre_run = '''
def process(filename):
2011-12-22 02:13:35 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-22 02:13:35 +04:00
open(shared.path_from_root('tests', 'unistd', 'access.js'), 'r').read()
)
open(filename, 'w').write(src)
'''
src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_curdir(self):
add_pre_run = '''
def process(filename):
2011-12-22 02:13:35 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-22 02:13:35 +04:00
open(shared.path_from_root('tests', 'unistd', 'curdir.js'), 'r').read()
)
open(filename, 'w').write(src)
'''
src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_close(self):
src = open(path_from_root('tests', 'unistd', 'close.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'close.out'), 'r').read()
self.do_run(src, expected)
def test_unistd_confstr(self):
src = open(path_from_root('tests', 'unistd', 'confstr.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'confstr.out'), 'r').read()
self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/unistd.h'])
def test_unistd_ttyname(self):
add_pre_run = '''
def process(filename):
2011-12-22 02:13:35 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-22 02:13:35 +04:00
open(shared.path_from_root('tests', 'unistd', 'ttyname.js'), 'r').read()
)
open(filename, 'w').write(src)
'''
src = open(path_from_root('tests', 'unistd', 'ttyname.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'ttyname.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_dup(self):
src = open(path_from_root('tests', 'unistd', 'dup.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'dup.out'), 'r').read()
self.do_run(src, expected)
def test_unistd_pathconf(self):
src = open(path_from_root('tests', 'unistd', 'pathconf.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'pathconf.out'), 'r').read()
self.do_run(src, expected)
def test_unistd_truncate(self):
add_pre_run = '''
def process(filename):
2011-12-22 02:13:35 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-22 02:13:35 +04:00
open(shared.path_from_root('tests', 'unistd', 'truncate.js'), 'r').read()
)
open(filename, 'w').write(src)
'''
src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_swab(self):
src = open(path_from_root('tests', 'unistd', 'swab.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'swab.out'), 'r').read()
self.do_run(src, expected)
def test_unistd_isatty(self):
add_pre_run = '''
def process(filename):
2011-12-22 02:13:35 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-22 02:13:35 +04:00
open(shared.path_from_root('tests', 'unistd', 'isatty.js'), 'r').read()
)
open(filename, 'w').write(src)
'''
src = open(path_from_root('tests', 'unistd', 'isatty.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'isatty.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
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):
add_pre_run = '''
def process(filename):
2011-12-22 02:13:35 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-22 02:13:35 +04:00
open(shared.path_from_root('tests', 'unistd', 'unlink.js'), 'r').read()
)
open(filename, 'w').write(src)
'''
src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'unlink.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_links(self):
add_pre_run = '''
def process(filename):
2011-12-22 02:13:35 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-22 02:13:35 +04:00
open(shared.path_from_root('tests', 'unistd', 'links.js'), 'r').read()
)
open(filename, 'w').write(src)
'''
src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_sleep(self):
src = open(path_from_root('tests', 'unistd', 'sleep.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'sleep.out'), 'r').read()
self.do_run(src, expected)
def test_unistd_io(self):
add_pre_run = '''
def process(filename):
2011-12-22 02:13:35 +04:00
import tools.shared as shared
src = open(filename, 'r').read().replace(
'// {{PRE_RUN_ADDITIONS}}',
2011-12-22 02:13:35 +04:00
open(shared.path_from_root('tests', 'unistd', 'io.js'), 'r').read()
)
open(filename, 'w').write(src)
'''
src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_misc(self):
src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'misc.out'), 'r').read()
self.do_run(src, expected)
2011-07-29 15:23:37 +04:00
def test_uname(self):
src = r'''
#include <stdio.h>
#include <sys/utsname.h>
int main() {
struct utsname u;
printf("ret: %d\n", uname(&u));
printf("sysname: %s\n", u.sysname);
printf("nodename: %s\n", u.nodename);
printf("release: %s\n", u.release);
printf("version: %s\n", u.version);
printf("machine: %s\n", u.machine);
printf("invalid: %d\n", uname(0));
return 0;
}
'''
expected = '''
ret: 0
sysname: Emscripten
nodename: emscripten
release: 1.0
version: #1
machine: x86-JS
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
2011-07-29 15:23:37 +04:00
def test_env(self):
src = open(path_from_root('tests', 'env', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'env', 'output.txt'), 'r').read()
self.do_run(src, expected)
def test_getloadavg(self):
src = r'''
#include <stdio.h>
#include <stdlib.h>
int main() {
double load[5] = {42.13, 42.13, 42.13, 42.13, 42.13};
printf("ret: %d\n", getloadavg(load, 5));
printf("load[0]: %.3lf\n", load[0]);
printf("load[1]: %.3lf\n", load[1]);
printf("load[2]: %.3lf\n", load[2]);
printf("load[3]: %.3lf\n", load[3]);
printf("load[4]: %.3lf\n", load[4]);
return 0;
}
'''
expected = '''
ret: 3
load[0]: 0.100
load[1]: 0.100
load[2]: 0.100
load[3]: 42.130
load[4]: 42.130
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
def test_ctype(self):
# The bit fiddling done by the macros using __ctype_b_loc requires this.
Settings.CORRECT_SIGNS = 1
src = open(path_from_root('tests', 'ctype', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'ctype', 'output.txt'), 'r').read()
self.do_run(src, expected)
CORRECT_SIGNS = 0
2011-10-30 00:10:22 +04:00
# libc++ tests
2011-10-18 02:23:40 +04:00
def test_iostream(self):
src = '''
#include <iostream>
int main()
{
2011-12-17 23:00:58 +04:00
std::cout << "hello world" << std::endl << 77 << "." << std::endl;
2011-10-18 02:23:40 +04:00
return 0;
}
'''
2011-12-17 23:00:58 +04:00
self.do_run(src, 'hello world\n77.\n')
2011-10-18 02:23:40 +04:00
2011-10-30 00:10:22 +04:00
def test_stdvec(self):
src = '''
#include <vector>
#include <stdio.h>
struct S {
int a;
float b;
};
void foo(int a, float b)
{
printf("%d:%.2f\\n", a, b);
}
int main ( int argc, char *argv[] )
{
std::vector<S> ar;
S s;
s.a = 789;
s.b = 123.456f;
ar.push_back(s);
s.a = 0;
s.b = 100.1f;
ar.push_back(s);
foo(ar[0].a, ar[0].b);
foo(ar[1].a, ar[1].b);
}
'''
self.do_run(src, '789:123.46\n0:100.1')
### 'Medium' tests
2010-11-06 06:48:19 +03:00
2010-08-26 08:01:10 +04:00
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)
2010-08-26 08:01:10 +04:00
2010-09-26 08:25:47 +04:00
def test_raytrace(self):
2011-12-07 02:32:44 +04:00
if Settings.USE_TYPED_ARRAYS == 2: return self.skip('Relies on double value rounding, extremely sensitive')
2011-06-06 18:40:27 +04:00
2011-11-12 02:05:16 +04:00
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):
2010-09-24 07:21:03 +04:00
results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''),
(50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ]
2010-08-26 08:01:10 +04:00
for i, j in results:
src = open(path_from_root('tests', 'fasta.cpp'), 'r').read()
self.do_run(src, j, [str(i)], lambda x: x.replace('\n', '*'), no_build=i>1)
2010-08-26 08:01:10 +04:00
def test_dlmalloc(self):
Settings.CORRECT_SIGNS = 2
Settings.CORRECT_SIGNS_LINES = ['src.cpp:' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]]
Settings.TOTAL_MEMORY = 100*1024*1024 # needed with typed arrays
2011-10-09 00:28:01 +04:00
src = open(path_from_root('src', '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)
2011-10-09 00:28:01 +04:00
# 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)
2011-10-09 00:28:01 +04:00
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([EMCC, path_from_root('tests', 'dlmalloc_test.c'),
'-o', os.path.join(self.get_dir(), 'src.cpp.o.js')], stdout=PIPE, stderr=self.stderr_redirect).communicate()
#print output
self.do_run('x', '*1,0*', ['200', '1'], no_build=True)
self.do_run('x', '*400,0*', ['400', '400'], no_build=True)
def test_libcxx(self):
self.do_run(path_from_root('tests', 'libcxx'),
'june -> 30\nPrevious (in alphabetical order) is july\nNext (in alphabetical order) is march',
main_file='main.cpp', additional_files=['hash.cpp'])
2011-01-17 10:22:57 +03:00
2011-03-13 22:13:21 +03:00
# This will fail without using libcxx, as libstdc++ (gnu c++ lib) will use but not link in
# __ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_
# So a way to avoid that problem is to include libcxx, as done here
self.do_run('''
2011-03-13 22:13:21 +03:00
#include <set>
#include <stdio.h>
int main() {
std::set<int> *fetchOriginatorNums = new std::set<int>();
fetchOriginatorNums->insert(171);
printf("hello world\\n");
return 1;
}
''', 'hello world', includes=[path_from_root('tests', 'libcxx', 'include')]);
def test_static_variable(self):
src = '''
#include <stdio.h>
struct DATA
{
int value;
DATA()
{
value = 0;
}
};
DATA & GetData()
{
static DATA data;
return data;
}
int main()
{
GetData().value = 10;
printf( "value:%i", GetData().value );
}
'''
self.do_run(src, 'value:10')
2011-03-13 22:13:21 +03:00
def test_cubescript(self):
if self.emcc_args is not None and '-O2' in self.emcc_args:
self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage
2011-07-30 07:09:49 +04:00
Building.COMPILER_TEST_OPTS = [] # remove -g, so we have one test without it by default
Settings.SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code...
# Overflows happen in hash loop
Settings.CORRECT_OVERFLOWS = 1
Settings.CHECK_OVERFLOWS = 0
2011-11-12 08:39:57 +04:00
if Settings.USE_TYPED_ARRAYS == 2:
Settings.CORRECT_SIGNS = 1
self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp')
2010-08-30 02:30:49 +04:00
def test_gcc_unmangler(self):
self.do_run(path_from_root('third_party'), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c')
2010-10-21 09:56:12 +04:00
2010-12-19 02:55:21 +03:00
#### Code snippet that is helpful to search for nonportable optimizations ####
#global LLVM_OPT_OPTS
#for opt in ['-aa-eval', '-adce', '-always-inline', '-argpromotion', '-basicaa', '-basiccg', '-block-placement', '-break-crit-edges', '-codegenprepare', '-constmerge', '-constprop', '-correlated-propagation', '-count-aa', '-dce', '-deadargelim', '-deadtypeelim', '-debug-aa', '-die', '-domfrontier', '-domtree', '-dse', '-extract-blocks', '-functionattrs', '-globaldce', '-globalopt', '-globalsmodref-aa', '-gvn', '-indvars', '-inline', '-insert-edge-profiling', '-insert-optimal-edge-profiling', '-instcombine', '-instcount', '-instnamer', '-internalize', '-intervals', '-ipconstprop', '-ipsccp', '-iv-users', '-jump-threading', '-lazy-value-info', '-lcssa', '-lda', '-libcall-aa', '-licm', '-lint', '-live-values', '-loop-deletion', '-loop-extract', '-loop-extract-single', '-loop-index-split', '-loop-reduce', '-loop-rotate', '-loop-unroll', '-loop-unswitch', '-loops', '-loopsimplify', '-loweratomic', '-lowerinvoke', '-lowersetjmp', '-lowerswitch', '-mem2reg', '-memcpyopt', '-memdep', '-mergefunc', '-mergereturn', '-module-debuginfo', '-no-aa', '-no-profile', '-partial-inliner', '-partialspecialization', '-pointertracking', '-postdomfrontier', '-postdomtree', '-preverify', '-prune-eh', '-reassociate', '-reg2mem', '-regions', '-scalar-evolution', '-scalarrepl', '-sccp', '-scev-aa', '-simplify-libcalls', '-simplify-libcalls-halfpowr', '-simplifycfg', '-sink', '-split-geps', '-sretpromotion', '-strip', '-strip-dead-debug-info', '-strip-dead-prototypes', '-strip-debug-declare', '-strip-nondebug', '-tailcallelim', '-tailduplicate', '-targetdata', '-tbaa']:
# LLVM_OPT_OPTS = [opt]
# try:
# self.do_run(path_from_root(['third_party']), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c')
2010-12-19 02:55:21 +03:00
# print opt, "ok"
# except:
# print opt, "FAIL"
2010-11-21 07:00:11 +03:00
def test_lua(self):
2011-12-22 04:56:03 +04:00
try:
os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1'
if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work')
# Overflows in luaS_newlstr hash loop
Settings.SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type)
Settings.CORRECT_OVERFLOWS = 1
Settings.CHECK_OVERFLOWS = 0
Settings.CORRECT_SIGNS = 1 # Not sure why, but needed
Settings.INIT_STACK = 1 # TODO: Investigate why this is necessary
self.do_ll_run(path_from_root('tests', 'lua', 'lua.ll'),
'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)'''],
output_nicerizer=lambda string: string.replace('\n\n', '\n').replace('\n\n', '\n'),
extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'])
finally:
del os.environ['EMCC_LEAVE_INPUTS_RAW']
2010-11-21 05:38:44 +03:00
2011-10-25 02:20:01 +04:00
def get_build_dir(self):
2011-02-28 03:54:21 +03:00
return os.path.join(self.get_dir(), 'building')
def get_freetype(self):
Settings.INIT_STACK = 1 # TODO: Investigate why this is necessary
2011-10-27 07:16:22 +04:00
return self.get_library('freetype', os.path.join('objs', '.libs', 'libfreetype.a.bc'))
def test_freetype(self):
if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix')
2011-04-23 00:23:37 +04:00
2011-11-18 22:02:45 +04:00
if Building.LLVM_OPTS: Settings.RELOOP = 0 # Too slow; we do care about typed arrays and MICRO_OPTS though
2011-03-22 05:50:03 +03:00
if Settings.CORRECT_SIGNS == 0: Settings.CORRECT_SIGNS = 1 # Not sure why, but needed
post = '''
def process(filename):
2011-12-21 09:42:12 +04:00
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(
2011-12-21 09:42:12 +04:00
map(ord, open(shared.path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read())
)
)
open(filename, 'w').write(src)
'''
# Main
self.do_run(open(path_from_root('tests', 'freetype', 'main.c'), 'r').read(),
open(path_from_root('tests', 'freetype', 'ref.txt'), 'r').read(),
2011-02-08 22:21:58 +03:00
['font.ttf', 'test!', '150', '120', '25'],
libraries=[self.get_freetype()],
2011-01-31 18:43:01 +03:00
includes=[path_from_root('tests', 'freetype', 'include')],
2011-04-23 22:05:22 +04:00
post_build=post)
2011-04-22 18:53:31 +04:00
#build_ll_hook=self.do_autodebug)
2011-01-24 05:23:44 +03:00
2011-09-11 07:38:30 +04:00
def test_sqlite(self):
2011-09-15 00:54:42 +04:00
# gcc -O3 -I/home/alon/Dev/emscripten/tests/sqlite -ldl src.c
if Settings.QUANTUM_SIZE == 1: return self.skip('TODO FIXME')
Settings.RELOOP = 0 # too slow
2011-09-11 07:38:30 +04:00
2011-11-17 22:16:42 +04:00
pgo_data = read_pgo_data(path_from_root('tests', 'sqlite', 'sqlite-autooptimize.fails.txt'))
2011-09-13 21:29:47 +04:00
2011-12-22 02:33:06 +04:00
Settings.CORRECT_SIGNS = 1 # XXX: in default, we fail with 2 here, even though the pgo_data should be correct (and works in s_0_0). Investigate this.
2011-11-17 22:16:42 +04:00
Settings.CORRECT_SIGNS_LINES = pgo_data['signs_lines']
Settings.CORRECT_OVERFLOWS = 0
Settings.CORRECT_ROUNDINGS = 0
Settings.SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff
Settings.DISABLE_EXCEPTION_CATCHING = 1
Settings.FAST_MEMORY = 4*1024*1024
Settings.EXPORTED_FUNCTIONS = ['_main', '_sqlite3_open', '_sqlite3_close', '_sqlite3_exec', '_sqlite3_free', '_callback'];
2011-09-11 07:38:30 +04:00
Settings.INVOKE_RUN = 0 # We append code that does run() ourselves
2011-09-11 07:38:30 +04:00
post = '''
def process(filename):
src = open(filename, 'a')
src.write(\'\'\'
FS.init();
FS.root.write = true;
FS.ignorePermissions = true; // /dev is read-only
FS.createPath('/', 'dev/shm/tmp', true, true);
FS.ignorePermissions = false;
FS.currentPath = '/dev/shm/tmp';
run();
\'\'\')
src.close()
'''
2011-09-11 07:38:30 +04:00
self.do_run(r'''
2011-09-11 07:38:30 +04:00
#define SQLITE_DISABLE_LFS
#define LONGDOUBLE_TYPE double
#define SQLITE_INT64_TYPE int
2011-09-12 03:16:10 +04:00
#define SQLITE_THREADSAFE 0
2011-09-11 09:01:22 +04:00
''' + open(path_from_root('tests', 'sqlite', 'sqlite3.c'), 'r').read() +
2011-09-12 03:16:10 +04:00
open(path_from_root('tests', 'sqlite', 'benchmark.c'), 'r').read(),
open(path_from_root('tests', 'sqlite', 'benchmark.txt'), 'r').read(),
2011-09-11 07:38:30 +04:00
includes=[path_from_root('tests', 'sqlite')],
force_c=True,
2011-09-12 03:16:10 +04:00
extra_emscripten_args=['-m'],
2011-09-13 21:29:47 +04:00
js_engines=[SPIDERMONKEY_ENGINE], # V8 is slow
2011-09-12 03:16:10 +04:00
post_build=post)#,build_ll_hook=self.do_autodebug)
2011-09-11 07:38:30 +04:00
2011-02-06 08:01:26 +03:00
def test_zlib(self):
if self.emcc_args is not None and '-O2' in self.emcc_args:
self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage
Settings.CORRECT_SIGNS = 1
2011-01-31 18:43:01 +03:00
self.do_run(open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(),
2011-01-31 18:43:01 +03:00
open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
2011-10-27 07:16:22 +04:00
libraries=[self.get_library('zlib', os.path.join('libz.a.bc'), make_args=['libz.a'])],
2011-01-31 18:43:01 +03:00
includes=[path_from_root('tests', 'zlib')],
force_c=True)
2011-04-22 18:53:31 +04:00
def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long
2011-10-27 07:41:51 +04:00
if Building.LLVM_OPTS: Settings.SAFE_HEAP = 0 # Optimizations make it so we do not have debug info on the line we need to ignore
# Note: this is also a good test of per-file and per-line changes (since we have multiple files, and correct specific lines)
if Settings.SAFE_HEAP:
# Ignore bitfield warnings
Settings.SAFE_HEAP = 3
Settings.SAFE_HEAP_LINES = ['btVoronoiSimplexSolver.h:40', 'btVoronoiSimplexSolver.h:41',
'btVoronoiSimplexSolver.h:42', 'btVoronoiSimplexSolver.h:43']
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()],
2011-10-27 07:16:22 +04:00
libraries=[self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletCollision.a.bc'),
os.path.join('src', '.libs', 'libBulletDynamics.a.bc'),
os.path.join('src', '.libs', 'libLinearMath.a.bc')],
configure_args=['--disable-demos','--disable-dependency-tracking'])],
includes=[path_from_root('tests', 'bullet', 'src')],
js_engines=[SPIDERMONKEY_ENGINE]) # V8 issue 1407
2011-03-17 03:19:57 +03:00
def test_poppler(self):
if not self.emcc_args == []: return self.skip('very slow, we only do this in default')
2011-03-13 22:13:21 +03:00
Settings.SAFE_HEAP = 0 # Has variable object
2011-03-22 05:50:03 +03:00
Settings.CORRECT_OVERFLOWS = 1
Settings.CORRECT_SIGNS = 1
2011-03-22 05:50:03 +03:00
2011-10-27 07:41:51 +04:00
Building.COMPILER_TEST_OPTS += [
2011-10-08 07:56:26 +04:00
'-I' + path_from_root('tests', 'libcxx', 'include'), # Avoid libstdc++ linking issue, see libcxx test
'-I' + path_from_root('tests', 'freetype', 'include'),
'-I' + path_from_root('tests', 'poppler', 'include'),
]
2011-03-13 00:37:44 +03:00
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.ignorePermissions = true;
FS.createDataFile('/', 'paper.pdf', eval(read('paper.pdf.js')), true, false);
FS.root.write = true;
FS.ignorePermissions = false;
run();
print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) })));
\'\'\'
)
src.close()
'''
2011-03-13 00:37:44 +03:00
2011-10-27 07:16:22 +04:00
#fontconfig = self.get_library('fontconfig', [os.path.join('src', '.libs', 'libfontconfig.a')]) # Used in file, but not needed, mostly
2011-03-13 22:13:21 +03:00
freetype = self.get_freetype()
2011-10-27 07:16:22 +04:00
poppler = self.get_library('poppler',
2011-12-17 07:21:25 +04:00
[os.path.join('poppler', '.libs', 'libpoppler.so.13.0.0'),
os.path.join('goo', '.libs', 'libgoo.a.bc'),
os.path.join('fofi', '.libs', 'libfofi.a.bc'),
os.path.join('splash', '.libs', 'libsplash.a.bc'),
2011-03-13 22:13:21 +03:00
os.path.join('utils', 'pdftoppm.o'),
os.path.join('utils', 'parseargs.o')],
configure_args=['--disable-libjpeg', '--disable-libpng', '--disable-poppler-qt', '--disable-poppler-qt4', '--disable-cms'])
2011-03-13 22:13:21 +03:00
# Combine libraries
combined = os.path.join(self.get_build_dir(), 'combined.bc')
2011-10-27 07:16:22 +04:00
Building.link([freetype, poppler], combined)
2011-03-13 22:13:21 +03:00
self.do_ll_run(combined,
map(ord, open(path_from_root('tests', 'poppler', 'ref.ppm'), 'r').read()).__str__().replace(' ', ''),
2011-12-09 02:02:09 +04:00
args='-scale-to 512 paper.pdf filename'.split(' '),
post_build=post)
#, build_ll_hook=self.do_autodebug)
2011-03-13 00:37:44 +03:00
2011-02-28 03:55:53 +03:00
def test_openjpeg(self):
if Settings.USE_TYPED_ARRAYS == 2:
Settings.CORRECT_SIGNS = 1
2011-03-06 00:30:17 +03:00
else:
Settings.CORRECT_SIGNS = 2
Settings.CORRECT_SIGNS_LINES = ["mqc.c:566", "mqc.c:317"]
2011-02-28 03:55:53 +03:00
post = '''
def process(filename):
2011-12-21 09:49:17 +04:00
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}}',
2011-12-21 09:49:17 +04:00
"FS.createDataFile('/', 'image.j2k', %s, true, false);FS.root.write = true;" % shared.line_splitter(str(
map(ord, open(original_j2k, 'rb').read())
))
).replace(
'// {{POST_RUN_ADDITIONS}}',
2011-12-21 09:43:32 +04:00
"print('Data: ' + JSON.stringify(FS.root.contents['image.raw'].contents));"
)
open(filename, 'w').write(src)
'''
2011-02-28 03:54:21 +03:00
shutil.copy(path_from_root('tests', 'openjpeg', 'opj_config.h'), self.get_dir())
2011-10-27 07:16:22 +04:00
lib = self.get_library('openjpeg',
[os.path.join('bin', 'libopenjpeg.so.1.4.0'),
2011-02-28 03:55:53 +03:00
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.sep.join('codec/CMakeFiles/j2k_to_image.dir/__/common/getopt.c.o'.split('/'))],
configure=['cmake', '.'],
#configure_args=['--enable-tiff=no', '--enable-jp3d=no', '--enable-png=no'],
make_args=[]) # no -j 2, since parallel builds can fail
2011-02-28 03:54:21 +03:00
# 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):
# Get the image generated by JS, from the JSON.stringify'd array
2011-03-05 19:02:38 +03:00
m = re.search('\[[\d, -]*\]', output)
2011-03-03 18:39:33 +03:00
try:
js_data = eval(m.group(0))
except AttributeError:
print 'Failed to find proper image output in: ' + output
2011-03-05 19:02:38 +03:00
raise
js_data = map(lambda x: x if x >= 0 else 256+x, js_data) # Our output may be signed, so unsign it
2011-02-28 03:54:21 +03:00
2011-03-06 00:30:17 +03:00
# Get the correct output
true_data = open(path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.raw'), 'rb').read()
2011-02-28 03:54:21 +03:00
# Compare them
2011-03-06 00:30:17 +03:00
assert(len(js_data) == len(true_data))
2011-02-28 03:54:21 +03:00
num = len(js_data)
2011-03-06 00:30:17 +03:00
diff_total = js_total = true_total = 0
2011-02-28 03:54:21 +03:00
for i in range(num):
js_total += js_data[i]
2011-03-06 00:30:17 +03:00
true_total += ord(true_data[i])
diff_total += abs(js_data[i] - ord(true_data[i]))
2011-02-28 03:54:21 +03:00
js_mean = js_total/float(num)
2011-03-06 00:30:17 +03:00
true_mean = true_total/float(num)
2011-02-28 03:54:21 +03:00
diff_mean = diff_total/float(num)
2011-03-06 05:41:15 +03:00
image_mean = 83.265
2011-03-06 00:30:17 +03:00
#print '[image stats:', js_mean, image_mean, true_mean, diff_mean, num, ']'
2011-03-06 05:41:15 +03:00
assert abs(js_mean - image_mean) < 0.01
assert abs(true_mean - image_mean) < 0.01
assert diff_mean < 0.01
2011-02-28 03:54:21 +03:00
return output
self.do_run(open(path_from_root('tests', 'openjpeg', 'codec', 'j2k_to_image.c'), 'r').read(),
2011-02-28 03:54:21 +03:00
'Successfully generated', # The real test for valid output is in image_compare
'-i image.j2k -o image.raw'.split(' '),
2011-02-28 03:54:21 +03:00
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')],
2011-02-28 03:54:21 +03:00
force_c=True,
post_build=post,
output_nicerizer=image_compare)#, build_ll_hook=self.do_autodebug)
2011-02-28 03:54:21 +03:00
2010-12-12 00:22:09 +03:00
def test_python(self):
2011-12-07 02:32:44 +04:00
if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work')
2011-09-04 23:06:59 +04:00
# Overflows in string_hash
Settings.CORRECT_OVERFLOWS = 1
Settings.CHECK_OVERFLOWS = 0
2011-11-18 22:02:45 +04:00
Settings.RELOOP = 0 # Too slow; we do care about typed arrays and MICRO_OPTS though
Settings.SAFE_HEAP = 0 # Has bitfields which are false positives. Also the PyFloat_Init tries to detect endianness.
Settings.CORRECT_SIGNS = 1 # Not sure why, but needed
Settings.EXPORTED_FUNCTIONS = ['_main', '_PyRun_SimpleStringFlags'] # for the demo
2011-06-19 10:15:14 +04:00
self.do_ll_run(path_from_root('tests', 'python', 'python.ll'),
'hello python world!\n[0, 2, 4, 6]\n5\n22\n5.470000',
2011-08-02 07:56:51 +04:00
args=['-S', '-c' '''print "hello python world!"; print [x*2 for x in range(4)]; t=2; print 10-3-t; print (lambda x: x*2)(11); print '%f' % 5.47'''],
extra_emscripten_args=['-m'])
2010-12-12 00:22:09 +03:00
2011-07-08 04:58:35 +04:00
# 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):
try:
self.banned_js_engines = [NODE_JS] # node issue 1669, exception causes stdout not to be flushed
os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1'
Settings.CHECK_OVERFLOWS = 0
if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly")
for name in glob.glob(path_from_root('tests', 'cases', '*.ll')):
shortname = name.replace('.ll', '')
if '' not in shortname: continue
print "Testing case '%s'..." % shortname
output_file = path_from_root('tests', 'cases', shortname + '.txt')
if Settings.QUANTUM_SIZE == 1:
q1_output_file = path_from_root('tests', 'cases', shortname + '_q1.txt')
if os.path.exists(q1_output_file):
output_file = q1_output_file
if os.path.exists(output_file):
output = open(output_file, 'r').read()
else:
output = 'hello, world!'
if output.rstrip() != 'skip':
self.do_ll_run(path_from_root('tests', 'cases', name), output)
# Optional source checking, a python script that gets a global generated with the source
src_checker = path_from_root('tests', 'cases', shortname + '.py')
if os.path.exists(src_checker):
generated = open('src.cpp.o.js').read()
exec(open(src_checker).read())
2011-09-04 23:06:59 +04:00
finally:
del os.environ['EMCC_LEAVE_INPUTS_RAW']
2011-03-03 18:39:33 +03:00
# Autodebug the code
def do_autodebug(self, filename):
output = Popen(['python', AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0]
2011-03-03 18:39:33 +03:00
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)
2011-03-03 18:39:33 +03:00
2011-03-03 06:12:13 +03:00
def test_autodebug(self):
2011-10-27 07:41:51 +04:00
if Building.LLVM_OPTS: return self.skip('LLVM opts mess us up')
2011-03-03 06:12:13 +03:00
# Run a test that should work, generating some code
self.test_structs()
2011-03-03 18:39:33 +03:00
filename = os.path.join(self.get_dir(), 'src.cpp')
self.do_autodebug(filename)
2011-03-03 06:12:13 +03:00
# Compare to each other, and to expected output
self.do_ll_run(path_from_root('tests', filename+'.o.ll.ll'))
2011-03-03 18:39:33 +03:00
# 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];
2011-09-11 01:30:17 +04:00
double y = 11.52;
printf("*%d,%d,%.2f*\\n", x, cache[20], y);
2011-03-03 18:39:33 +03:00
return 0;
}
'''
self.do_run(src, build_ll_hook=self.do_autodebug)
self.do_run(src, 'AD:', build_ll_hook=self.do_autodebug)
2011-03-03 06:12:13 +03:00
2011-04-25 04:57:01 +04:00
def test_dfe(self):
def hook(filename):
ll = open(filename + '.o.ll').read()
assert 'unneeded' not in ll, 'DFE should remove the unneeded function'
src = '''
#include <stdio.h>
void unneeded()
{
printf("some totally useless stuff\\n");
}
int main()
{
printf("*hello slim world*\\n");
return 0;
}
'''
# Using build_ll_hook forces a recompile, which leads to DFE being done even without opts
self.do_run(src, '*hello slim world*', build_ll_hook=hook)
2011-04-25 04:57:01 +04:00
2011-09-18 23:57:07 +04:00
def test_profiling(self):
2011-10-29 01:05:59 +04:00
src = '''
#include <emscripten.h>
#include <unistd.h>
int main()
{
EMSCRIPTEN_PROFILE_INIT(3);
EMSCRIPTEN_PROFILE_BEGIN(0);
usleep(10 * 1000);
EMSCRIPTEN_PROFILE_END(0);
EMSCRIPTEN_PROFILE_BEGIN(1);
usleep(50 * 1000);
EMSCRIPTEN_PROFILE_END(1);
EMSCRIPTEN_PROFILE_BEGIN(2);
usleep(250 * 1000);
EMSCRIPTEN_PROFILE_END(2);
return 0;
}
'''
post1 = '''
def process(filename):
src = open(filename, 'a')
src.write(\'\'\'
Profiling.dump();
\'\'\')
src.close()
'''
2011-10-29 01:05:59 +04:00
self.do_run(src, '''Profiling data:
Block 0: ''', post_build=post1)
# Part 2: old JS version
Settings.PROFILE = 1
Settings.INVOKE_RUN = 0
2011-09-18 23:57:07 +04:00
src = '''
#include <stdio.h>
int inner1(int x) {
for (int i = 0; i < 20; i++)
x += x/3;
return x;
}
int inner2(int x) {
for (int i = 0; i < 10; i++)
x -= x/4;
return x;
}
int inner3(int x) {
for (int i = 0; i < 5; i++)
x += x/2;
x = inner1(x) - inner2(x);
for (int i = 0; i < 5; i++)
x -= x/2;
return x;
}
int main()
{
int total = 0;
for (int i = 0; i < 5000; i++)
total += inner1(i) - 4*inner3(i);
printf("*%d*\\n", total);
return 0;
}
'''
post = '''
def process(filename):
src = open(filename, 'a')
src.write(\'\'\'
startProfiling();
run();
stopProfiling();
printProfiling();
print('*ok*');
\'\'\')
src.close()
'''
2011-09-18 23:57:07 +04:00
2011-12-08 03:26:00 +04:00
self.do_run(src, ': __Z6inner1i (5000)\n', post_build=post)
2011-09-18 23:57:07 +04:00
2010-11-06 06:48:19 +03:00
### Integration tests
def test_scriptaclass(self):
2011-07-07 07:50:04 +04:00
header_filename = os.path.join(self.get_dir(), 'header.h')
header = '''
2010-11-06 06:48:19 +03:00
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)
2010-11-18 10:13:17 +03:00
void mulVal(int mul);
2010-11-06 06:48:19 +03:00
};
'''
2011-07-07 07:50:04 +04:00
h = open(header_filename, 'w')
h.write(header)
h.close()
src = '''
#include "header.h"
2010-11-06 06:48:19 +03:00
ScriptMe::ScriptMe(int val) : value(val) { }
int ScriptMe::getVal() { return value; }
2010-11-18 10:13:17 +03:00
void ScriptMe::mulVal(int mul) { value *= mul; }
2010-11-06 06:48:19 +03:00
'''
# Way 1: use demangler and namespacer
2010-11-06 06:48:19 +03:00
script_src = '''
var sme = Module._.ScriptMe.__new__(83); // malloc(sizeof(ScriptMe)), ScriptMe::ScriptMe(sme, 83) / new ScriptMe(83) (at addr sme)
2010-11-06 22:55:25 +03:00
Module._.ScriptMe.mulVal(sme, 2); // ScriptMe::mulVal(sme, 2) sme.mulVal(2)
print('*' + Module._.ScriptMe.getVal(sme) + '*');
2011-02-21 06:03:14 +03:00
_free(sme);
2010-11-06 21:48:23 +03:00
print('*ok*');
2010-11-06 06:48:19 +03:00
'''
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)
2010-11-06 06:48:19 +03:00
# Way 2: use CppHeaderParser
Settings.RUNTIME_TYPE_INFO = 1
2011-07-07 07:50:04 +04:00
header = '''
#include <stdio.h>
class Parent {
protected:
int value;
public:
Parent(int val);
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; }
2011-07-07 07:50:04 +04:00
};
class Child2 : public Parent {
2011-07-07 07:50:04 +04:00
public:
Child2() : Parent(9) { printf("Child2:%d\\n", value); };
int getValCube() { return value*value*value; }
2011-07-24 10:43:01 +04:00
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(); };
2011-07-07 07:50:04 +04:00
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([BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0]
#print output
2011-07-07 07:50:04 +04:00
assert 'Traceback' not in output, 'Failure in binding generation: ' + output
src = '''
#include "header.h"
2011-07-07 07:50:04 +04:00
Parent::Parent(int val) : value(val) { printf("Parent:%d\\n", val); }
void Parent::mulVal(int mul) { value *= mul; }
#include "bindingtest.cpp"
'''
2011-12-22 02:09:14 +04:00
post2 = '''
def process(filename):
script_src_2 = \'\'\'
2011-07-07 07:50:04 +04:00
var sme = new Parent(42);
sme.mulVal(2);
2011-07-07 07:50:04 +04:00
print('*')
print(sme.getVal());
print('c1');
var c1 = new Child1();
print(c1.getVal());
c1.mulVal(2);
print(c1.getVal());
print(c1.getValSqr());
print(c1.getValSqr(3));
print(c1.getValTimes()); // default argument should be 1
print(c1.getValTimes(2));
2011-07-07 07:50:04 +04:00
print('c1 v2');
c1 = new Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2
2011-07-07 07:50:04 +04:00
print(c1.getVal());
c1.mulVal(2);
print(c1.getVal());
print(c1.getValSqr());
print(c1.getValSqr(3));
2011-07-07 07:50:04 +04:00
print('c2')
var c2 = new Child2();
print(c2.getVal());
c2.mulVal(2);
print(c2.getVal());
print(c2.getValCube());
var succeeded;
try {
succeeded = 0;
print(c2.doSomethingSecret()); // should fail since private
succeeded = 1;
} catch(e) {}
print(succeeded);
try {
succeeded = 0;
print(c2.getValSqr()); // function from the other class
succeeded = 1;
} catch(e) {}
print(succeeded);
try {
succeeded = 0;
c2.getValCube(); // sanity
succeeded = 1;
} catch(e) {}
print(succeeded);
2011-07-24 10:43:01 +04:00
Child2.prototype.printStatic(); // static calls go through the prototype
// virtual function
c2.virtualFunc();
Child2.prototype.runVirtualFunc(c2);
c2.virtualFunc2();
// extend the class from JS
var c3 = new Child2;
customizeVTable(c3, [{
original: Child2.prototype.virtualFunc,
replacement: function() {
print('*js virtualf replacement*');
}
}, {
original: Child2.prototype.virtualFunc2,
replacement: function() {
print('*js virtualf2 replacement*');
}
}]);
c3.virtualFunc();
Child2.prototype.runVirtualFunc(c3);
c3.virtualFunc2();
c2.virtualFunc(); // original should remain the same
Child2.prototype.runVirtualFunc(c2);
c2.virtualFunc2();
print('*ok*');
2011-12-22 02:09:14 +04:00
\'\'\'
src = open(filename, 'r').read().replace(
'// {{MODULE_ADDITIONS}',
2011-12-22 02:09:14 +04:00
open('bindingtest.js').read() + '\\n\\n' + script_src_2 + '\\n\\n' +
'// {{MODULE_ADDITIONS}'
)
open(filename, 'w').write(src)
'''
self.do_run(src, '''*
2011-07-07 07:50:04 +04:00
84
c1
Parent:7
Child1:7
7
14
196
588
14
28
2011-07-07 07:50:04 +04:00
c1 v2
Parent:16
Child1:15
15
30
900
2700
c2
Parent:9
Child2:9
9
18
5832
0
0
1
2011-07-24 10:43:01 +04:00
*static*
*virtualf*
*virtualf*
*virtualf2*
Parent:9
Child2:9
*js virtualf replacement*
*js virtualf replacement*
*js virtualf2 replacement*
*virtualf*
*virtualf*
*virtualf2*
2011-07-07 07:50:04 +04:00
*ok*
''', post_build=post2)
def test_typeinfo(self):
Settings.RUNTIME_TYPE_INFO = 1
if Settings.QUANTUM_SIZE != 4: return self.skip('We assume normal sizes in the output here')
src = '''
#include<stdio.h>
struct UserStruct {
int x;
char y;
short z;
};
struct Encloser {
short x;
UserStruct us;
int y;
};
int main() {
Encloser e;
e.us.y = 5;
printf("*ok:%d*\\n", e.us.y);
return 0;
}
'''
post = '''
def process(filename):
src = open(filename, 'r').read().replace(
'// {{POST_RUN_ADDITIONS}}',
\'\'\'
if (Runtime.typeInfo) {
print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|');
var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser')
print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|');
print('|' + JSON.stringify(Runtime.generateStructInfo(null, 'UserStruct')) + '|');
} else {
print('No type info.');
}
\'\'\'
)
open(filename, 'w').write(src)
'''
self.do_run(src,
'*ok:5*\n|i32,i8,i16|0,4,6|\n|0,4,8,10,12|\n|{"__size__":8,"x":0,"y":4,"z":6}|',
post_build=post)
# Make sure that without the setting, we don't spam the .js with the type info
Settings.RUNTIME_TYPE_INFO = 0
self.do_run(src, 'No type info.', post_build=post)
### Tests for tools
def test_safe_heap(self):
if not Settings.SAFE_HEAP: return self.skip('We need SAFE_HEAP to test SAFE_HEAP')
2011-12-09 22:35:05 +04:00
if Settings.USE_TYPED_ARRAYS == 2: return self.skip('It is ok to violate the load-store assumption with TA2')
2011-10-27 07:41:51 +04:00
if Building.LLVM_OPTS: return self.skip('LLVM can optimize away the intermediate |x|')
2011-10-18 22:49:36 +04:00
src = '''
#include<stdio.h>
int main() {
int *x = new int;
*x = 20;
float *y = (float*)x;
printf("%f\\n", *y);
2011-04-22 00:17:24 +04:00
printf("*ok*\\n");
return 0;
}
'''
2011-04-22 00:17:24 +04:00
try:
self.do_run(src, '*nothingatall*')
2011-04-22 00:17:24 +04:00
except Exception, e:
# This test *should* fail, by throwing this exception
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
# And we should not fail if we disable checking on that line
Settings.SAFE_HEAP = 3
Settings.SAFE_HEAP_LINES = ["src.cpp:7"]
2011-04-22 00:17:24 +04:00
self.do_run(src, '*ok*')
2011-04-22 00:17:24 +04:00
# But if we disable the wrong lines, we still fail
Settings.SAFE_HEAP_LINES = ["src.cpp:99"]
2011-04-22 00:17:24 +04:00
try:
self.do_run(src, '*nothingatall*')
except Exception, e:
2010-12-24 01:09:16 +03:00
# This test *should* fail, by throwing this exception
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
# And reverse the checks with = 2
Settings.SAFE_HEAP = 2
Settings.SAFE_HEAP_LINES = ["src.cpp:99"]
self.do_run(src, '*ok*')
Settings.SAFE_HEAP = 1
# Linking multiple files should work too
module = '''
#include<stdio.h>
void callFunc() {
int *x = new 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>
extern void callFunc();
int main() {
callFunc();
int *x = new int;
*x = 20;
float *y = (float*)x;
printf("%f\\n", *y);
printf("*ok*\\n");
return 0;
}
'''
main_name = os.path.join(self.get_dir(), 'main.cpp')
open(main_name, 'w').write(main)
Building.emcc(module_name, ['-g'])
Building.emcc(main_name, ['-g'])
all_name = os.path.join(self.get_dir(), 'all.bc')
Building.link([module_name + '.o', main_name + '.o'], all_name)
try:
self.do_ll_run(all_name, '*nothingatall*')
except Exception, e:
# This test *should* fail, by throwing this exception
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
# And we should not fail if we disable checking on those lines
Settings.SAFE_HEAP = 3
Settings.SAFE_HEAP_LINES = ["module.cpp:7", "main.cpp:9"]
self.do_ll_run(all_name, '*ok*')
# But we will fail if we do not disable exactly what we need to - any mistake leads to error
for lines in [["module.cpp:22", "main.cpp:9"], ["module.cpp:7", "main.cpp:29"], ["module.cpp:127", "main.cpp:449"], ["module.cpp:7"], ["main.cpp:9"]]:
Settings.SAFE_HEAP_LINES = lines
try:
self.do_ll_run(all_name, '*nothingatall*')
except Exception, e:
# This test *should* fail, by throwing this exception
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
2010-12-20 00:43:26 +03:00
def test_check_overflow(self):
Settings.CHECK_OVERFLOWS = 1
Settings.CORRECT_OVERFLOWS = 0
2010-12-20 00:43:26 +03:00
src = '''
#include<stdio.h>
int main() {
int t = 77;
for (int i = 0; i < 30; i++) {
//t = (t << 2) + t + 1; // This would have worked, since << forces into 32-bit int...
t = t*5 + 1; // Python lookdict_string has ~the above line, which turns into this one with optimizations...
printf("%d,%d\\n", t, t & 127);
}
return 0;
}
'''
try:
self.do_run(src, '*nothingatall*')
2010-12-20 00:43:26 +03:00
except Exception, e:
2010-12-24 01:09:16 +03:00
# This test *should* fail, by throwing this exception
assert 'Too many corrections' in str(e), str(e)
2010-12-20 00:43:26 +03:00
2011-02-13 06:36:02 +03:00
def test_debug(self):
src = '''
#include <stdio.h>
#include <assert.h>
void checker(int x) {
x += 20;
assert(x < 15); // this is line 7!
}
int main() {
checker(10);
return 0;
}
'''
try:
2011-12-21 09:23:21 +04:00
post = r'''
def process(filename):
lines = open(filename, 'r').readlines()
lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines)
found_line_num = any(('//@line 7 "' in line) for line in lines)
found_filename = any(('src.cpp"\n' in line) for line in lines)
assert found_line_num, 'Must have debug info with the line number'
assert found_filename, 'Must have debug info with the filename'
'''
self.do_run(src, '*nothingatall*', post_build=post)
2011-02-13 06:36:02 +03:00
except Exception, e:
# This test *should* fail
assert 'Assertion failed' in str(e), str(e)
def test_autoassemble(self):
src = r'''
#include <stdio.h>
int main() {
puts("test\n");
return 0;
}
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'src.cpp')
self.build(src, dirname, filename)
new_filename = os.path.join(dirname, 'new.bc')
shutil.copy(filename + '.o', new_filename)
2011-10-27 07:25:00 +04:00
Building.emscripten(new_filename, append_ext=False)
shutil.copy(filename + '.o.js', os.path.join(self.get_dir(), 'new.cpp.o.js'))
self.do_run(None, 'test\n', basename='new.cpp', no_build=True)
2011-02-28 03:54:21 +03:00
def test_linespecific(self):
Settings.CHECK_SIGNS = 0
Settings.CHECK_OVERFLOWS = 0
2011-11-12 09:21:58 +04:00
# Signs
src = '''
#include <stdio.h>
#include <assert.h>
int main()
{
int varey = 100;
unsigned int MAXEY = -1;
printf("*%d*\\n", varey >= MAXEY); // 100 >= -1? not in unsigned!
}
'''
Settings.CORRECT_SIGNS = 0
self.do_run(src, '*1*') # This is a fail - we expect 0
Settings.CORRECT_SIGNS = 1
self.do_run(src, '*0*') # Now it will work properly
# And now let's fix just that one line
Settings.CORRECT_SIGNS = 2
Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"]
self.do_run(src, '*0*')
# Fixing the wrong line should not work
Settings.CORRECT_SIGNS = 2
Settings.CORRECT_SIGNS_LINES = ["src.cpp:3"]
self.do_run(src, '*1*')
# And reverse the checks with = 2
Settings.CORRECT_SIGNS = 3
Settings.CORRECT_SIGNS_LINES = ["src.cpp:3"]
self.do_run(src, '*0*')
Settings.CORRECT_SIGNS = 3
Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"]
self.do_run(src, '*1*')
2011-11-25 08:12:17 +04:00
Settings.CORRECT_SIGNS = 0
# Overflows
src = '''
#include<stdio.h>
int main() {
int t = 77;
for (int i = 0; i < 30; i++) {
t = t*5 + 1;
}
printf("*%d,%d*\\n", t, t & 127);
return 0;
}
'''
correct = '*186854335,63*'
Settings.CORRECT_OVERFLOWS = 0
try:
self.do_run(src, correct)
raise Exception('UNEXPECTED-PASS')
except Exception, e:
assert 'UNEXPECTED' not in str(e), str(e)
assert 'Expected to find' in str(e), str(e)
Settings.CORRECT_OVERFLOWS = 1
self.do_run(src, correct) # Now it will work properly
# And now let's fix just that one line
Settings.CORRECT_OVERFLOWS = 2
Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:6"]
self.do_run(src, correct)
# Fixing the wrong line should not work
Settings.CORRECT_OVERFLOWS = 2
Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:3"]
try:
self.do_run(src, correct)
raise Exception('UNEXPECTED-PASS')
except Exception, e:
assert 'UNEXPECTED' not in str(e), str(e)
assert 'Expected to find' in str(e), str(e)
2010-12-20 00:43:26 +03:00
# And reverse the checks with = 2
Settings.CORRECT_OVERFLOWS = 3
Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:3"]
self.do_run(src, correct)
Settings.CORRECT_OVERFLOWS = 3
Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:6"]
try:
self.do_run(src, correct)
raise Exception('UNEXPECTED-PASS')
except Exception, e:
assert 'UNEXPECTED' not in str(e), str(e)
assert 'Expected to find' in str(e), str(e)
2011-11-25 08:12:17 +04:00
Settings.CORRECT_OVERFLOWS = 0
# Roundings
src = '''
#include <stdio.h>
#include <assert.h>
int main()
{
TYPE x = -5;
printf("*%d*", x/2);
x = 5;
printf("*%d*", x/2);
float y = -5.33;
x = y;
printf("*%d*", x);
y = 5.33;
x = y;
printf("*%d*", x);
printf("\\n");
}
'''
if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1
Settings.CORRECT_ROUNDINGS = 0
self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here!
self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick
self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-6**5*') # We fail, since no fast shortcut for 32-bit unsigneds
Settings.CORRECT_ROUNDINGS = 1
Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well
self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') # Correct
self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Correct
2011-11-25 08:12:17 +04:00
self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') # Correct
Settings.CORRECT_SIGNS = 0
if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1
Settings.CORRECT_ROUNDINGS = 2
Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake
self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*')
self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right
self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-5**5*') # No such luck here
# And reverse the check with = 2
if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1
Settings.CORRECT_ROUNDINGS = 3
Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:999"]
self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*')
self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*')
Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well
self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*')
Settings.CORRECT_SIGNS = 0
2011-11-17 22:16:42 +04:00
def test_pgo(self):
Settings.PGO = Settings.CHECK_OVERFLOWS = Settings.CORRECT_OVERFLOWS = Settings.CHECK_SIGNS = Settings.CORRECT_SIGNS = 1
src = '''
#include<stdio.h>
int main() {
int t = 77;
for (int i = 0; i < 30; i++) {
t = t*5 + 1;
}
printf("*%d,%d*\\n", t, t & 127);
2011-05-27 03:29:59 +04:00
int varey = 100;
unsigned int MAXEY = -1;
for (int j = 0; j < 2; j++) {
printf("*%d*\\n", varey >= MAXEY); // 100 >= -1? not in unsigned!
MAXEY = 1; // So we succeed the second time around
}
return 0;
}
'''
def check(output):
# TODO: check the line #
2011-11-12 09:24:16 +04:00
assert 'Overflow|src.cpp:6 : 60 hits, %20 failures' in output, 'no indication of Overflow corrections: ' + output
assert 'UnSign|src.cpp:13 : 6 hits, %17 failures' in output, 'no indication of Sign corrections: ' + output
return output
self.do_run(src, '*186854335,63*\n', output_nicerizer=check)
2011-11-17 22:16:42 +04:00
Settings.PGO = Settings.CHECK_OVERFLOWS = Settings.CORRECT_OVERFLOWS = Settings.CHECK_SIGNS = Settings.CORRECT_SIGNS = 0
# Now, recompile with the PGO data, and it should work
pgo_data = read_pgo_data(self.get_stdout_path())
Settings.CORRECT_SIGNS = 2
Settings.CORRECT_SIGNS_LINES = pgo_data['signs_lines']
Settings.CORRECT_OVERFLOWS = 2
Settings.CORRECT_OVERFLOWS_LINES = pgo_data['overflows_lines']
self.do_run(src, '*186854335,63*\n')
# Sanity check: Without PGO, we will fail
try:
self.do_run(src, '*186854335,63*\n')
except:
pass
def test_exit_status(self):
Settings.CATCH_EXIT_CODE = 1
src = '''
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello, world!\\n");
exit(118); // Unusual exit status to make sure it's working!
}
'''
self.do_run(src, 'hello, world!\nExit Status: 118')
# Generate tests for everything
def make_run(fullname, name=-1, compiler=-1, llvm_opts=0, embetter=0, quantum_size=0, typed_arrays=0, emcc_args=None):
2010-12-30 08:33:28 +03:00
exec('''
class %s(T):
def tearDown(self):
super(%s, self).tearDown()
2010-12-30 08:33:28 +03:00
def setUp(self):
super(%s, self).setUp()
Building.COMPILER_TEST_OPTS = ['-g']
os.chdir(self.get_dir()) # Ensure the directory exists and go there
2011-10-27 07:41:51 +04:00
Building.COMPILER = %r
self.emcc_args = %s
if self.emcc_args is not None:
Settings.load(self.emcc_args)
2011-12-10 05:32:33 +04:00
Building.LLVM_OPTS = 0
return
llvm_opts = %d # 1 is yes, 2 is yes and unsafe
embetter = %d
2011-04-23 00:23:37 +04:00
quantum_size = %d
# TODO: Move much of these to a init() function in shared.py, and reuse that
Settings.USE_TYPED_ARRAYS = %d
Settings.INVOKE_RUN = 1
Settings.RELOOP = 0 # we only do them in the "o2" pass
Settings.MICRO_OPTS = embetter
Settings.QUANTUM_SIZE = quantum_size
Settings.ASSERTIONS = 1-embetter
Settings.SAFE_HEAP = 1-(embetter and llvm_opts)
2011-10-27 07:41:51 +04:00
Building.LLVM_OPTS = llvm_opts
2011-11-17 22:16:42 +04:00
Settings.PGO = 0
Settings.CHECK_OVERFLOWS = 1-(embetter or llvm_opts)
Settings.CORRECT_OVERFLOWS = 1-(embetter and llvm_opts)
Settings.CORRECT_SIGNS = 0
Settings.CORRECT_ROUNDINGS = 0
Settings.CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = []
Settings.CHECK_SIGNS = 0 #1-(embetter or llvm_opts)
Settings.INIT_STACK = 0
Settings.RUNTIME_TYPE_INFO = 0
Settings.DISABLE_EXCEPTION_CATCHING = 0
Settings.PROFILE = 0
Settings.INCLUDE_FULL_LIBRARY = 0
Settings.BUILD_AS_SHARED_LIB = 0
Settings.RUNTIME_LINKED_LIBS = []
Settings.CATCH_EXIT_CODE = 0
Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2)
2011-12-07 04:32:03 +04:00
Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0
2011-11-11 22:10:11 +04:00
if Settings.USE_TYPED_ARRAYS == 2:
Settings.I64_MODE = 1
Settings.SAFE_HEAP = 1 # only checks for alignment problems, which is very important with unsafe opts
else:
Settings.I64_MODE = 0
2011-11-18 02:44:42 +04:00
Building.pick_llvm_opts(3, safe=Building.LLVM_OPTS != 2)
2010-12-30 08:33:28 +03:00
TT = %s
''' % (fullname, fullname, fullname, compiler, str(emcc_args), llvm_opts, embetter, quantum_size, typed_arrays, fullname))
2010-10-10 03:54:23 +04:00
return TT
2010-12-30 08:33:28 +03:00
# Make one run with the defaults
exec('default = make_run("default", compiler=CLANG, emcc_args=[])')
# Make one run with -O1, with safe heap
exec('o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "SAFE_HEAP=1"])')
# Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow)
exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "--closure", "0"])')
# Make custom runs with various options
2011-12-07 01:50:44 +04:00
for compiler, quantum, embetter, typed_arrays, llvm_opts in [
2011-12-10 05:37:34 +04:00
(CLANG, 1, 1, 0, 0),
(CLANG, 1, 1, 1, 1),
2011-12-07 01:50:44 +04:00
(CLANG, 4, 0, 0, 0),
(CLANG, 4, 0, 0, 1),
(CLANG, 4, 1, 1, 0),
(CLANG, 4, 1, 1, 1),
]:
2011-12-07 01:50:44 +04:00
fullname = 's_%d_%d%s%s' % (
llvm_opts, embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays)
)
exec('%s = make_run(fullname, %r,%r,%d,%d,%d,%d)' % (fullname, fullname, compiler, llvm_opts, embetter, quantum, typed_arrays))
2011-04-23 00:23:37 +04:00
2010-10-10 03:54:23 +04:00
del T # T is just a shape for the specific subclasses, we don't test it itself
2011-11-17 22:54:32 +04:00
class other(RunnerCore):
2011-11-21 09:02:46 +04:00
def test_emcc(self):
2011-12-12 08:43:08 +04:00
def clear():
for name in os.listdir(self.get_dir()):
try_delete(name)
2011-12-11 22:23:03 +04:00
for compiler in [EMCC, EMXX]:
2011-12-11 23:39:00 +04:00
shortcompiler = os.path.basename(compiler)
suffix = '.c' if compiler == EMCC else '.cpp'
2011-12-11 23:39:00 +04:00
2011-12-11 22:23:03 +04:00
# --version
2011-12-13 06:07:07 +04:00
output = Popen([compiler, '--version'], stdout=PIPE, stderr=PIPE).communicate()
2011-12-11 22:23:03 +04:00
self.assertContained('''emcc (Emscripten GCC-like replacement) 2.0
Copyright (C) 2011 the Emscripten authors.
This is free and open source software under the MIT license.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
2011-12-11 23:54:22 +04:00
''', output[0], output[1])
2011-12-11 22:23:03 +04:00
2011-12-11 23:39:00 +04:00
# --help
2011-12-13 06:07:07 +04:00
output = Popen([compiler, '--help'], stdout=PIPE, stderr=PIPE).communicate()
2011-12-11 23:39:00 +04:00
self.assertContained('''%s [options] file...
Most normal gcc/g++ options will work, for example:
--help Display this information
--version Display compiler version information
Options that are modified or new in %s include:
2011-12-13 01:35:51 +04:00
-O0 No optimizations (default)
2011-12-13 22:09:59 +04:00
''' % (shortcompiler, shortcompiler), output[0], output[1])
2011-12-11 23:39:00 +04:00
# emcc src.cpp ==> writes a.out.js
2011-12-12 08:43:08 +04:00
clear()
2011-12-13 06:07:07 +04:00
output = Popen([compiler, path_from_root('tests', 'hello_world' + suffix)], stdout=PIPE, stderr=PIPE).communicate()
assert len(output[0]) == 0, output[0]
assert os.path.exists('a.out.js'), '\n'.join(output)
self.assertContained('hello, world!', run_js('a.out.js'))
# properly report source code errors, and stop there
clear()
assert not os.path.exists('a.out.js')
output = Popen([compiler, path_from_root('tests', 'hello_world_error' + suffix)], stdout=PIPE, stderr=PIPE).communicate()
assert not os.path.exists('a.out.js'), 'compilation failed, so no output file is expected'
assert len(output[0]) == 0, output[0]
self.assertNotContained('IOError', output[1]) # no python stack
self.assertNotContained('Traceback', output[1]) # no python stack
self.assertContained('error: invalid preprocessing directive', output[1])
self.assertContained('''error: use of undeclared identifier 'cheez''', output[1])
self.assertContained('2 errors generated', output[1])
assert output[1].split('2 errors generated.')[1].replace('\n', '') == 'emcc: compiler frontend failed to generate LLVM bitcode, halting'
2011-12-12 08:43:08 +04:00
# emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file
# regression check: -o js should create "js", with bitcode content
for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'js']]:
target = args[1] if len(args) == 2 else 'hello_world.o'
2011-12-12 08:43:08 +04:00
clear()
2011-12-13 06:07:07 +04:00
output = Popen([compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate()
2011-12-12 08:43:08 +04:00
assert len(output[0]) == 0, output[0]
assert os.path.exists(target), 'Expected %s to exist since args are %s : %s' % (target, str(args), '\n'.join(output))
2011-12-12 08:43:08 +04:00
self.assertContained('hello, world!', self.run_llvm_interpreter([target]))
# emcc src.ll ==> generates .js
clear()
output = Popen([compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate()
assert len(output[0]) == 0, output[0]
assert os.path.exists('a.out.js'), '\n'.join(output)
self.assertContained('hello, world!', run_js('a.out.js'))
# emcc [..] -o [path] ==> should work with absolute paths
try:
os.mkdir('a_dir')
os.chdir('a_dir')
os.mkdir('b_dir')
for path in [os.path.abspath(os.path.join('..', 'file1.js')), os.path.join('b_dir', 'file2.js')]:
clear()
output = Popen([compiler, path_from_root('tests', 'hello_world.ll'), '-o', path], stdout=PIPE, stderr=PIPE).communicate()
assert os.path.exists(path), path + ' does not exist; ' + '\n'.join(output)
self.assertContained('hello, world!', run_js(path))
finally:
os.chdir(self.get_dir())
try:
shutil.rmtree('a_dir')
except:
pass
# dlmalloc. dlmalloc is special in that it is the only part of libc that is (1) hard to write well, and
# very speed-sensitive. So we do not implement it in JS in library.js, instead we compile it from source
for source, has_malloc in [('hello_world' + suffix, False), ('hello_malloc.cpp', True)]:
clear()
output = Popen([compiler, path_from_root('tests', source)], stdout=PIPE, stderr=PIPE).communicate()
assert os.path.exists('a.out.js'), '\n'.join(output)
self.assertContained('hello, world!', run_js('a.out.js'))
generated = open('a.out.js').read()
assert ('function _malloc(bytes) {' in generated) == (not has_malloc), 'If malloc is needed, it should be there, if not not'
2011-12-14 23:59:09 +04:00
# Optimization: emcc src.cpp -o something.js [-Ox]. -O0 is the same as not specifying any optimization setting
for params, opt_level, bc_params, closure, has_malloc in [ # bc params are used after compiling to bitcode
(['-o', 'something.js'], 0, None, 0, 1),
(['-o', 'something.js', '-O0'], 0, None, 0, 0),
(['-o', 'something.js', '-O1'], 1, None, 0, 0),
(['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 1, 0),
(['-o', 'something.js', '-O2'], 2, None, 1, 1),
(['-o', 'something.js', '-O2', '--closure', '0'], 2, None, 0, 0),
(['-o', 'something.js', '-O3'], 3, None, 1, 1),
(['-o', 'something.js', '-O3', '--closure', '0'], 3, None, 0, 0),
# and, test compiling to bitcode first
(['-o', 'something.bc'], 0, [], 0, 0),
(['-o', 'something.bc'], 0, ['-O0'], 0, 0),
(['-o', 'something.bc'], 1, ['-O1'], 0, 0),
(['-o', 'something.bc'], 2, ['-O2'], 1, 0),
(['-o', 'something.bc'], 3, ['-O3'], 1, 0),
2011-12-14 23:59:09 +04:00
]:
#print params, opt_level, bc_params, closure
2011-12-13 01:35:51 +04:00
clear()
output = Popen([compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params,
2011-12-13 06:07:07 +04:00
stdout=PIPE, stderr=PIPE).communicate()
2011-12-13 01:35:51 +04:00
assert len(output[0]) == 0, output[0]
if bc_params is not None:
assert os.path.exists('something.bc'), output[1]
output = Popen([compiler, 'something.bc', '-o', 'something.js'] + bc_params, stdout=PIPE, stderr=PIPE).communicate()
assert os.path.exists('something.js'), output[1]
2011-12-13 04:53:09 +04:00
assert ('Warning: The relooper optimization can be very slow.' in output[1]) == (opt_level >= 2), 'relooper warning should appear in opt >= 2'
2011-12-13 04:55:42 +04:00
assert ('Warning: Applying some potentially unsafe optimizations!' in output[1]) == (opt_level >= 3), 'unsafe warning should appear in opt >= 3'
2011-12-13 01:35:51 +04:00
self.assertContained('hello, world!', run_js('something.js'))
# Verify optimization level etc. in the generated code
2011-12-13 01:35:51 +04:00
# XXX these are quite sensitive, and will need updating when code generation changes
generated = open('something.js').read() # TODO: parse out the _main function itself, not support code, if the tests below need that some day
assert 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 should be used by default'
2011-12-13 01:35:51 +04:00
assert 'SAFE_HEAP' not in generated, 'safe heap should not be used by default'
assert ': while(' not in generated, 'when relooping we also js-optimize, so there should be no labelled whiles'
if closure:
assert 'Module._main = ' in generated, 'closure compiler should have been run'
else:
# closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure
assert 'Module._main = ' not in generated, 'closure compiler should not have been run'
# XXX find a way to test this: assert ('& 255' in generated or '&255' in generated) == (opt_level <= 2), 'corrections should be in opt <= 2'
assert ('(__label__)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2'
2012-01-05 20:43:28 +04:00
assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
assert 'var $i;' in generated, 'micro opts should always be on'
if opt_level >= 1: assert 'HEAP8[HEAP32[' in generated, 'eliminator should create compound expressions, and fewer one-time vars'
assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts'
assert ('function _malloc(bytes) {' in generated) == (not has_malloc), 'If malloc is needed, it should be there, if not not'
2011-12-13 01:35:51 +04:00
2011-12-13 22:09:59 +04:00
# emcc -s RELOOP=1 src.cpp ==> should pass -s to emscripten.py. --typed-arrays is a convenient alias for -s USE_TYPED_ARRAYS
2011-12-13 06:27:53 +04:00
for params, test, text in [
(['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'),
(['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'),
([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'),
2011-12-13 22:38:06 +04:00
(['-s', 'EXPORTED_FUNCTIONS=["_main", "_dump"]'], lambda generated: 'Module["_dump"]' in generated, 'dump is now exported'),
2011-12-13 22:09:59 +04:00
(['--typed-arrays', '0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'),
(['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'),
(['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'),
(['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'),
2011-12-13 06:27:53 +04:00
]:
clear()
output = Popen([compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js'] + params, stdout=PIPE, stderr=PIPE).communicate()
2011-12-13 06:27:53 +04:00
assert len(output[0]) == 0, output[0]
assert os.path.exists('a.out.js'), '\n'.join(output)
self.assertContained('hello, world!', run_js('a.out.js'))
assert test(open('a.out.js').read()), text
2011-12-14 23:39:53 +04:00
# Compiling two source files into a final JS.
for args, target in [([], 'a.out.js'), (['-o', 'combined.js'], 'combined.js')]:
clear()
output = Popen([compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp')] + args,
stdout=PIPE, stderr=PIPE).communicate()
assert len(output[0]) == 0, output[0]
assert os.path.exists(target), '\n'.join(output)
self.assertContained('side got: hello from main, over', run_js(target))
# Compiling two files with -c will generate separate .bc files
clear()
2011-12-14 23:39:53 +04:00
output = Popen([compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp'), '-c'] + args,
stdout=PIPE, stderr=PIPE).communicate()
if '-o' in args:
# specifying -o and -c is an error
assert 'fatal error' in output[1], output[1]
continue
assert os.path.exists('twopart_main.o'), '\n'.join(output)
assert os.path.exists('twopart_side.o'), '\n'.join(output)
assert not os.path.exists(target), 'We should only have created bitcode here: ' + '\n'.join(output)
# Compiling one of them alone is expected to fail
output = Popen([compiler, 'twopart_main.o'] + args, stdout=PIPE, stderr=PIPE).communicate()
assert os.path.exists(target), '\n'.join(output)
#print '\n'.join(output)
2011-12-14 23:39:53 +04:00
self.assertContained('is not a function', run_js(target, stderr=STDOUT))
try_delete(target)
# Combining those bc files into js should work
output = Popen([compiler, 'twopart_main.o', 'twopart_side.o'] + args, stdout=PIPE, stderr=PIPE).communicate()
assert os.path.exists(target), '\n'.join(output)
2011-12-14 23:39:53 +04:00
self.assertContained('side got: hello from main, over', run_js(target))
# Combining bc files into another bc should also work
try_delete(target)
assert not os.path.exists(target)
output = Popen([compiler, 'twopart_main.o', 'twopart_side.o', '-o', 'combined.bc'] + args, stdout=PIPE, stderr=PIPE).communicate()
assert os.path.exists('combined.bc'), '\n'.join(output)
self.assertContained('side got: hello from main, over', self.run_llvm_interpreter(['combined.bc']))
2012-01-05 02:36:02 +04:00
# --js-transform <transform>
clear()
trans = os.path.join(self.get_dir(), 't.py')
trans_file = open(trans, 'w')
trans_file.write('''
import sys
f = open(sys.argv[1], 'w')
f.write('transformed!')
f.close()
''')
trans_file.close()
output = Popen([compiler, path_from_root('tests', 'hello_world' + suffix), '--js-transform', 'python t.py'], stdout=PIPE, stderr=PIPE).communicate()
assert open('a.out.js').read() == 'transformed!', 'Transformed output must be as expected'
# TODO: Add in files test a clear example of using disablePermissions, and link to it from the wiki
2011-12-14 23:59:09 +04:00
# TODO: test normal project linking, static and dynamic: get_library should not need to be told what to link!
# TODO: deprecate llvm optimizations, dlmalloc, etc. in emscripten.py.
2011-11-21 09:02:46 +04:00
# Finally, do some web browser tests
def run_browser(html_file, message):
webbrowser.open_new(html_file)
print 'A web browser window should have opened a page containing the results of a part of this test.'
print 'You need to manually look at the page to see that it works ok: ' + message
print '(sleeping for a bit to keep the directory alive for the web browser..)'
time.sleep(5)
print '(moving on..)'
# test HTML generation.
clear()
2011-12-13 06:07:07 +04:00
output = Popen([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.html'], stdout=PIPE, stderr=PIPE).communicate()
assert len(output[0]) == 0, output[0]
assert os.path.exists('something.html'), output
run_browser('something.html', 'You should see "hello, world!" and a colored cube.')
# And test running in a web worker
clear()
2011-12-13 06:07:07 +04:00
output = Popen([EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'], stdout=PIPE, stderr=PIPE).communicate()
assert len(output[0]) == 0, output[0]
assert os.path.exists('worker.js'), output
self.assertContained('you should not see this text when in a worker!', run_js('worker.js')) # code should run standalone
html_file = open('main.html', 'w')
html_file.write('''
<html>
<body>
<script>
var worker = new Worker('worker.js');
worker.onmessage = function(event) {
document.write("<hr>Called back by the worker: " + event.data + "<br><hr>");
};
</script>
</body>
</html>
''')
html_file.close()
run_browser('main.html', 'You should see that the worker was called, and said "hello from worker!"')
2011-08-24 05:25:31 +04:00
def test_eliminator(self):
input = open(path_from_root('tools', 'eliminator', 'eliminator-test.js')).read()
expected = open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read()
2011-11-20 01:37:26 +04:00
output = Popen([COFFEESCRIPT, VARIABLE_ELIMINATOR], stdin=PIPE, stdout=PIPE).communicate(input)[0]
self.assertIdentical(expected, output)
2011-08-24 05:25:31 +04:00
2011-11-20 09:38:22 +04:00
def test_js_optimizer(self):
2011-12-27 02:41:32 +04:00
for input, expected, passes in [
(open(path_from_root('tools', 'test-js-optimizer.js')).read(), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(),
['hoistMultiples', 'loopOptimizer', 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost']),
(open(path_from_root('tools', 'test-js-optimizer-t2c.js')).read(), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(),
['simplifyExpressionsPre', 'optimizeShiftsConservative']),
(open(path_from_root('tools', 'test-js-optimizer-t2.js')).read(), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(),
['simplifyExpressionsPre', 'optimizeShiftsAggressive']),
2011-12-27 02:41:32 +04:00
]:
output = Popen([NODE_JS, JS_OPTIMIZER] + passes, stdin=PIPE, stdout=PIPE).communicate(input)[0]
self.assertIdentical(expected, output.replace('\n\n', '\n'))
2011-11-20 09:38:22 +04:00
2011-12-18 23:34:19 +04:00
elif 'benchmark' in str(sys.argv):
# Benchmarks. Run them with argument |benchmark|. To run a specific test, do
# |benchmark.test_X|.
2010-10-10 03:54:23 +04:00
2011-10-30 23:55:36 +04:00
fingerprint = [time.asctime()]
try:
fingerprint.append('em: ' + Popen(['git', 'show'], stdout=PIPE).communicate()[0].split('\n')[0])
except:
pass
try:
d = os.getcwd()
os.chdir(os.path.expanduser('~/Dev/mozilla-central'))
fingerprint.append('sm: ' + filter(lambda line: 'changeset' in line,
Popen(['hg', 'tip'], stdout=PIPE).communicate()[0].split('\n'))[0])
except:
pass
finally:
os.chdir(d)
print 'Running Emscripten benchmarks... [ %s ]' % ' | '.join(fingerprint)
2011-01-02 09:59:55 +03:00
2010-10-10 03:54:23 +04:00
sys.argv = filter(lambda x: x != 'benchmark', sys.argv)
assert(os.path.exists(CLOSURE_COMPILER))
2011-10-12 06:04:06 +04:00
try:
index = SPIDERMONKEY_ENGINE.index("options('strict')")
SPIDERMONKEY_ENGINE = SPIDERMONKEY_ENGINE[:index-1] + SPIDERMONKEY_ENGINE[index+1:] # closure generates non-strict
except:
pass
2011-05-25 21:53:50 +04:00
2011-10-27 07:41:51 +04:00
Building.COMPILER = CLANG
2011-12-04 22:38:17 +04:00
# Pick the JS engine to benchmark. If you specify one, it will be picked. For example, python tests/runner.py benchmark SPIDERMONKEY_ENGINE
JS_ENGINE = JS_ENGINES[0]
for i in range(1, len(sys.argv)):
arg = sys.argv[i]
2011-12-18 23:34:19 +04:00
if not arg.startswith('benchmark.test_'):
JS_ENGINE = eval(arg)
sys.argv[i] = None
sys.argv = filter(lambda arg: arg is not None, sys.argv)
2011-12-04 22:38:17 +04:00
print 'Benchmarking JS engine:', JS_ENGINE
2011-10-27 07:41:51 +04:00
Building.COMPILER_TEST_OPTS = []
2011-02-21 06:03:11 +03:00
2011-11-02 04:10:06 +04:00
TEST_REPS = 10
2011-12-22 05:55:38 +04:00
TOTAL_TESTS = 7
2010-10-10 03:54:23 +04:00
tests_done = 0
2011-10-12 04:42:00 +04:00
total_times = map(lambda x: 0., range(TOTAL_TESTS))
total_native_times = map(lambda x: 0., range(TOTAL_TESTS))
2010-10-10 03:54:23 +04:00
class benchmark(RunnerCore):
2011-11-28 00:29:59 +04:00
def print_stats(self, times, native_times, last=False):
2010-10-10 03:54:23 +04:00
mean = sum(times)/len(times)
squared_times = map(lambda x: x*x, times)
mean_of_squared = sum(squared_times)/len(times)
std = math.sqrt(mean_of_squared - mean*mean)
2011-11-28 00:29:59 +04:00
sorted_times = times[:]
sorted_times.sort()
median = sum(sorted_times[len(sorted_times)/2 - 1:len(sorted_times)/2 + 1])/2
mean_native = sum(native_times)/len(native_times)
squared_native_times = map(lambda x: x*x, native_times)
mean_of_squared_native = sum(squared_native_times)/len(native_times)
std_native = math.sqrt(mean_of_squared_native - mean_native*mean_native)
2011-11-28 00:29:59 +04:00
sorted_native_times = native_times[:]
sorted_native_times.sort()
median_native = sum(sorted_native_times[len(sorted_native_times)/2 - 1:len(sorted_native_times)/2 + 1])/2
2011-11-28 00:29:59 +04:00
final = mean / mean_native
if last:
norm = 0
2011-10-12 04:42:00 +04:00
for i in range(len(times)):
2011-11-28 00:29:59 +04:00
norm += times[i]/native_times[i]
norm /= len(times)
print
print ' JavaScript: %.3f Native: %.3f Ratio: %.3f Normalized ratio: %.3f' % (mean, mean_native, final, norm)
return
2011-10-12 04:42:00 +04:00
print
2011-11-28 00:29:59 +04:00
print ' JavaScript: mean: %.3f (+-%.3f) secs median: %.3f range: %.3f-%.3f (noise: %3.3f%%) (%d runs)' % (mean, std, median, min(times), max(times), 100*std/mean, TEST_REPS)
print ' Native : mean: %.3f (+-%.3f) secs median: %.3f range: %.3f-%.3f (noise: %3.3f%%) JS is %.2f X slower' % (mean_native, std_native, median_native, min(native_times), max(native_times), 100*std_native/mean_native, final)
2010-10-10 03:54:23 +04:00
2011-12-14 09:14:52 +04:00
def do_benchmark(self, src, args=[], expected_output='FAIL', emcc_args=[]):
2010-10-10 03:54:23 +04:00
dirname = self.get_dir()
filename = os.path.join(dirname, 'src.cpp')
2011-12-14 09:14:52 +04:00
f = open(filename, 'w')
f.write(src)
f.close()
final_filename = os.path.join(dirname, 'src.js')
2011-12-18 23:34:19 +04:00
try_delete(final_filename)
2011-12-14 09:14:52 +04:00
output = Popen([EMCC, filename, '-O3', '-s', 'USE_TYPED_ARRAYS=1', '-s', 'QUANTUM_SIZE=1',
'-s', 'TOTAL_MEMORY=100*1024*1024', '-s', 'FAST_MEMORY=10*1024*1024',
'-o', final_filename] + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate()
2011-12-18 23:34:19 +04:00
assert os.path.exists(final_filename), 'Failed to compile file: ' + '\n'.join(output)
# Run JS
2011-10-12 04:42:00 +04:00
global total_times, tests_done
2010-10-10 03:54:23 +04:00
times = []
for i in range(TEST_REPS):
start = time.time()
2011-01-20 09:57:53 +03:00
js_output = self.run_generated_code(JS_ENGINE, final_filename, args, check_timeout=False)
2010-10-10 03:54:23 +04:00
curr = time.time()-start
times.append(curr)
2011-10-12 04:42:00 +04:00
total_times[tests_done] += curr
2010-10-15 10:07:23 +04:00
if i == 0:
# Sanity check on output
self.assertContained(expected_output, js_output)
2010-10-10 03:54:23 +04:00
# Run natively
self.build_native(filename)
global total_native_times
native_times = []
for i in range(TEST_REPS):
start = time.time()
self.run_native(filename, args)
curr = time.time()-start
native_times.append(curr)
2011-10-12 04:42:00 +04:00
total_native_times[tests_done] += curr
self.print_stats(times, native_times)
2010-10-10 03:54:23 +04:00
tests_done += 1
if tests_done == TOTAL_TESTS:
2011-11-28 00:29:59 +04:00
print 'Total stats:',
self.print_stats(total_times, total_native_times, last=True)
2011-06-11 23:03:10 +04:00
def test_primes(self):
src = '''
#include<stdio.h>
#include<math.h>
int main() {
int primes = 0, curri = 2;
while (primes < 100000) {
int ok = true;
for (int j = 2; j < sqrtf(curri); j++) {
if (curri % j == 0) {
ok = false;
break;
}
}
if (ok) {
primes++;
}
curri++;
}
printf("lastprime: %d.\\n", curri-1);
return 1;
}
'''
2011-11-17 06:43:29 +04:00
self.do_benchmark(src, [], 'lastprime: 1297001.')
2011-06-12 06:05:20 +04:00
def test_memops(self):
src = '''
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main() {
int N = 1024*1024;
2011-07-02 06:56:14 +04:00
int M = 190;
2011-06-12 06:05:20 +04:00
int final = 0;
char *buf = (char*)malloc(N);
for (int t = 0; t < M; t++) {
2011-06-12 06:05:20 +04:00
for (int i = 0; i < N; i++)
buf[i] = (i + final)%256;
2011-06-12 06:05:20 +04:00
for (int i = 0; i < N; i++)
final += buf[i] & 1;
final = final % 1000;
2011-06-12 06:05:20 +04:00
}
printf("final: %d.\\n", final);
return 1;
}
'''
2011-11-17 06:43:29 +04:00
self.do_benchmark(src, [], 'final: 720.')
2011-06-12 06:05:20 +04:00
2012-01-07 06:27:58 +04:00
def zzztest_copy(self):
src = r'''
#include<stdio.h>
struct vec {
int x, y, z;
int r, g, b;
vec(int x_, int y_, int z_, int r_, int g_, int b_) : x(x_), y(y_), z(z_), r(r_), g(g_), b(b_) {}
static vec add(vec a, vec b) {
return vec(a.x+b.x, a.y+b.y, a.z+b.z, a.r+b.r, a.g+b.g, a.b+b.b);
}
void norm() {
x %= 1024;
y %= 1024;
z %= 1024;
r %= 1024;
b %= 1024;
g %= 1024;
}
int sum() { return x + y + z + r + g + b; }
};
int main() {
int total = 0;
for (int i = 0; i < 2500; i++) {
for (int j = 0; j < 1000; j++) {
vec c(i, i+i%10, j*2, i%255, j%120, i%15);
vec d(j+i%10, j*2, j%255, i%120, j%15, j);
vec e = c;
c.norm();
d.norm();
vec f = vec::add(c, d);
f = vec::add(e, f);
f.norm();
f = vec::add(d, f);
total += f.sum() % 100;
total %= 10240;
}
}
printf("sum:%d\n", total);
return 1;
}
'''
2012-01-07 06:33:24 +04:00
self.do_benchmark(src, [], 'sum:3588\n', emcc_args=['-s', 'QUANTUM_SIZE=4', '-s', 'USE_TYPED_ARRAYS=2'])
2012-01-07 06:27:58 +04:00
2010-10-10 03:54:23 +04:00
def test_fannkuch(self):
src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read()
2011-11-17 06:43:29 +04:00
self.do_benchmark(src, ['10'], 'Pfannkuchen(10) = 38.')
2010-10-10 03:54:23 +04:00
2011-11-25 00:31:28 +04:00
def test_corrections(self):
2011-11-27 01:58:30 +04:00
src = r'''
2011-11-25 00:31:28 +04:00
#include<stdio.h>
#include<math.h>
2011-11-25 00:31:28 +04:00
int main() {
2011-11-27 01:58:30 +04:00
int N = 4100;
int M = 4100;
2011-11-25 03:44:56 +04:00
unsigned int f = 0;
2011-11-27 01:58:30 +04:00
unsigned short s = 0;
2011-11-25 00:31:28 +04:00
for (int t = 0; t < M; t++) {
for (int i = 0; i < N; i++) {
2011-11-25 03:44:56 +04:00
f += i / ((t % 5)+1);
if (f > 1000) f /= (t % 3)+1;
if (i % 4 == 0) f += sqrtf(i) * (i % 8 == 0 ? 1 : -1);
2011-11-27 01:58:30 +04:00
s += (short(f)*short(f)) % 256;
2011-11-25 00:31:28 +04:00
}
}
2011-11-27 01:58:30 +04:00
printf("final: %d:%d.\n", f, s);
2011-11-25 00:31:28 +04:00
return 1;
}
'''
2011-12-14 09:14:52 +04:00
self.do_benchmark(src, [], 'final: 826:14324.', emcc_args=['-s', 'CORRECT_SIGNS=1', '-s', 'CORRECT_OVERFLOWS=1', '-s', 'CORRECT_ROUNDINGS=1'])
2011-11-25 00:31:28 +04:00
def fasta(self, emcc_args=[]):
src = open(path_from_root('tests', 'fasta.cpp'), 'r').read()
self.do_benchmark(src, ['2100000'], '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\nTCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\nAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\nGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\nCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\nGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\nGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\nTTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\nAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\nGCCTGGGCGA''',
emcc_args=emcc_args)
def test_fasta(self):
self.fasta()
2011-12-22 05:48:38 +04:00
def zzztest_fasta_t2(self):
self.fasta(emcc_args=['-s', 'USE_TYPED_ARRAYS=2', '-s', 'QUANTUM_SIZE=4'])
2011-11-07 22:17:11 +04:00
def test_skinning(self):
src = open(path_from_root('tests', 'skinning_test_no_simd.cpp'), 'r').read()
2011-11-17 06:43:29 +04:00
self.do_benchmark(src, ['10000', '1000'], 'blah=0.000000')
2011-11-07 22:17:11 +04:00
2011-06-17 03:19:58 +04:00
def test_dlmalloc(self):
2011-12-14 09:21:09 +04:00
# XXX This seems to have regressed slightly with emcc. Are -g and the signs lines passed properly?
2011-10-09 00:28:01 +04:00
src = open(path_from_root('src', 'dlmalloc.c'), 'r').read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read()
2011-12-14 09:14:52 +04:00
self.do_benchmark(src, ['400', '400'], '*400,0*', emcc_args=['-g', '-s', 'CORRECT_SIGNS=2', '-s', 'CORRECT_SIGNS_LINES=[4820, 4195, 4250, 4203, 4209, 4239, 4231]'])
2011-12-18 23:34:19 +04:00
elif 'sanity' in str(sys.argv):
# Run some sanity checks on the test runner and emcc.
sys.argv = filter(lambda x: x != 'sanity', sys.argv)
2011-12-19 02:47:33 +04:00
print
print 'Running sanity checks.'
print 'WARNING: This will modify ~/.emscripten, and in theory can break it although it should be restored properly. A backup will be saved in ~/.emscripten_backup'
print
2011-12-18 23:34:19 +04:00
assert os.path.exists(CONFIG_FILE), 'To run these tests, we need a (working!) ~/.emscripten file to already exist'
shutil.copyfile(CONFIG_FILE, CONFIG_FILE + '_backup')
def restore():
shutil.copyfile(CONFIG_FILE + '_backup', CONFIG_FILE)
2011-12-19 02:47:33 +04:00
SANITY_FILE = CONFIG_FILE + '_sanity'
2011-12-18 23:34:19 +04:00
def wipe():
try_delete(CONFIG_FILE)
2011-12-19 02:47:33 +04:00
try_delete(SANITY_FILE)
2011-12-18 23:34:19 +04:00
commands = [[EMCC], ['python', path_from_root('tests', 'runner.py'), 'blahblah']]
class sanity(RunnerCore):
def setUp(self):
wipe()
def tearDown(self):
restore()
2011-12-19 02:47:33 +04:00
def do(self, command):
if type(command) is not list:
command = [command]
return Popen(command, stdout=PIPE, stderr=STDOUT).communicate()[0]
def check_working(self, command, expected=None):
2011-12-19 02:47:33 +04:00
if type(command) is not list:
command = [command]
if expected is None:
if command[0] == EMCC:
expected = 'no input files'
else:
expected = "has no attribute 'blahblah'"
2011-12-19 02:47:33 +04:00
output = self.do(command)
self.assertContained(expected, output)
2011-12-19 02:47:33 +04:00
return output
def test_aaa_normal(self): # this should be the very first thing that runs. if this fails, everything else is irrelevant!
2011-12-18 23:34:19 +04:00
for command in commands:
# Your existing ~/.emscripten should work!
restore()
2011-12-19 02:47:33 +04:00
self.check_working(command)
2011-12-18 23:34:19 +04:00
def test_firstrun(self):
for command in commands:
wipe()
2011-12-19 02:47:33 +04:00
output = self.do(command)
2011-12-18 23:34:19 +04:00
self.assertContained('Welcome to Emscripten!', output)
self.assertContained('This is the first time any of the Emscripten tools has been run.', output)
self.assertContained('A settings file has been copied to ~/.emscripten, at absolute path: %s' % CONFIG_FILE, output)
self.assertContained('Please edit that file and change the paths to fit your system', output)
self.assertContained('make sure LLVM_ROOT and NODE_JS are correct', output)
self.assertContained('This command will now exit. When you are done editing those paths, re-run it.', output)
assert output.replace('\n', '').endswith('===='), 'We should have stopped: ' + output
assert (open(CONFIG_FILE).read() == open(path_from_root('settings.py')).read()), 'Settings should be copied from settings.py'
# Second run, with bad ~/.emscripten
for settings in ['blah', 'LLVM_ROOT="blah"; JS_ENGINES=[]; COMPILER_ENGINE=NODE_JS=SPIDERMONKEY_ENGINE=[]']:
f = open(CONFIG_FILE, 'w')
f.write(settings)
f.close()
2011-12-19 02:47:33 +04:00
output = self.do(command)
2011-12-18 23:34:19 +04:00
if 'LLVM_ROOT' not in settings:
self.assertContained('Error in evaluating ~/.emscripten', output)
else:
2011-12-19 02:47:33 +04:00
self.assertContained('FATAL', output) # sanity check should fail
def test_closure_compiler(self):
CLOSURE_FATAL = 'fatal: Closure compiler'
CLOSURE_WARNING = 'WARNING: Closure compiler'
# Sanity check should find closure
restore()
output = self.check_working(EMCC)
self.assertNotContained(CLOSURE_FATAL, output)
self.assertNotContained(CLOSURE_WARNING, output)
# Append a bad path for closure, will warn
f = open(CONFIG_FILE, 'a')
f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n')
f.close()
output = self.check_working(EMCC, CLOSURE_WARNING)
# And if you actually try to use the bad path, will be fatal
f = open(CONFIG_FILE, 'a')
f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n')
f.close()
output = self.check_working([EMCC, '-O2', 'tests/hello_world.cpp'], CLOSURE_FATAL)
# With a working path, all is well
restore()
try_delete('a.out.js')
output = self.check_working([EMCC, '-O2', 'tests/hello_world.cpp'], 'The relooper optimization can be very slow')
assert os.path.exists('a.out.js')
2011-12-19 02:47:33 +04:00
def test_emcc(self):
def mtime(filename):
return os.stat(filename).st_mtime
SANITY_MESSAGE = 'Emscripten: Running sanity checks'
SANITY_FAIL_MESSAGE = 'sanity check failed to run'
# emcc should check sanity if no ~/.emscripten_sanity
restore()
time.sleep(0.1)
assert not os.path.exists(SANITY_FILE) # restore is just the settings, not the sanity
output = self.check_working(EMCC)
self.assertContained(SANITY_MESSAGE, output)
assert os.path.exists(SANITY_FILE) # EMCC should have checked sanity successfully
assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE)
self.assertNotContained(SANITY_FAIL_MESSAGE, output)
# emcc run again should not sanity check, because the sanity file is newer
output = self.check_working(EMCC)
self.assertNotContained(SANITY_MESSAGE, output)
self.assertNotContained(SANITY_FAIL_MESSAGE, output)
# But the test runner should
output = self.check_working(commands[1])
self.assertContained(SANITY_MESSAGE, output)
self.assertNotContained(SANITY_FAIL_MESSAGE, output)
# Make sure the test runner didn't do anything to the setup
output = self.check_working(EMCC)
self.assertNotContained(SANITY_MESSAGE, output)
self.assertNotContained(SANITY_FAIL_MESSAGE, output)
# emcc should also check sanity if the file is outdated
time.sleep(0.1)
restore()
assert mtime(SANITY_FILE) < mtime(CONFIG_FILE)
output = self.check_working(EMCC)
self.assertContained(SANITY_MESSAGE, output)
assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE)
self.assertNotContained(SANITY_FAIL_MESSAGE, output)
2011-12-18 23:34:19 +04:00
else:
raise Exception('Test runner is confused: ' + str(sys.argv))
2011-06-17 03:19:58 +04:00
2010-08-26 08:01:10 +04:00
if __name__ == '__main__':
2010-12-30 08:33:28 +03:00
sys.argv = [sys.argv[0]] + ['-v'] + sys.argv[1:] # Verbose output by default
# Sanity checks
2011-12-18 23:34:19 +04:00
check_sanity(force=True)
total_engines = len(JS_ENGINES)
JS_ENGINES = filter(check_engine, JS_ENGINES)
if len(JS_ENGINES) == 0:
print 'WARNING: None of the JS engines in JS_ENGINES appears to work.'
elif len(JS_ENGINES) < total_engines:
print 'WARNING: Not all the JS engines in JS_ENGINES appears to work, ignoring those.'
# Go
2010-10-10 03:54:23 +04:00
unittest.main()
2010-08-26 08:01:10 +04:00