4473 строки
159 KiB
Python
4473 строки
159 KiB
Python
'''
|
|
Simple test runner
|
|
|
|
See settings.py file for options¶ms. Edit as needed.
|
|
'''
|
|
|
|
from subprocess import Popen, PIPE, STDOUT
|
|
import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, tempfile, re, json, difflib
|
|
|
|
# Setup
|
|
|
|
__rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
|
def path_from_root(*pathelems):
|
|
return os.path.join(__rootpath__, *pathelems)
|
|
exec(open(path_from_root('tools', 'shared.py'), 'r').read())
|
|
|
|
# Sanity check for config
|
|
|
|
try:
|
|
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.')
|
|
|
|
# Paths
|
|
|
|
EMSCRIPTEN = path_from_root('emscripten.py')
|
|
DEMANGLER = path_from_root('third_party', 'demangler.py')
|
|
NAMESPACER = path_from_root('tools', 'namespacer.py')
|
|
EMMAKEN = path_from_root('tools', 'emmaken.py')
|
|
AUTODEBUGGER = path_from_root('tools', 'autodebugger.py')
|
|
DFE = path_from_root('tools', 'dead_function_eliminator.py')
|
|
|
|
# Global cache for tests (we have multiple TestCase instances; this object lets them share data)
|
|
|
|
GlobalCache = {}
|
|
|
|
class Dummy: pass
|
|
Settings = Dummy()
|
|
Settings.saveJS = 0
|
|
|
|
# Core test runner class, shared between normal tests and benchmarks
|
|
|
|
class RunnerCore(unittest.TestCase):
|
|
def setUp(self):
|
|
dirname = tempfile.mkdtemp(prefix="ems_" + self.__class__.__name__ + "_", dir=TEMP_DIR)
|
|
if not os.path.exists(dirname):
|
|
os.makedirs(dirname)
|
|
self.working_dir = dirname
|
|
|
|
def tearDown(self):
|
|
if Settings.saveJS:
|
|
for name in os.listdir(self.get_dir()):
|
|
if name.endswith(('.o.js', '.cc.js')):
|
|
suff = '.'.join(name.split('.')[-2:])
|
|
shutil.copy(os.path.join(self.get_dir(), name),
|
|
os.path.join(TEMP_DIR, self.id().replace('__main__.', '').replace('.test_', '.')+'.'+suff))
|
|
shutil.rmtree(self.get_dir())
|
|
|
|
def skip(self, why):
|
|
print >> sys.stderr, '<skipping: %s> ' % why,
|
|
|
|
def get_dir(self):
|
|
return self.working_dir
|
|
|
|
# Similar to LLVM::createStandardModulePasses()
|
|
def pick_llvm_opts(self, optimization_level, handpicked=None):
|
|
global LLVM_OPT_OPTS, QUANTUM_SIZE, USE_TYPED_ARRAYS
|
|
|
|
if handpicked is None:
|
|
handpicked = True # Not even TA2 can withstand instruction combining
|
|
|
|
LLVM_OPT_OPTS = pick_llvm_opts(optimization_level, handpicked, quantum_size=QUANTUM_SIZE)
|
|
|
|
# Emscripten optimizations that we run on the .ll file
|
|
def do_ll_opts(self, filename):
|
|
# Remove target info. This helps LLVM opts, if we run them later
|
|
cleaned = filter(lambda line: not line.startswith('target datalayout = ') and not line.startswith('target triple = '),
|
|
open(filename + '.o.ll', 'r').readlines())
|
|
os.unlink(filename + '.o.ll')
|
|
open(filename + '.o.ll.orig', 'w').write(''.join(cleaned))
|
|
|
|
output = Popen(['python', DFE, filename + '.o.ll.orig', filename + '.o.ll'], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
assert os.path.exists(filename + '.o.ll'), 'Failed to run ll optimizations'
|
|
|
|
# Optional LLVM optimizations
|
|
def do_llvm_opts(self, filename):
|
|
if LLVM_OPTS:
|
|
shutil.move(filename + '.o', filename + '.o.pre')
|
|
output = Popen([LLVM_OPT, filename + '.o.pre'] + LLVM_OPT_OPTS + ['-o=' + filename + '.o'], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
assert os.path.exists(filename + '.o'), 'Failed to run llvm optimizations: ' + output
|
|
|
|
def do_llvm_dis(self, filename):
|
|
# LLVM binary ==> LLVM assembly
|
|
try:
|
|
os.remove(filename + '.o.ll')
|
|
except:
|
|
pass
|
|
output = Popen([LLVM_DIS, filename + '.o'] + LLVM_DIS_OPTS + ['-o=' + filename + '.o.ll'], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
assert os.path.exists(filename + '.o.ll'), 'Could not create .ll file: ' + output
|
|
|
|
def do_llvm_as(self, source, target):
|
|
# LLVM assembly ==> LLVM binary
|
|
try:
|
|
os.remove(target)
|
|
except:
|
|
pass
|
|
output = Popen([LLVM_AS, source, '-o=' + target], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
assert os.path.exists(target), 'Could not create bc file: ' + output
|
|
|
|
def do_link(self, files, target):
|
|
output = Popen([LLVM_LINK] + files + ['-o', target], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
assert output is None or 'Could not open input file' not in output, 'Linking error: ' + output
|
|
|
|
def prep_ll_run(self, filename, ll_file, force_recompile=False, build_ll_hook=None):
|
|
if ll_file.endswith(('.bc', '.o')):
|
|
if ll_file != filename + '.o':
|
|
shutil.copy(ll_file, filename + '.o')
|
|
self.do_llvm_dis(filename)
|
|
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
|
|
|
|
if LLVM_OPTS or force_recompile or build_ll_hook:
|
|
self.do_ll_opts(filename)
|
|
if build_ll_hook:
|
|
build_ll_hook(filename)
|
|
shutil.move(filename + '.o.ll', filename + '.o.ll.pre')
|
|
self.do_llvm_as(filename + '.o.ll.pre', filename + '.o')
|
|
output = Popen([LLVM_AS, filename + '.o.ll.pre'] + ['-o=' + filename + '.o'], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
assert 'error:' not in output, 'Error in llvm-as: ' + output
|
|
self.do_llvm_opts(filename)
|
|
self.do_llvm_dis(filename)
|
|
|
|
# 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=[]):
|
|
# 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
|
|
else:
|
|
# copy whole directory, and use a specific main .cpp file
|
|
shutil.rmtree(dirname)
|
|
shutil.copytree(src, dirname)
|
|
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)
|
|
|
|
# C++ => LLVM binary
|
|
os.chdir(dirname)
|
|
cwd = os.getcwd()
|
|
|
|
for f in [filename] + additional_files:
|
|
try:
|
|
# Make sure we notice if compilation steps failed
|
|
os.remove(f + '.o')
|
|
os.remove(f + '.o.ll')
|
|
except:
|
|
pass
|
|
output = Popen([COMPILER, '-emit-llvm'] + COMPILER_OPTS + COMPILER_TEST_OPTS +
|
|
['-I', dirname, '-I', os.path.join(dirname, 'include')] +
|
|
map(lambda include: '-I' + include, includes) +
|
|
['-c', f, '-o', f + '.o'],
|
|
stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
assert os.path.exists(f + '.o'), 'Source compilation error: ' + output
|
|
|
|
os.chdir(cwd)
|
|
|
|
# Link all files
|
|
if len(additional_files) + len(libraries) > 0:
|
|
shutil.move(filename + '.o', filename + '.o.alone')
|
|
self.do_link([filename + '.o.alone'] + map(lambda f: f + '.o', additional_files) + libraries,
|
|
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)
|
|
|
|
self.do_emscripten(filename, output_processor, extra_args=extra_emscripten_args)
|
|
|
|
def do_emscripten(self, filename, output_processor=None, append_ext=True, extra_args=[]):
|
|
# Add some headers by default. TODO: remove manually adding these in each test
|
|
if '-H' not in extra_args:
|
|
extra_args += ['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']
|
|
|
|
# Run Emscripten
|
|
exported_settings = {}
|
|
for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'AUTO_OPTIMIZE', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO', 'DISABLE_EXCEPTION_CATCHING', 'TOTAL_MEMORY', 'FAST_MEMORY', 'EXCEPTION_DEBUG', 'PROFILE']:
|
|
try:
|
|
value = eval(setting)
|
|
if value is not None:
|
|
exported_settings[setting] = value
|
|
except:
|
|
pass
|
|
settings = ['-s %s=%s' % (k, json.dumps(v)) for k, v in exported_settings.items()]
|
|
compiler_output = timeout_run(Popen(['python', EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE, stderr=STDOUT), TIMEOUT, 'Compiling')
|
|
#print compiler_output
|
|
|
|
# Detect compilation crashes and errors
|
|
if compiler_output is not None and 'Traceback' in compiler_output and 'in test_' in compiler_output: print compiler_output; assert 0
|
|
assert os.path.exists(filename + '.o.js') and len(open(filename + '.o.js', 'r').read()) > 0, 'Emscripten failed to generate .js: ' + str(compiler_output)
|
|
|
|
if output_processor is not None:
|
|
output_processor(open(filename + '.o.js').read())
|
|
|
|
def run_generated_code(self, engine, filename, args=[], check_timeout=True):
|
|
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(engine, filename, args, check_timeout, stdout=open(stdout, 'w'), stderr=open(stderr, 'w'))
|
|
if cwd is not None:
|
|
os.chdir(cwd)
|
|
ret = open(stdout, 'r').read() + open(stderr, 'r').read()
|
|
assert 'strict warning:' not in ret, 'We should pass all strict mode checks: ' + ret
|
|
return ret
|
|
|
|
def run_llvm_interpreter(self, args):
|
|
return Popen([EXEC_LLVM] + args, stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
|
|
def build_native(self, filename, compiler='g++'):
|
|
Popen([compiler, '-O3', filename, '-o', filename+'.native'], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
|
|
def run_native(self, filename, args):
|
|
Popen([filename+'.native'] + args, stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
|
|
def assertContained(self, value, string):
|
|
if type(value) is not str: value = value() # lazy loading
|
|
if type(string) is not str: string = string()
|
|
if value not in string:
|
|
raise Exception("Expected to 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')]))
|
|
))
|
|
|
|
def assertNotContained(self, value, string):
|
|
if type(value) is not str: value = value() # lazy loading
|
|
if type(string) is not str: string = string()
|
|
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')]))
|
|
))
|
|
|
|
###################################################################################################
|
|
|
|
if 'benchmark' not in str(sys.argv):
|
|
# Tests
|
|
|
|
print "Running Emscripten tests..."
|
|
|
|
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=[]):
|
|
#print 'Running test:', inspect.stack()[1][3].replace('test_', ''), '[%s,%s,%s]' % (COMPILER.split(os.sep)[-1], 'llvm-optimizations' if LLVM_OPTS else '', 'reloop&optimize' if RELOOP else '')
|
|
if force_c or (main_file is not None and main_file[-2:]) == '.c':
|
|
basename = 'src.c'
|
|
global COMPILER
|
|
COMPILER = to_cc(COMPILER)
|
|
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, basename)
|
|
if not no_build:
|
|
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)
|
|
|
|
if post_build is not None:
|
|
post_build(filename + '.o.js')
|
|
|
|
# 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'])
|
|
print '[autogenerated expected output: %20s]' % (expected_output[0:17].replace('\n', '')+'...')
|
|
|
|
# Run in both JavaScript engines, if optimizing - significant differences there (typed arrays)
|
|
if js_engines is None:
|
|
js_engines = [SPIDERMONKEY_ENGINE, V8_ENGINE]
|
|
if USE_TYPED_ARRAYS == 2:
|
|
js_engines = [SPIDERMONKEY_ENGINE] # when oh when will v8 support typed arrays in the console
|
|
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)
|
|
|
|
#shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging
|
|
|
|
# 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=[]):
|
|
|
|
filename = os.path.join(self.get_dir(), 'src.cpp')
|
|
|
|
self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook)
|
|
self.do_emscripten(filename, extra_args=extra_emscripten_args)
|
|
self.do_run(None,
|
|
expected_output,
|
|
args,
|
|
no_build=True,
|
|
js_engines=js_engines,
|
|
output_nicerizer=output_nicerizer,
|
|
post_build=post_build)
|
|
|
|
def test_hello_world(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
int main()
|
|
{
|
|
printf("hello, world!\\n");
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, 'hello, world!')
|
|
|
|
def test_intvars(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
int global = 20;
|
|
int *far;
|
|
int main()
|
|
{
|
|
int x = 5;
|
|
int y = x+17;
|
|
int z = (y-1)/2; // Should stay an integer after division!
|
|
y += 1;
|
|
int w = x*3+4;
|
|
int k = w < 15 ? 99 : 101;
|
|
far = &k;
|
|
*far += global;
|
|
int i = k > 100; // Should be an int, not a bool!
|
|
int j = i << 6;
|
|
j >>= 1;
|
|
j = j ^ 5;
|
|
int h = 1;
|
|
h |= 0;
|
|
int p = h;
|
|
p &= 0;
|
|
printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", x, y, z, w, k, i, j, h, p);
|
|
|
|
long hash = -1;
|
|
size_t perturb;
|
|
int ii = 0;
|
|
for (perturb = hash; ; perturb >>= 5) {
|
|
printf("%d:%d", ii, perturb);
|
|
ii++;
|
|
if (ii == 9) break;
|
|
printf(",");
|
|
}
|
|
printf("*\\n");
|
|
printf("*%.1d,%.2d*\\n", 56, 9);
|
|
|
|
// Fixed-point math on 64-bit ints. Tricky to support since we have no 64-bit shifts in JS
|
|
{
|
|
struct Fixed {
|
|
static int Mult(int a, int b) {
|
|
return ((long long)a * (long long)b) >> 16;
|
|
}
|
|
};
|
|
printf("fixed:%d\\n", Fixed::Mult(150000, 140000));
|
|
}
|
|
|
|
printf("*%ld*%p\\n", (long)21, &hash); // The %p should not enter an infinite loop!
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*5,23,10,19,121,1,37,1,0*\n0:-1,1:134217727,2:4194303,3:131071,4:4095,5:127,6:3,7:0,8:0*\n*56,09*\nfixed:320434\n*21*')
|
|
|
|
def test_sintvars(self):
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 1 # Relevant to this test
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct S {
|
|
char *match_start;
|
|
char *strstart;
|
|
};
|
|
int main()
|
|
{
|
|
struct S _s;
|
|
struct S *s = &_s;
|
|
unsigned short int sh;
|
|
|
|
s->match_start = (char*)32522;
|
|
s->strstart = (char*)(32780);
|
|
printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start));
|
|
sh = s->strstart - s->match_start;
|
|
printf("*%d,%d*\\n", sh, sh>>7);
|
|
|
|
s->match_start = (char*)32999;
|
|
s->strstart = (char*)(32780);
|
|
printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start));
|
|
sh = s->strstart - s->match_start;
|
|
printf("*%d,%d*\\n", sh, sh>>7);
|
|
}
|
|
'''
|
|
output = '*32780,32522,258*\n*258,2*\n*32780,32999,-219*\n*65317,510*'
|
|
global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 0 # We should not need overflow correction to get this right
|
|
self.do_run(src, output, force_c=True)
|
|
|
|
def test_bigint(self):
|
|
if USE_TYPED_ARRAYS != 0: return self.skip('Typed arrays truncate i64')
|
|
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;
|
|
}
|
|
'''
|
|
self.do_run(src, '*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*')
|
|
|
|
def test_unsigned(self):
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 1 # We test for exactly this sort of thing here
|
|
global CHECK_SIGNS; CHECK_SIGNS = 0
|
|
src = '''
|
|
#include <stdio.h>
|
|
const signed char cvals[2] = { -1, -2 }; // compiler can store this is a string, so -1 becomes \FF, and needs re-signing
|
|
int main()
|
|
{
|
|
{
|
|
unsigned char x = 200;
|
|
printf("*%d*\\n", x);
|
|
unsigned char y = -22;
|
|
printf("*%d*\\n", y);
|
|
}
|
|
|
|
int varey = 100;
|
|
unsigned int MAXEY = -1, MAXEY2 = -77;
|
|
printf("*%u,%d,%u*\\n", MAXEY, varey >= MAXEY, MAXEY2); // 100 >= -1? not in unsigned!
|
|
|
|
int y = cvals[0];
|
|
printf("*%d,%d,%d,%d*\\n", cvals[0], cvals[0] < 0, y, y < 0);
|
|
y = cvals[1];
|
|
printf("*%d,%d,%d,%d*\\n", cvals[1], cvals[1] < 0, y, y < 0);
|
|
|
|
// zext issue - see mathop in jsifier
|
|
unsigned char x8 = -10;
|
|
unsigned long hold = 0;
|
|
hold += x8;
|
|
int y32 = hold+50;
|
|
printf("*%u,%u*\\n", hold, y32);
|
|
|
|
// Comparisons
|
|
x8 = 0;
|
|
for (int i = 0; i < 254; i++) x8++; // make it an actual 254 in JS - not a -2
|
|
printf("*%d,%d*\\n", x8+1 == 0xff, x8+1 != 0xff); // 0xff may be '-1' in the bitcode
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src)#, '*4294967295,0,4294967219*\n*-1,1,-1,1*\n*-2,1,-2,1*\n*246,296*\n*1,0*')
|
|
|
|
# Now let's see some code that should just work in USE_TYPED_ARRAYS == 2, but requires
|
|
# corrections otherwise
|
|
if USE_TYPED_ARRAYS == 2:
|
|
CORRECT_SIGNS = 0
|
|
CHECK_SIGNS = 1
|
|
else:
|
|
CORRECT_SIGNS = 1
|
|
CHECK_SIGNS = 0
|
|
|
|
src = '''
|
|
#include <stdio.h>
|
|
int main()
|
|
{
|
|
{
|
|
unsigned char x;
|
|
unsigned char *y = &x;
|
|
*y = -1;
|
|
printf("*%d*\\n", x);
|
|
}
|
|
{
|
|
unsigned short x;
|
|
unsigned short *y = &x;
|
|
*y = -1;
|
|
printf("*%d*\\n", x);
|
|
}
|
|
/*{ // This case is not checked. The hint for unsignedness is just the %u in printf, and we do not analyze that
|
|
unsigned int x;
|
|
unsigned int *y = &x;
|
|
*y = -1;
|
|
printf("*%u*\\n", x);
|
|
}*/
|
|
{
|
|
char x;
|
|
char *y = &x;
|
|
*y = 255;
|
|
printf("*%d*\\n", x);
|
|
}
|
|
{
|
|
char x;
|
|
char *y = &x;
|
|
*y = 65535;
|
|
printf("*%d*\\n", x);
|
|
}
|
|
{
|
|
char x;
|
|
char *y = &x;
|
|
*y = 0xffffffff;
|
|
printf("*%d*\\n", x);
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*255*\n*65535*\n*-1*\n*-1*\n*-1*')
|
|
|
|
def test_bitfields(self):
|
|
global SAFE_HEAP; SAFE_HEAP = 0 # bitfields do loads on invalid areas, by design
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct bitty {
|
|
unsigned x : 1;
|
|
unsigned y : 1;
|
|
unsigned z : 1;
|
|
};
|
|
int main()
|
|
{
|
|
bitty b;
|
|
printf("*");
|
|
for (int i = 0; i <= 1; i++)
|
|
for (int j = 0; j <= 1; j++)
|
|
for (int k = 0; k <= 1; k++) {
|
|
b.x = i;
|
|
b.y = j;
|
|
b.z = k;
|
|
printf("%d,%d,%d,", b.x, b.y, b.z);
|
|
}
|
|
printf("*\\n");
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*0,0,0,0,0,1,0,1,0,0,1,1,1,0,0,1,0,1,1,1,0,1,1,1,*')
|
|
|
|
def test_floatvars(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
int main()
|
|
{
|
|
float x = 1.234, y = 3.5, q = 0.00000001;
|
|
y *= 3;
|
|
int z = x < y;
|
|
printf("*%d,%d,%.1f,%d,%.4f,%.2f*\\n", z, int(y), y, (int)x, x, q);
|
|
|
|
/*
|
|
// 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*')
|
|
|
|
def test_math(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <cmath>
|
|
int main()
|
|
{
|
|
printf("*%.2f,%.2f,%f,%f", M_PI, -M_PI, 1/0.0, -1/0.0);
|
|
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");
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*3.14,-3.14,inf,-inf,0,0,0,1,0,1,1,0*')
|
|
|
|
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*')
|
|
|
|
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*')
|
|
|
|
# Test for issue 39
|
|
if not LLVM_OPTS:
|
|
self.do_ll_run(path_from_root('tests', 'issue_39.ll'), '*yes*')
|
|
|
|
def test_if_else(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
int main()
|
|
{
|
|
int x = 5;
|
|
if (x > 10) {
|
|
printf("*yes*\\n");
|
|
} else {
|
|
printf("*no*\\n");
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*no*')
|
|
|
|
def test_loop(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
int main()
|
|
{
|
|
int x = 5;
|
|
for (int i = 0; i < 6; i++)
|
|
x += x*i;
|
|
printf("*%d*\\n", x);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*3600*')
|
|
|
|
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
|
|
}
|
|
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*')
|
|
|
|
def test_strings(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int x = 5, y = 9, magic = 7; // fool compiler with magic
|
|
memmove(&x, &y, magic-7); // 0 should not crash us
|
|
|
|
int xx, yy, zz;
|
|
char s[32];
|
|
int cc = sscanf("abc_10.b1_xyz_543_defg", "abc_%d.%2x_xyz_%3d_%3s", &xx, &yy, &zz, s);
|
|
printf("%d:%d,%d,%d,%s\\n", cc, xx, yy, zz, s);
|
|
|
|
printf("%d\\n", argc);
|
|
puts(argv[1]);
|
|
puts(argv[2]);
|
|
printf("%d\\n", atoi(argv[3])+2);
|
|
const char *foolingthecompiler = "\\rabcd";
|
|
printf("%d\\n", strlen(foolingthecompiler)); // Tests parsing /0D in llvm - should not be a 0 (end string) then a D!
|
|
printf("%s\\n", NULL); // Should print '(null)', not the string at address 0, which is a real address for us!
|
|
printf("/* a comment */\\n"); // Should not break the generated code!
|
|
printf("// another\\n"); // Should not break the generated code!
|
|
|
|
char* strdup_val = strdup("test");
|
|
printf("%s\\n", strdup_val);
|
|
free(strdup_val);
|
|
|
|
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'])
|
|
|
|
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)*')
|
|
|
|
def test_funcs(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
int funcy(int x)
|
|
{
|
|
return x*9;
|
|
}
|
|
int main()
|
|
{
|
|
printf("*%d,%d*\\n", funcy(8), funcy(10));
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*72,90*')
|
|
|
|
def test_structs(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct S
|
|
{
|
|
int x, y;
|
|
};
|
|
int main()
|
|
{
|
|
S a, b;
|
|
a.x = 5; a.y = 6;
|
|
b.x = 101; b.y = 7009;
|
|
S *c, *d;
|
|
c = &a;
|
|
c->x *= 2;
|
|
c = &b;
|
|
c->y -= 1;
|
|
d = c;
|
|
d->y += 10;
|
|
printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n", a.x, a.y, b.x, b.y, c->x, c->y, d->x, d->y);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*10,6,101,7018,101,7018,101,7018*')
|
|
|
|
gen_struct_src = '''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "emscripten.h"
|
|
|
|
struct S
|
|
{
|
|
int x, y;
|
|
};
|
|
int main()
|
|
{
|
|
S* a = {{gen_struct}};
|
|
a->x = 51; a->y = 62;
|
|
printf("*%d,%d*\\n", a->x, a->y);
|
|
{{del_struct}}(a);
|
|
return 0;
|
|
}
|
|
'''
|
|
|
|
def test_mallocstruct(self):
|
|
self.do_run(self.gen_struct_src.replace('{{gen_struct}}', '(S*)malloc(sizeof(S))').replace('{{del_struct}}', 'free'), '*51,62*')
|
|
|
|
def test_newstruct(self):
|
|
self.do_run(self.gen_struct_src.replace('{{gen_struct}}', 'new S').replace('{{del_struct}}', 'delete'), '*51,62*')
|
|
|
|
def test_addr_of_stacked(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
void alter(int *y)
|
|
{
|
|
*y += 5;
|
|
}
|
|
int main()
|
|
{
|
|
int x = 2;
|
|
alter(&x);
|
|
printf("*%d*\\n", x);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*7*')
|
|
|
|
def test_globals(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
char cache[256], *next = cache;
|
|
|
|
int main()
|
|
{
|
|
cache[10] = 25;
|
|
next[20] = 51;
|
|
printf("*%d,%d*\\n", next[10], cache[20]);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*25,51*')
|
|
|
|
def test_linked_list(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct worker_args {
|
|
int value;
|
|
struct worker_args *next;
|
|
};
|
|
int main()
|
|
{
|
|
worker_args a;
|
|
worker_args b;
|
|
a.value = 60;
|
|
a.next = &b;
|
|
b.value = 900;
|
|
b.next = NULL;
|
|
worker_args* c = &a;
|
|
int total = 0;
|
|
while (c) {
|
|
total += c->value;
|
|
c = c->next;
|
|
}
|
|
|
|
// Chunk of em
|
|
worker_args chunk[10];
|
|
for (int i = 0; i < 9; i++) {
|
|
chunk[i].value = i*10;
|
|
chunk[i].next = &chunk[i+1];
|
|
}
|
|
chunk[9].value = 90;
|
|
chunk[9].next = &chunk[0];
|
|
|
|
c = chunk;
|
|
do {
|
|
total += c->value;
|
|
c = c->next;
|
|
} while (c != chunk);
|
|
|
|
printf("*%d,%d*\\n", total, b.next);
|
|
// NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.)
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*1410,0*')
|
|
|
|
def test_sup(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
struct S4 { int x; }; // size: 4
|
|
struct S4_2 { short x, y; }; // size: 4, but for alignment purposes, 2
|
|
struct S6 { short x, y, z; }; // size: 6
|
|
struct S6w { char x[6]; }; // size: 6 also
|
|
struct S6z { int x; short y; }; // size: 8, since we align to a multiple of the biggest - 4
|
|
|
|
struct C___ { S6 a, b, c; int later; };
|
|
struct Carr { S6 a[3]; int later; }; // essentially the same, but differently defined
|
|
struct C__w { S6 a; S6w b; S6 c; int later; }; // same size, different struct
|
|
struct Cp1_ { int pre; short a; S6 b, c; int later; }; // fillers for a
|
|
struct Cp2_ { int a; short pre; S6 b, c; int later; }; // fillers for a (get addr of the other filler)
|
|
struct Cint { S6 a; int b; S6 c; int later; }; // An int (different size) for b
|
|
struct C4__ { S6 a; S4 b; S6 c; int later; }; // Same size as int from before, but a struct
|
|
struct C4_2 { S6 a; S4_2 b; S6 c; int later; }; // Same size as int from before, but a struct with max element size 2
|
|
struct C__z { S6 a; S6z b; S6 c; int later; }; // different size, 8 instead of 6
|
|
|
|
int main()
|
|
{
|
|
#define TEST(struc) \\
|
|
{ \\
|
|
struc *s = 0; \\
|
|
printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a), (int)&(s->b), (int)&(s->c), (int)&(s->later), sizeof(struc)); \\
|
|
}
|
|
#define TEST_ARR(struc) \\
|
|
{ \\
|
|
struc *s = 0; \\
|
|
printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a[0]), (int)&(s->a[1]), (int)&(s->a[2]), (int)&(s->later), sizeof(struc)); \\
|
|
}
|
|
printf("sizeofs:%d,%d\\n", sizeof(S6), sizeof(S6z));
|
|
TEST(C___);
|
|
TEST_ARR(Carr);
|
|
TEST(C__w);
|
|
TEST(Cp1_);
|
|
TEST(Cp2_);
|
|
TEST(Cint);
|
|
TEST(C4__);
|
|
TEST(C4_2);
|
|
TEST(C__z);
|
|
return 1;
|
|
}
|
|
'''
|
|
if QUANTUM_SIZE == 1:
|
|
self.do_run(src, 'sizeofs:6,8\n*C___: 0,3,6,9<24*\n*Carr: 0,3,6,9<24*\n*C__w: 0,3,9,12<24*\n*Cp1_: 1,2,5,8<24*\n*Cp2_: 0,2,5,8<24*\n*Cint: 0,3,4,7<24*\n*C4__: 0,3,4,7<24*\n*C4_2: 0,3,5,8<20*\n*C__z: 0,3,5,8<28*')
|
|
else:
|
|
self.do_run(src, 'sizeofs:6,8\n*C___: 0,6,12,20<24*\n*Carr: 0,6,12,20<24*\n*C__w: 0,6,12,20<24*\n*Cp1_: 4,6,12,20<24*\n*Cp2_: 0,6,12,20<24*\n*Cint: 0,8,12,20<24*\n*C4__: 0,8,12,20<24*\n*C4_2: 0,6,10,16<20*\n*C__z: 0,8,16,24<28*')
|
|
|
|
def test_assert(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
int main() {
|
|
assert(1 == true); // pass
|
|
assert(1 == false); // fail
|
|
return 1;
|
|
}
|
|
'''
|
|
self.do_run(src, 'Assertion failed: 1 == false')
|
|
|
|
def test_exceptions(self):
|
|
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!*')
|
|
|
|
global DISABLE_EXCEPTION_CATCHING
|
|
DISABLE_EXCEPTION_CATCHING = 1
|
|
self.do_run(src, 'Compiled code throwing an exception')
|
|
|
|
def test_typed_exceptions(self):
|
|
return self.skip('TODO: fix this for llvm 3.0')
|
|
|
|
global SAFE_HEAP; SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access.
|
|
global EXCEPTION_DEBUG; EXCEPTION_DEBUG = 0 # Messes up expected output.
|
|
src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read()
|
|
expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_class(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct Random {
|
|
enum { IM = 139968, IA = 3877, IC = 29573 };
|
|
Random() : last(42) {}
|
|
float get( float max = 1.0f ) {
|
|
last = ( last * IA + IC ) % IM;
|
|
return max * last / IM;
|
|
}
|
|
protected:
|
|
unsigned int last;
|
|
} rng1;
|
|
int main()
|
|
{
|
|
Random rng2;
|
|
int count = 0;
|
|
for (int i = 0; i < 100; i++) {
|
|
float x1 = rng1.get();
|
|
float x2 = rng2.get();
|
|
printf("%f, %f\\n", x1, x2);
|
|
if (x1 != x2) count += 1;
|
|
}
|
|
printf("*%d*\\n", count);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*0*')
|
|
|
|
def test_inherit(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct Parent {
|
|
int x1, x2;
|
|
};
|
|
struct Child : Parent {
|
|
int y;
|
|
};
|
|
int main()
|
|
{
|
|
Parent a;
|
|
a.x1 = 50;
|
|
a.x2 = 87;
|
|
Child b;
|
|
b.x1 = 78;
|
|
b.x2 = 550;
|
|
b.y = 101;
|
|
Child* c = (Child*)&a;
|
|
c->x1 ++;
|
|
c = &b;
|
|
c->y --;
|
|
printf("*%d,%d,%d,%d,%d,%d,%d*\\n", a.x1, a.x2, b.x1, b.x2, b.y, c->x1, c->x2);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*51,87,78,550,100,78,550*')
|
|
|
|
def test_polymorph(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct Pure {
|
|
virtual int implme() = 0;
|
|
};
|
|
struct Parent : Pure {
|
|
virtual int getit() { return 11; };
|
|
int implme() { return 32; }
|
|
};
|
|
struct Child : Parent {
|
|
int getit() { return 74; }
|
|
int implme() { return 1012; }
|
|
};
|
|
|
|
struct Other {
|
|
int one() { return 11; }
|
|
int two() { return 22; }
|
|
};
|
|
|
|
int main()
|
|
{
|
|
Parent *x = new Parent();
|
|
Parent *y = new Child();
|
|
printf("*%d,%d,%d,%d*\\n", x->getit(), y->getit(), x->implme(), y->implme());
|
|
|
|
Other *o = new Other;
|
|
int (Other::*Ls)() = &Other::one;
|
|
printf("*%d*\\n", (o->*(Ls))());
|
|
Ls = &Other::two;
|
|
printf("*%d*\\n", (o->*(Ls))());
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*11,74,32,1012*\n*11*\n*22*')
|
|
|
|
def test_funcptr(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
int calc1() { return 26; }
|
|
int calc2() { return 90; }
|
|
typedef int (*fp_t)();
|
|
|
|
fp_t globally1 = calc1;
|
|
fp_t globally2 = calc2;
|
|
|
|
int nothing(const char *str) { return 0; }
|
|
|
|
int main()
|
|
{
|
|
fp_t fp = calc1;
|
|
void *vp = (void*)fp;
|
|
fp_t fpb = (fp_t)vp;
|
|
fp_t fp2 = calc2;
|
|
void *vp2 = (void*)fp2;
|
|
fp_t fpb2 = (fp_t)vp2;
|
|
printf("*%d,%d,%d,%d,%d,%d*\\n", fp(), fpb(), fp2(), fpb2(), globally1(), globally2());
|
|
|
|
fp_t t = calc1;
|
|
printf("*%d,%d", t == calc1, t == calc2);
|
|
t = calc2;
|
|
printf(",%d,%d*\\n", t == calc1, t == calc2);
|
|
|
|
int (*other)(const char *str);
|
|
other = nothing;
|
|
other("*hello!*");
|
|
other = puts;
|
|
other("*goodbye!*");
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*26,26,90,90,26,90*\n*1,0,0,1*\n*goodbye!*')
|
|
|
|
def test_mathfuncptr(self):
|
|
src = '''
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
int
|
|
main(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*')
|
|
|
|
def test_alloca(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
int main() {
|
|
char *pc;
|
|
pc = (char *)alloca(5);
|
|
printf("z:%d*%d*\\n", pc > 0, (int)pc);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, 'z:1*', force_c=True)
|
|
|
|
def test_array2(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
static const double grid[4][2] = {
|
|
{-3/3.,-1/3.},{+1/3.,-3/3.},
|
|
{-1/3.,+3/3.},{+3/3.,+1/3.}
|
|
};
|
|
|
|
int main() {
|
|
for (int i = 0; i < 4; i++)
|
|
printf("%d:%.2f,%.2f ", i, grid[i][0], grid[i][1]);
|
|
printf("\\n");
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '0:-1.00,-0.33 1:0.33,-1.00 2:-0.33,1.00 3:1.00,0.33')
|
|
|
|
def test_array2b(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
static const struct {
|
|
unsigned char left;
|
|
unsigned char right;
|
|
} prioritah[] = {
|
|
{6, 6}, {6, 6}, {7, 95}, {7, 7}
|
|
};
|
|
|
|
int main() {
|
|
printf("*%d,%d\\n", prioritah[1].left, prioritah[1].right);
|
|
printf("%d,%d*\\n", prioritah[2].left, prioritah[2].right);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*6,6\n7,95*')
|
|
|
|
|
|
def test_constglobalstructs(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct IUB {
|
|
int c;
|
|
double p;
|
|
unsigned int pi;
|
|
};
|
|
|
|
IUB iub[] = {
|
|
{ 'a', 0.27, 5 },
|
|
{ 'c', 0.15, 4 },
|
|
{ 'g', 0.12, 3 },
|
|
{ 't', 0.27, 2 },
|
|
};
|
|
|
|
const unsigned char faceedgesidx[6][4] =
|
|
{
|
|
{ 4, 5, 8, 10 },
|
|
{ 6, 7, 9, 11 },
|
|
{ 0, 2, 8, 9 },
|
|
{ 1, 3, 10,11 },
|
|
{ 0, 1, 4, 6 },
|
|
{ 2, 3, 5, 7 },
|
|
};
|
|
|
|
int main( int argc, const char *argv[] ) {
|
|
printf("*%d,%d,%d,%d*\\n", iub[0].c, int(iub[1].p*100), iub[2].pi, faceedgesidx[3][2]);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*97,15,3,10*')
|
|
|
|
def test_conststructs(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
struct IUB {
|
|
int c;
|
|
double p;
|
|
unsigned int pi;
|
|
};
|
|
|
|
int main( int argc, const char *argv[] ) {
|
|
int before = 70;
|
|
IUB iub[] = {
|
|
{ 'a', 0.3029549426680, 5 },
|
|
{ 'c', 0.15, 4 },
|
|
{ 'g', 0.12, 3 },
|
|
{ 't', 0.27, 2 },
|
|
};
|
|
int after = 90;
|
|
printf("*%d,%d,%d,%d,%d,%d*\\n", before, iub[0].c, int(iub[1].p*100), iub[2].pi, int(iub[0].p*10000), after);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*70,97,15,3,3029,90*')
|
|
|
|
def test_mod_globalstruct(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
struct malloc_params {
|
|
size_t magic, page_size;
|
|
};
|
|
|
|
malloc_params mparams;
|
|
|
|
#define SIZE_T_ONE ((size_t)1)
|
|
#define page_align(S) (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE))
|
|
|
|
int main()
|
|
{
|
|
mparams.page_size = 4096;
|
|
printf("*%d,%d,%d,%d*\\n", mparams.page_size, page_align(1000), page_align(6000), page_align(66474));
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*4096,4096,8192,69632*')
|
|
|
|
def test_pystruct(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
// Based on CPython code
|
|
union PyGC_Head {
|
|
struct {
|
|
union PyGC_Head *gc_next;
|
|
union PyGC_Head *gc_prev;
|
|
size_t gc_refs;
|
|
} gc;
|
|
long double dummy; /* force worst-case alignment */
|
|
} ;
|
|
|
|
struct gc_generation {
|
|
PyGC_Head head;
|
|
int threshold; /* collection threshold */
|
|
int count; /* count of allocations or collections of younger
|
|
generations */
|
|
};
|
|
|
|
#define NUM_GENERATIONS 3
|
|
#define GEN_HEAD(n) (&generations[n].head)
|
|
|
|
/* linked lists of container objects */
|
|
static struct gc_generation generations[NUM_GENERATIONS] = {
|
|
/* PyGC_Head, threshold, count */
|
|
{{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0},
|
|
{{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0},
|
|
{{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0},
|
|
};
|
|
|
|
int main()
|
|
{
|
|
gc_generation *n = NULL;
|
|
printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n",
|
|
(int)(&n[0]),
|
|
(int)(&n[0].head),
|
|
(int)(&n[0].head.gc.gc_next),
|
|
(int)(&n[0].head.gc.gc_prev),
|
|
(int)(&n[0].head.gc.gc_refs),
|
|
(int)(&n[0].threshold), (int)(&n[0].count), (int)(&n[1])
|
|
);
|
|
printf("*%d,%d,%d*\\n",
|
|
(int)(&generations[0]) ==
|
|
(int)(&generations[0].head.gc.gc_next),
|
|
(int)(&generations[0]) ==
|
|
(int)(&generations[0].head.gc.gc_prev),
|
|
(int)(&generations[0]) ==
|
|
(int)(&generations[1])
|
|
);
|
|
int x1 = (int)(&generations[0]);
|
|
int x2 = (int)(&generations[1]);
|
|
printf("*%d*\\n", x1 == x2);
|
|
for (int i = 0; i < NUM_GENERATIONS; i++) {
|
|
PyGC_Head *list = GEN_HEAD(i);
|
|
printf("%d:%d,%d\\n", i, (int)list == (int)(list->gc.gc_prev), (int)list ==(int)(list->gc.gc_next));
|
|
}
|
|
printf("*%d,%d,%d*\\n", sizeof(PyGC_Head), sizeof(gc_generation), int(GEN_HEAD(2)) - int(GEN_HEAD(1)));
|
|
}
|
|
'''
|
|
if QUANTUM_SIZE == 1:
|
|
# Compressed memory. Note that sizeof() does give the fat sizes, however!
|
|
self.do_run(src, '*0,0,0,1,2,3,4,5*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,5*')
|
|
else:
|
|
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)
|
|
|
|
def test_sizeof(self):
|
|
# Has invalid writes between printouts
|
|
global SAFE_HEAP; SAFE_HEAP = 0
|
|
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "emscripten.h"
|
|
|
|
struct A { int x, y; };
|
|
|
|
int main( int argc, const char *argv[] ) {
|
|
int *a = new int[10];
|
|
int *b = new int[1];
|
|
int *c = new int[10];
|
|
for (int i = 0; i < 10; i++)
|
|
a[i] = 2;
|
|
*b = 5;
|
|
for (int i = 0; i < 10; i++)
|
|
c[i] = 8;
|
|
printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]);
|
|
// Should overwrite a, but not touch b!
|
|
memcpy(a, c, 10*sizeof(int));
|
|
printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]);
|
|
|
|
// Part 2
|
|
A as[3] = { { 5, 12 }, { 6, 990 }, { 7, 2 } };
|
|
memcpy(&as[0], &as[2], sizeof(A));
|
|
|
|
printf("*%d,%d,%d,%d,%d,%d*\\n", as[0].x, as[0].y, as[1].x, as[1].y, as[2].x, as[2].y);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*2,2,5,8,8***8,8,5,8,8***7,2,6,990,7,2*', [], lambda x: x.replace('\n', '*'))
|
|
|
|
def test_emscripten_api(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include "emscripten.h"
|
|
|
|
int main() {
|
|
EMSCRIPTEN_COMMENT("hello from the source");
|
|
emscripten_run_script("print('hello world' + '!')");
|
|
return 0;
|
|
}
|
|
'''
|
|
|
|
def check(filename):
|
|
src = open(filename, 'r').read()
|
|
assert '// hello from the source' in src
|
|
|
|
self.do_run(src, 'hello world!', post_build=check)
|
|
|
|
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 QUANTUM_SIZE == 1:
|
|
self.do_run(src, '''*4*\n0:22016,0,8,12\n1:22018,1,12,8\n''')
|
|
else:
|
|
self.do_run(src, '''*16*\n0:22016,0,32,48\n1:22018,1,48,32\n''')
|
|
|
|
def test_tinyfuncstr(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
struct Class {
|
|
static char *name1() { return "nameA"; }
|
|
char *name2() { return "nameB"; }
|
|
};
|
|
|
|
int main() {
|
|
printf("*%s,%s*\\n", Class::name1(), (new Class())->name2());
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*nameA,nameB*')
|
|
|
|
def test_llvmswitch(self):
|
|
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*')
|
|
|
|
def test_indirectbr(self):
|
|
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')
|
|
|
|
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 QUANTUM_SIZE == 1:
|
|
self.do_run(src, '*4,2,3*\n*6,2,3*')
|
|
else:
|
|
self.do_run(src, '*4,3,4*\n*6,4,6*')
|
|
|
|
def test_varargs(self):
|
|
if QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this')
|
|
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
|
|
void vary(const char *s, ...)
|
|
{
|
|
va_list v;
|
|
va_start(v, s);
|
|
char d[20];
|
|
vsnprintf(d, 20, s, v);
|
|
puts(d);
|
|
|
|
// Try it with copying
|
|
va_list tempva;
|
|
__va_copy(tempva, v);
|
|
vsnprintf(d, 20, s, tempva);
|
|
puts(d);
|
|
|
|
va_end(v);
|
|
}
|
|
|
|
void vary2(char color, const char *s, ...)
|
|
{
|
|
va_list v;
|
|
va_start(v, s);
|
|
char d[21];
|
|
d[0] = color;
|
|
vsnprintf(d+1, 20, s, v);
|
|
puts(d);
|
|
va_end(v);
|
|
}
|
|
|
|
#define GETMAX(pref, type) \
|
|
type getMax##pref(int num, ...) \
|
|
{ \
|
|
va_list vv; \
|
|
va_start(vv, num); \
|
|
type maxx = va_arg(vv, type); \
|
|
for (int i = 1; i < num; i++) \
|
|
{ \
|
|
type curr = va_arg(vv, type); \
|
|
maxx = curr > maxx ? curr : maxx; \
|
|
} \
|
|
va_end(vv); \
|
|
return maxx; \
|
|
}
|
|
GETMAX(i, int);
|
|
GETMAX(D, double);
|
|
|
|
int main() {
|
|
vary("*cheez: %d+%d*", 0, 24); // Also tests that '0' is not special as an array ender
|
|
vary("*albeit*"); // Should not fail with no var args in vararg function
|
|
vary2('Q', "%d*", 85);
|
|
|
|
int maxxi = getMaxi(6, 2, 5, 21, 4, -10, 19);
|
|
printf("maxxi:%d*\\n", maxxi);
|
|
double maxxD = getMaxD(6, (double)2.1, (double)5.1, (double)22.1, (double)4.1, (double)-10.1, (double)19.1);
|
|
printf("maxxD:%.2f*\\n", (float)maxxD);
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*cheez: 0+24*\n*cheez: 0+24*\n*albeit*\n*albeit*\nQ85*\nmaxxi:21*\nmaxxD:22.10*\n')
|
|
|
|
def test_stdlibs(self):
|
|
if USE_TYPED_ARRAYS == 2:
|
|
# Typed arrays = 2 + safe heap prints a warning that messes up our output.
|
|
global SAFE_HEAP; SAFE_HEAP = 0
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/time.h>
|
|
|
|
void clean()
|
|
{
|
|
printf("*cleaned*\\n");
|
|
}
|
|
|
|
int comparer(const void *a, const void *b) {
|
|
int aa = *((int*)a);
|
|
int bb = *((int*)b);
|
|
return aa - bb;
|
|
}
|
|
|
|
int main() {
|
|
// timeofday
|
|
timeval t;
|
|
gettimeofday(&t, NULL);
|
|
printf("*%d,%d\\n", int(t.tv_sec), int(t.tv_usec)); // should not crash
|
|
|
|
// atexit
|
|
atexit(clean);
|
|
|
|
// qsort
|
|
int values[6] = { 3, 2, 5, 1, 5, 6 };
|
|
qsort(values, 5, sizeof(int), comparer);
|
|
printf("*%d,%d,%d,%d,%d,%d*\\n", values[0], values[1], values[2], values[3], values[4], values[5]);
|
|
|
|
printf("*stdin==0:%d*\\n", stdin == 0); // check that external values are at least not NULL
|
|
printf("*%%*\\n");
|
|
printf("*%.1ld*\\n", 5);
|
|
|
|
printf("*%.1f*\\n", strtod("66", NULL)); // checks dependency system, as our strtod needs _isspace etc.
|
|
|
|
printf("*%ld*\\n", strtol("10", NULL, 0));
|
|
printf("*%ld*\\n", strtol("0", NULL, 0));
|
|
printf("*%ld*\\n", strtol("-10", NULL, 0));
|
|
printf("*%ld*\\n", strtol("12", NULL, 16));
|
|
|
|
printf("*%lu*\\n", strtoul("10", NULL, 0));
|
|
printf("*%lu*\\n", strtoul("0", NULL, 0));
|
|
printf("*%lu*\\n", strtoul("-10", NULL, 0));
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
|
|
self.do_run(src, '*1,2,3,5,5,6*\n*stdin==0:0*\n*%*\n*5*\n*66.0*\n*10*\n*0*\n*-10*\n*18*\n*10*\n*0*\n*4294967286*\n*cleaned*')
|
|
|
|
def test_time(self):
|
|
if USE_TYPED_ARRAYS == 2: return self.skip('Typed arrays = 2 truncate i64s')
|
|
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,
|
|
extra_emscripten_args=['-H', 'libc/time.h'])
|
|
#extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'])
|
|
|
|
def test_statics(self):
|
|
# static initializers save i16 but load i8 for some reason
|
|
global SAFE_HEAP, SAFE_HEAP_LINES
|
|
if SAFE_HEAP:
|
|
SAFE_HEAP = 3
|
|
SAFE_HEAP_LINES = ['src.cpp:19', 'src.cpp:26']
|
|
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define CONSTRLEN 32
|
|
|
|
void conoutfv(const char *fmt)
|
|
{
|
|
static char buf[CONSTRLEN];
|
|
strcpy(buf, fmt);
|
|
puts(buf);
|
|
}
|
|
|
|
struct XYZ {
|
|
float x, y, z;
|
|
XYZ(float a, float b, float c) : x(a), y(b), z(c) { }
|
|
static const XYZ& getIdentity()
|
|
{
|
|
static XYZ iT(1,2,3);
|
|
return iT;
|
|
}
|
|
};
|
|
struct S {
|
|
static const XYZ& getIdentity()
|
|
{
|
|
static const XYZ iT(XYZ::getIdentity());
|
|
return iT;
|
|
}
|
|
};
|
|
|
|
int main() {
|
|
conoutfv("*staticccz*");
|
|
printf("*%.2f,%.2f,%.2f*\\n", S::getIdentity().x, S::getIdentity().y, S::getIdentity().z);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*staticccz*\n*1.00,2.00,3.00*')
|
|
|
|
def test_copyop(self):
|
|
# clang generated code is vulnerable to this, as it uses
|
|
# memcpy for assignments, with hardcoded numbers of bytes
|
|
# (llvm-gcc copies items one by one). See QUANTUM_SIZE in
|
|
# settings.js.
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
struct vec {
|
|
double x,y,z;
|
|
vec() : x(0), y(0), z(0) { };
|
|
vec(const double a, const double b, const double c) : x(a), y(b), z(c) { };
|
|
};
|
|
|
|
struct basis {
|
|
vec a, b, c;
|
|
basis(const vec& v) {
|
|
a=v; // should not touch b!
|
|
printf("*%.2f,%.2f,%.2f*\\n", b.x, b.y, b.z);
|
|
}
|
|
};
|
|
|
|
int main() {
|
|
basis B(vec(1,0,0));
|
|
|
|
// Part 2: similar problem with memset and memmove
|
|
int x = 1, y = 77, z = 2;
|
|
memset((void*)&x, 0, sizeof(int));
|
|
memset((void*)&z, 0, sizeof(int));
|
|
printf("*%d,%d,%d*\\n", x, y, z);
|
|
memcpy((void*)&x, (void*)&z, sizeof(int));
|
|
memcpy((void*)&z, (void*)&x, sizeof(int));
|
|
printf("*%d,%d,%d*\\n", x, y, z);
|
|
memmove((void*)&x, (void*)&z, sizeof(int));
|
|
memmove((void*)&z, (void*)&x, sizeof(int));
|
|
printf("*%d,%d,%d*\\n", x, y, z);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, '*0.00,0.00,0.00*\n*0,77,0*\n*0,77,0*\n*0,77,0*')
|
|
|
|
def test_memcpy(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]\\n", i, j, k);
|
|
reset(buffer);
|
|
memcpy(buffer+i, buffer+j, k);
|
|
dump(buffer);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src)
|
|
|
|
def test_nestedstructs(self):
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include "emscripten.h"
|
|
|
|
struct base {
|
|
int x;
|
|
float y;
|
|
union {
|
|
int a;
|
|
float b;
|
|
};
|
|
char c;
|
|
};
|
|
|
|
struct hashtableentry {
|
|
int key;
|
|
base data;
|
|
};
|
|
|
|
struct hashset {
|
|
typedef hashtableentry entry;
|
|
struct chain { entry elem; chain *next; };
|
|
// struct chainchunk { chain chains[100]; chainchunk *next; };
|
|
};
|
|
|
|
struct hashtable : hashset {
|
|
hashtable() {
|
|
base *b = NULL;
|
|
entry *e = NULL;
|
|
chain *c = NULL;
|
|
printf("*%d,%d,%d,%d,%d,%d|%d,%d,%d,%d,%d,%d,%d,%d|%d,%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n",
|
|
sizeof(base),
|
|
int(&(b->x)), int(&(b->y)), int(&(b->a)), int(&(b->b)), int(&(b->c)),
|
|
sizeof(hashtableentry),
|
|
int(&(e->key)), int(&(e->data)), int(&(e->data.x)), int(&(e->data.y)), int(&(e->data.a)), int(&(e->data.b)), int(&(e->data.c)),
|
|
sizeof(hashset::chain),
|
|
int(&(c->elem)), int(&(c->next)), int(&(c->elem.key)), int(&(c->elem.data)), int(&(c->elem.data.x)), int(&(c->elem.data.y)), int(&(c->elem.data.a)), int(&(c->elem.data.b)), int(&(c->elem.data.c))
|
|
);
|
|
}
|
|
};
|
|
|
|
struct B { char buffer[62]; int last; char laster; char laster2; };
|
|
|
|
struct Bits {
|
|
unsigned short A : 1;
|
|
unsigned short B : 1;
|
|
unsigned short C : 1;
|
|
unsigned short D : 1;
|
|
unsigned short x1 : 1;
|
|
unsigned short x2 : 1;
|
|
unsigned short x3 : 1;
|
|
unsigned short x4 : 1;
|
|
};
|
|
|
|
int main() {
|
|
hashtable t;
|
|
|
|
// Part 2 - the char[] should be compressed, BUT have a padding space at the end so the next
|
|
// one is aligned properly. Also handle char; char; etc. properly.
|
|
B *b = NULL;
|
|
printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", int(b), int(&(b->buffer)), int(&(b->buffer[0])), int(&(b->buffer[1])), int(&(b->buffer[2])),
|
|
int(&(b->last)), int(&(b->laster)), int(&(b->laster2)), sizeof(B));
|
|
|
|
// Part 3 - bitfields, and small structures
|
|
Bits *b2 = NULL;
|
|
printf("*%d*\\n", sizeof(Bits));
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
if QUANTUM_SIZE == 1:
|
|
# Compressed memory. Note that sizeof() does give the fat sizes, however!
|
|
self.do_run(src, '*16,0,1,2,2,3|20,0,1,1,2,3,3,4|24,0,5,0,1,1,2,3,3,4*\n*0,0,0,1,2,62,63,64,72*\n*2*')
|
|
else:
|
|
# Bloated memory; same layout as C/C++
|
|
self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*')
|
|
|
|
def test_dlfcn_basic(self):
|
|
global BUILD_AS_SHARED_LIB
|
|
lib_src = '''
|
|
#include <cstdio>
|
|
|
|
class Foo {
|
|
public:
|
|
Foo() {
|
|
printf("Constructing lib object.\\n");
|
|
}
|
|
};
|
|
|
|
Foo global;
|
|
'''
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.cpp')
|
|
BUILD_AS_SHARED_LIB = 1
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
src = '''
|
|
#include <cstdio>
|
|
#include <dlfcn.h>
|
|
|
|
class Bar {
|
|
public:
|
|
Bar() {
|
|
printf("Constructing main object.\\n");
|
|
}
|
|
};
|
|
|
|
Bar global;
|
|
|
|
int main() {
|
|
dlopen("liblib.so", RTLD_NOW);
|
|
return 0;
|
|
}
|
|
'''
|
|
BUILD_AS_SHARED_LIB = 0
|
|
def add_pre_run_and_checks(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
'''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);'''
|
|
)
|
|
open(filename, 'w').write(src)
|
|
self.do_run(src, 'Constructing main object.\nConstructing lib object.\n',
|
|
post_build=add_pre_run_and_checks)
|
|
|
|
def test_dlfcn_qsort(self):
|
|
global BUILD_AS_SHARED_LIB, EXPORTED_FUNCTIONS
|
|
lib_src = '''
|
|
int lib_cmp(const void* left, const void* right) {
|
|
const int* a = (const int*) left;
|
|
const int* b = (const int*) right;
|
|
if(*a > *b) return 1;
|
|
else if(*a == *b) return 0;
|
|
else return -1;
|
|
}
|
|
|
|
typedef int (*CMP_TYPE)(const void*, const void*);
|
|
|
|
extern "C" CMP_TYPE get_cmp() {
|
|
return lib_cmp;
|
|
}
|
|
'''
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.cpp')
|
|
BUILD_AS_SHARED_LIB = 1
|
|
EXPORTED_FUNCTIONS = ['_get_cmp']
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <dlfcn.h>
|
|
|
|
typedef int (*CMP_TYPE)(const void*, const void*);
|
|
|
|
int main_cmp(const void* left, const void* right) {
|
|
const int* a = (const int*) left;
|
|
const int* b = (const int*) right;
|
|
if(*a < *b) return 1;
|
|
else if(*a == *b) return 0;
|
|
else return -1;
|
|
}
|
|
|
|
int main() {
|
|
void* lib_handle;
|
|
CMP_TYPE (*getter_ptr)();
|
|
CMP_TYPE lib_cmp_ptr;
|
|
int arr[5] = {4, 2, 5, 1, 3};
|
|
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
if (lib_handle == NULL) {
|
|
printf("Could not load lib.\\n");
|
|
return 1;
|
|
}
|
|
getter_ptr = (CMP_TYPE (*)()) dlsym(lib_handle, "get_cmp");
|
|
if (getter_ptr == NULL) {
|
|
printf("Could not find func.\\n");
|
|
return 1;
|
|
}
|
|
lib_cmp_ptr = getter_ptr();
|
|
|
|
qsort((void*)arr, 5, sizeof(int), main_cmp);
|
|
printf("Sort with main comparison: ");
|
|
for (int i = 0; i < 5; i++) {
|
|
printf("%d ", arr[i]);
|
|
}
|
|
printf("\\n");
|
|
|
|
qsort((void*)arr, 5, sizeof(int), lib_cmp_ptr);
|
|
printf("Sort with lib comparison: ");
|
|
for (int i = 0; i < 5; i++) {
|
|
printf("%d ", arr[i]);
|
|
}
|
|
printf("\\n");
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
BUILD_AS_SHARED_LIB = 0
|
|
EXPORTED_FUNCTIONS = ['_main']
|
|
def add_pre_run_and_checks(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)
|
|
|
|
def test_dlfcn_data_and_fptr(self):
|
|
global LLVM_OPTS
|
|
if LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
|
|
|
|
global BUILD_AS_SHARED_LIB, EXPORTED_FUNCTIONS, EXPORTED_GLOBALS
|
|
lib_src = '''
|
|
#include <stdio.h>
|
|
|
|
int global = 42;
|
|
|
|
extern void parent_func(); // a function that is defined in the parent
|
|
|
|
void lib_fptr() {
|
|
printf("Second calling lib_fptr from main.\\n");
|
|
parent_func();
|
|
// call it also through a pointer, to check indexizing
|
|
void (*p_f)();
|
|
p_f = parent_func;
|
|
p_f();
|
|
}
|
|
|
|
extern "C" void (*func(int x, void(*fptr)()))() {
|
|
printf("In func: %d\\n", x);
|
|
fptr();
|
|
return lib_fptr;
|
|
}
|
|
'''
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'liblib.cpp')
|
|
BUILD_AS_SHARED_LIB = 1
|
|
EXPORTED_FUNCTIONS = ['_func']
|
|
EXPORTED_GLOBALS = ['_global']
|
|
self.build(lib_src, dirname, filename)
|
|
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
|
|
|
|
src = '''
|
|
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
|
|
typedef void (*FUNCTYPE(int, void(*)()))();
|
|
|
|
FUNCTYPE func;
|
|
|
|
void parent_func() {
|
|
printf("parent_func called from child\\n");
|
|
}
|
|
|
|
void main_fptr() {
|
|
printf("First calling main_fptr from lib.\\n");
|
|
}
|
|
|
|
int main() {
|
|
void* lib_handle;
|
|
FUNCTYPE* func_fptr;
|
|
|
|
// Test basic lib loading.
|
|
lib_handle = dlopen("liblib.so", RTLD_NOW);
|
|
if (lib_handle == NULL) {
|
|
printf("Could not load lib.\\n");
|
|
return 1;
|
|
}
|
|
|
|
// Test looked up function.
|
|
func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func");
|
|
// Load twice to test cache.
|
|
func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func");
|
|
if (func_fptr == NULL) {
|
|
printf("Could not find func.\\n");
|
|
return 1;
|
|
}
|
|
|
|
// Test passing function pointers across module bounds.
|
|
void (*fptr)() = func_fptr(13, main_fptr);
|
|
fptr();
|
|
|
|
// Test global data.
|
|
int* global = (int*) dlsym(lib_handle, "global");
|
|
if (global == NULL) {
|
|
printf("Could not find global.\\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("Var: %d\\n", *global);
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
BUILD_AS_SHARED_LIB = 0
|
|
EXPORTED_FUNCTIONS = ['_main']
|
|
EXPORTED_GLOBALS = []
|
|
def add_pre_run_and_checks(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)
|
|
|
|
def test_dlfcn_alias(self):
|
|
global BUILD_AS_SHARED_LIB, EXPORTED_FUNCTIONS, INCLUDE_FULL_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')
|
|
BUILD_AS_SHARED_LIB = 1
|
|
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;
|
|
}
|
|
'''
|
|
BUILD_AS_SHARED_LIB = 0
|
|
INCLUDE_FULL_LIBRARY = 1
|
|
EXPORTED_FUNCTIONS = ['_main']
|
|
def add_pre_run_and_checks(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'])
|
|
INCLUDE_FULL_LIBRARY = 0
|
|
|
|
def test_dlfcn_varargs(self):
|
|
if QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this')
|
|
global BUILD_AS_SHARED_LIB, EXPORTED_FUNCTIONS
|
|
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')
|
|
BUILD_AS_SHARED_LIB = 1
|
|
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;
|
|
}
|
|
'''
|
|
BUILD_AS_SHARED_LIB = 0
|
|
EXPORTED_FUNCTIONS = ['_main']
|
|
def add_pre_run_and_checks(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
'''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);'''
|
|
)
|
|
open(filename, 'w').write(src)
|
|
self.do_run(src, '100*200*13*42*',
|
|
output_nicerizer=lambda x: x.replace('\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):
|
|
if USE_TYPED_ARRAYS == 2: return self.skip('Typed arrays = 2 truncate doubles')
|
|
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("84e+420", &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);
|
|
return 0;
|
|
}
|
|
'''
|
|
expected = '''
|
|
0
|
|
0
|
|
0
|
|
1
|
|
1
|
|
1
|
|
123
|
|
123.456
|
|
-123.456
|
|
1.23457e+18
|
|
1.23457e+68
|
|
8.4e+221
|
|
inf
|
|
1.23e-48
|
|
1.23e-248
|
|
0
|
|
1.234e+57
|
|
10
|
|
'''
|
|
self.do_run(src, re.sub(r'\n\s+', '\n', expected))
|
|
|
|
def test_parseInt(self):
|
|
if USE_TYPED_ARRAYS != 0: return self.skip('Typed arrays truncate i64')
|
|
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):
|
|
if USE_TYPED_ARRAYS != 0: return self.skip('Typed arrays truncate i64')
|
|
src = open(path_from_root('tests', 'printf', 'test.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'printf', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_printf_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))
|
|
|
|
def test_langinfo(self):
|
|
src = open(path_from_root('tests', 'langinfo', 'test.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'langinfo', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/langinfo.h'])
|
|
|
|
def test_files(self):
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both.
|
|
def post(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{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()
|
|
self.do_run(src, 'size: 7\ndata: 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',
|
|
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
|
|
|
def test_folders(self):
|
|
def add_pre_run(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)
|
|
|
|
def test_stat(self):
|
|
def add_pre_run(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)
|
|
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'])
|
|
|
|
def test_fcntl(self):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
"FS.createDataFile('/', 'test', 'abcdef', true, true);"
|
|
)
|
|
open(filename, 'w').write(src)
|
|
src = open(path_from_root('tests', 'fcntl', 'src.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'fcntl', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
|
|
|
def test_fcntl_open(self):
|
|
def add_pre_run(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)
|
|
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'])
|
|
|
|
def test_fcntl_misc(self):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
"FS.createDataFile('/', 'test', 'abcdef', true, true);"
|
|
)
|
|
open(filename, 'w').write(src)
|
|
src = open(path_from_root('tests', 'fcntl-misc', 'src.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'fcntl-misc', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
|
|
|
def test_poll(self):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
'''
|
|
FS.createDataFile('/', 'file', 'abcdef', true, true);
|
|
FS.createDevice('/', 'device', function() {}, function() {});
|
|
'''
|
|
)
|
|
open(filename, 'w').write(src)
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
|
|
int main() {
|
|
struct pollfd multi[5];
|
|
multi[0].fd = open("/file", O_RDONLY, 0777);
|
|
multi[1].fd = open("/device", O_RDONLY, 0777);
|
|
multi[2].fd = 123;
|
|
multi[3].fd = open("/file", O_RDONLY, 0777);
|
|
multi[4].fd = open("/file", O_RDONLY, 0777);
|
|
multi[0].events = POLLIN | POLLOUT | POLLNVAL | POLLERR;
|
|
multi[1].events = POLLIN | POLLOUT | POLLNVAL | POLLERR;
|
|
multi[2].events = POLLIN | POLLOUT | POLLNVAL | POLLERR;
|
|
multi[3].events = 0x00;
|
|
multi[4].events = POLLOUT | POLLNVAL | POLLERR;
|
|
|
|
printf("ret: %d\n", poll(multi, 5, 123));
|
|
printf("errno: %d\n", errno);
|
|
printf("multi[0].revents: %d\n", multi[0].revents == (POLLIN | POLLOUT));
|
|
printf("multi[1].revents: %d\n", multi[1].revents == (POLLIN | POLLOUT));
|
|
printf("multi[2].revents: %d\n", multi[2].revents == POLLNVAL);
|
|
printf("multi[3].revents: %d\n", multi[3].revents == 0);
|
|
printf("multi[4].revents: %d\n", multi[4].revents == POLLOUT);
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
expected = r'''
|
|
ret: 4
|
|
errno: 0
|
|
multi[0].revents: 1
|
|
multi[1].revents: 1
|
|
multi[2].revents: 1
|
|
multi[3].revents: 1
|
|
multi[4].revents: 1
|
|
'''
|
|
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h,poll.h'])
|
|
|
|
def test_statvfs(self):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <sys/statvfs.h>
|
|
|
|
int main() {
|
|
struct statvfs s;
|
|
|
|
printf("result: %d\n", statvfs("/test", &s));
|
|
printf("errno: %d\n", errno);
|
|
|
|
printf("f_bsize: %lu\n", s.f_bsize);
|
|
printf("f_frsize: %lu\n", s.f_frsize);
|
|
printf("f_blocks: %lu\n", s.f_blocks);
|
|
printf("f_bfree: %lu\n", s.f_bfree);
|
|
printf("f_bavail: %lu\n", s.f_bavail);
|
|
printf("f_files: %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
|
|
f_ffree: 1000000
|
|
f_favail: 1000000
|
|
f_fsid: 42
|
|
f_flag: 2
|
|
f_namemax: 255
|
|
'''
|
|
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
|
|
|
|
def test_libgen(self):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <libgen.h>
|
|
|
|
int main() {
|
|
char p1[16] = "/usr/lib", p1x[16] = "/usr/lib";
|
|
printf("%s -> ", p1);
|
|
printf("%s : %s\n", dirname(p1x), basename(p1));
|
|
|
|
char p2[16] = "/usr", p2x[16] = "/usr";
|
|
printf("%s -> ", p2);
|
|
printf("%s : %s\n", dirname(p2x), basename(p2));
|
|
|
|
char p3[16] = "/usr/", p3x[16] = "/usr/";
|
|
printf("%s -> ", p3);
|
|
printf("%s : %s\n", dirname(p3x), basename(p3));
|
|
|
|
char p4[16] = "/usr/lib///", p4x[16] = "/usr/lib///";
|
|
printf("%s -> ", p4);
|
|
printf("%s : %s\n", dirname(p4x), basename(p4));
|
|
|
|
char p5[16] = "/", p5x[16] = "/";
|
|
printf("%s -> ", p5);
|
|
printf("%s : %s\n", dirname(p5x), basename(p5));
|
|
|
|
char p6[16] = "///", p6x[16] = "///";
|
|
printf("%s -> ", p6);
|
|
printf("%s : %s\n", dirname(p6x), basename(p6));
|
|
|
|
char p7[16] = "/usr/../lib/..", p7x[16] = "/usr/../lib/..";
|
|
printf("%s -> ", p7);
|
|
printf("%s : %s\n", dirname(p7x), basename(p7));
|
|
|
|
char p8[16] = "", p8x[16] = "";
|
|
printf("(empty) -> %s : %s\n", dirname(p8x), basename(p8));
|
|
|
|
printf("(null) -> %s : %s\n", dirname(0), basename(0));
|
|
|
|
return 0;
|
|
}
|
|
'''
|
|
expected = '''
|
|
/usr/lib -> /usr : lib
|
|
/usr -> / : usr
|
|
/usr/ -> / : usr
|
|
/usr/lib/// -> /usr : lib
|
|
/ -> / : /
|
|
/// -> / : /
|
|
/usr/../lib/.. -> /usr/../lib : ..
|
|
(empty) -> . : .
|
|
(null) -> . : .
|
|
'''
|
|
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
|
|
|
|
def test_utime(self):
|
|
def add_pre_run_and_checks(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)
|
|
|
|
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)
|
|
|
|
def test_fs_base(self):
|
|
global INCLUDE_FULL_LIBRARY; INCLUDE_FULL_LIBRARY = 1
|
|
try:
|
|
def addJS(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(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:
|
|
INCLUDE_FULL_LIBRARY = 0
|
|
|
|
def test_unistd_access(self):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(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):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(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):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(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):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(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):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(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):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(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):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(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):
|
|
def add_pre_run(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
open(path_from_root('tests', 'unistd', 'io.js'), 'r').read()
|
|
)
|
|
open(filename, 'w').write(src)
|
|
src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read()
|
|
self.do_run(src, expected, post_build=add_pre_run)
|
|
|
|
def test_unistd_misc(self):
|
|
src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'unistd', 'misc.out'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_uname(self):
|
|
src = r'''
|
|
#include <stdio.h>
|
|
#include <sys/utsname.h>
|
|
|
|
int main() {
|
|
struct utsname u;
|
|
printf("ret: %d\n", uname(&u));
|
|
printf("sysname: %s\n", u.sysname);
|
|
printf("nodename: %s\n", u.nodename);
|
|
printf("release: %s\n", u.release);
|
|
printf("version: %s\n", u.version);
|
|
printf("machine: %s\n", u.machine);
|
|
printf("invalid: %d\n", uname(0));
|
|
return 0;
|
|
}
|
|
'''
|
|
expected = '''
|
|
ret: 0
|
|
sysname: Emscripten
|
|
nodename: emscripten
|
|
release: 1.0
|
|
version: #1
|
|
machine: x86-JS
|
|
'''
|
|
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
|
|
|
|
def test_env(self):
|
|
src = open(path_from_root('tests', 'env', 'src.c'), 'r').read()
|
|
expected = open(path_from_root('tests', 'env', 'output.txt'), 'r').read()
|
|
self.do_run(src, expected)
|
|
|
|
def test_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.
|
|
global CORRECT_SIGNS; 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
|
|
|
|
### 'Big' tests
|
|
|
|
def test_fannkuch(self):
|
|
results = [ (1,0), (2,1), (3,2), (4,4), (5,7), (6,10), (7, 16), (8,22) ]
|
|
for i, j in results:
|
|
src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read()
|
|
self.do_run(src, 'Pfannkuchen(%d) = %d.' % (i,j), [str(i)], no_build=i>1)
|
|
|
|
def test_raytrace(self):
|
|
global USE_TYPED_ARRAYS
|
|
if USE_TYPED_ARRAYS == 2: return self.skip('Relies on double values')
|
|
|
|
src = open(path_from_root('tests', 'raytrace.cpp'), 'r').read()
|
|
output = open(path_from_root('tests', 'raytrace.ppm'), 'r').read()
|
|
self.do_run(src, output, ['3', '16'])#, build_ll_hook=self.do_autodebug)
|
|
|
|
def test_fasta(self):
|
|
results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''),
|
|
(50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ]
|
|
for 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)
|
|
|
|
def test_dlmalloc(self):
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 2
|
|
global CORRECT_SIGNS_LINES; CORRECT_SIGNS_LINES = ['src.cpp:' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]]
|
|
global TOTAL_MEMORY; TOTAL_MEMORY = 100*1024*1024 # needed with typed arrays
|
|
|
|
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)
|
|
|
|
# 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)
|
|
|
|
def zzztest_gl(self):
|
|
# Switch to gcc from g++ - we don't compile properly otherwise (why?)
|
|
global COMPILER
|
|
COMPILER = COMPILER.replace('++', '')
|
|
|
|
def post(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
'''Module["__CANVAS__"] = {
|
|
getContext: function() {},
|
|
};'''
|
|
)
|
|
open(filename, 'w').write(src)
|
|
self.do_run(path_from_root('tests', 'gl'), '*?*', main_file='sdl_ogl.c', post_build=post)
|
|
|
|
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'])
|
|
|
|
# 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('''
|
|
#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_cubescript(self):
|
|
global COMPILER_TEST_OPTS; COMPILER_TEST_OPTS = [] # remove -g, so we have one test without it by default
|
|
|
|
global SAFE_HEAP; SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code...
|
|
|
|
# Overflows happen in hash loop
|
|
global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1
|
|
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0
|
|
|
|
self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp')
|
|
#build_ll_hook=self.do_autodebug)
|
|
|
|
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')
|
|
|
|
#### Code snippet that is helpful to search for nonportable optimizations ####
|
|
#global LLVM_OPT_OPTS
|
|
#for opt in ['-aa-eval', '-adce', '-always-inline', '-argpromotion', '-basicaa', '-basiccg', '-block-placement', '-break-crit-edges', '-codegenprepare', '-constmerge', '-constprop', '-correlated-propagation', '-count-aa', '-dce', '-deadargelim', '-deadtypeelim', '-debug-aa', '-die', '-domfrontier', '-domtree', '-dse', '-extract-blocks', '-functionattrs', '-globaldce', '-globalopt', '-globalsmodref-aa', '-gvn', '-indvars', '-inline', '-insert-edge-profiling', '-insert-optimal-edge-profiling', '-instcombine', '-instcount', '-instnamer', '-internalize', '-intervals', '-ipconstprop', '-ipsccp', '-iv-users', '-jump-threading', '-lazy-value-info', '-lcssa', '-lda', '-libcall-aa', '-licm', '-lint', '-live-values', '-loop-deletion', '-loop-extract', '-loop-extract-single', '-loop-index-split', '-loop-reduce', '-loop-rotate', '-loop-unroll', '-loop-unswitch', '-loops', '-loopsimplify', '-loweratomic', '-lowerinvoke', '-lowersetjmp', '-lowerswitch', '-mem2reg', '-memcpyopt', '-memdep', '-mergefunc', '-mergereturn', '-module-debuginfo', '-no-aa', '-no-profile', '-partial-inliner', '-partialspecialization', '-pointertracking', '-postdomfrontier', '-postdomtree', '-preverify', '-prune-eh', '-reassociate', '-reg2mem', '-regions', '-scalar-evolution', '-scalarrepl', '-sccp', '-scev-aa', '-simplify-libcalls', '-simplify-libcalls-halfpowr', '-simplifycfg', '-sink', '-split-geps', '-sretpromotion', '-strip', '-strip-dead-debug-info', '-strip-dead-prototypes', '-strip-debug-declare', '-strip-nondebug', '-tailcallelim', '-tailduplicate', '-targetdata', '-tbaa']:
|
|
# LLVM_OPT_OPTS = [opt]
|
|
# try:
|
|
# self.do_run(path_from_root(['third_party']), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c')
|
|
# print opt, "ok"
|
|
# except:
|
|
# print opt, "FAIL"
|
|
|
|
def test_lua(self):
|
|
global QUANTUM_SIZE
|
|
if QUANTUM_SIZE == 1: return self.skip('TODO: make this work')
|
|
|
|
# Overflows in luaS_newlstr hash loop
|
|
global SAFE_HEAP; SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type)
|
|
global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1
|
|
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 1 # Not sure why, but needed
|
|
global INIT_STACK; 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'])
|
|
|
|
def get_building_dir(self):
|
|
return os.path.join(self.get_dir(), 'building')
|
|
|
|
# Build a library into a .bc file. We build the .bc file once and cache it for all our tests. (We cache in
|
|
# memory since the test directory is destroyed and recreated for each test. Note that we cache separately
|
|
# for different compilers)
|
|
def get_library(self, name, generated_libs, configure=['./configure'], configure_args=[], make=['make'], make_args=['-j', '2'], cache=True, build_subdir=None):
|
|
if type(generated_libs) is not list: generated_libs = [generated_libs]
|
|
if build_subdir and configure.startswith('./'):
|
|
configure = '.' + configure
|
|
|
|
if GlobalCache is not None:
|
|
cache_name = name + '|' + COMPILER
|
|
if cache and GlobalCache.get(cache_name):
|
|
print >> sys.stderr, '<load build from cache> ',
|
|
bc_file = os.path.join(self.get_dir(), 'lib' + name + '.bc')
|
|
f = open(bc_file, 'wb')
|
|
f.write(GlobalCache[cache_name])
|
|
f.close()
|
|
return bc_file
|
|
|
|
temp_dir = self.get_building_dir()
|
|
project_dir = os.path.join(temp_dir, name)
|
|
shutil.copytree(path_from_root('tests', name), project_dir) # Useful in debugging sometimes to comment this out
|
|
os.chdir(project_dir)
|
|
if build_subdir:
|
|
try:
|
|
os.mkdir('cbuild')
|
|
except:
|
|
pass
|
|
os.chdir(os.path.join(project_dir, 'cbuild'))
|
|
env = os.environ.copy()
|
|
env['RANLIB'] = env['AR'] = env['CXX'] = env['CC'] = env['LIBTOOL'] = EMMAKEN
|
|
env['EMMAKEN_COMPILER'] = COMPILER
|
|
env['EMSCRIPTEN_TOOLS'] = path_from_root('tools')
|
|
env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(COMPILER_OPTS + COMPILER_TEST_OPTS) # Normal CFLAGS is ignored by some configure's.
|
|
if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |make| call)
|
|
env['EMMAKEN_JUST_CONFIGURE'] = '1'
|
|
Popen(configure + configure_args, stdout=open(os.path.join(self.get_dir(), 'configure'), 'w'),
|
|
stderr=open(os.path.join(self.get_dir(), 'configure_err'), 'w'), env=env).communicate()[0]
|
|
del env['EMMAKEN_JUST_CONFIGURE']
|
|
Popen(make + make_args, stdout=open(os.path.join(self.get_dir(), 'make'), 'w'),
|
|
stderr=open(os.path.join(self.get_dir(), 'make_err'), 'w'), env=env).communicate()[0]
|
|
bc_file = os.path.join(project_dir, 'bc.bc')
|
|
self.do_link(map(lambda lib: os.path.join(project_dir, 'cbuild', lib) if build_subdir else os.path.join(project_dir, lib), generated_libs), bc_file)
|
|
if cache and GlobalCache is not None:
|
|
print >> sys.stderr, '<save build into cache> ',
|
|
GlobalCache[cache_name] = open(bc_file, 'rb').read()
|
|
return bc_file
|
|
|
|
def get_freetype(self):
|
|
global INIT_STACK; INIT_STACK = 1 # TODO: Investigate why this is necessary
|
|
|
|
return self.get_library('freetype', os.path.join('objs', '.libs', 'libfreetype.a.bc'))
|
|
|
|
def test_freetype(self):
|
|
if QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix')
|
|
|
|
if LLVM_OPTS: global RELOOP; RELOOP = 0 # Too slow; we do care about typed arrays and OPTIMIZE though
|
|
|
|
global CORRECT_SIGNS
|
|
if CORRECT_SIGNS == 0: CORRECT_SIGNS = 1 # Not sure why, but needed
|
|
|
|
def post(filename):
|
|
# Embed the font into the document
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
'''FS.createDataFile('/', 'font.ttf', %s, true, false);''' % str(
|
|
map(ord, open(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(),
|
|
['font.ttf', 'test!', '150', '120', '25'],
|
|
libraries=[self.get_freetype()],
|
|
includes=[path_from_root('tests', 'freetype', 'include')],
|
|
post_build=post)
|
|
#build_ll_hook=self.do_autodebug)
|
|
|
|
def test_sqlite(self):
|
|
# gcc -O3 -I/home/alon/Dev/emscripten/tests/sqlite -ldl src.c
|
|
global QUANTUM_SIZE, OPTIMIZE, RELOOP, USE_TYPED_ARRAYS
|
|
if QUANTUM_SIZE == 1: return self.skip('TODO FIXME')
|
|
RELOOP = 0 # too slow
|
|
|
|
auto_optimize_data = read_auto_optimize_data(path_from_root('tests', 'sqlite', 'sqlite-autooptimize.fails.txt'))
|
|
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 2
|
|
global CORRECT_SIGNS_LINES; CORRECT_SIGNS_LINES = auto_optimize_data['signs_lines']
|
|
global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 0
|
|
global CORRECT_ROUNDINGS; CORRECT_ROUNDINGS = 0
|
|
global SAFE_HEAP; SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff
|
|
global DISABLE_EXCEPTION_CATCHING; DISABLE_EXCEPTION_CATCHING = 1
|
|
global FAST_MEMORY; FAST_MEMORY = 4*1024*1024
|
|
global EXPORTED_FUNCTIONS; EXPORTED_FUNCTIONS = ['_main', '_sqlite3_open', '_sqlite3_close', '_sqlite3_exec', '_sqlite3_free', '_callback'];
|
|
|
|
global INVOKE_RUN; INVOKE_RUN = 0 # We append code that does run() ourselves
|
|
|
|
def post(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()
|
|
|
|
self.do_run(r'''
|
|
#define SQLITE_DISABLE_LFS
|
|
#define LONGDOUBLE_TYPE double
|
|
#define SQLITE_INT64_TYPE int
|
|
#define SQLITE_THREADSAFE 0
|
|
''' + open(path_from_root('tests', 'sqlite', 'sqlite3.c'), 'r').read() +
|
|
open(path_from_root('tests', 'sqlite', 'benchmark.c'), 'r').read(),
|
|
open(path_from_root('tests', 'sqlite', 'benchmark.txt'), 'r').read(),
|
|
includes=[path_from_root('tests', 'sqlite')],
|
|
force_c=True,
|
|
extra_emscripten_args=['-m'],
|
|
js_engines=[SPIDERMONKEY_ENGINE], # V8 is slow
|
|
post_build=post)#,build_ll_hook=self.do_autodebug)
|
|
|
|
def test_zlib(self):
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 1
|
|
|
|
self.do_run(open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(),
|
|
open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
|
|
libraries=[self.get_library('zlib', os.path.join('libz.a.bc'), make_args=['libz.a'])],
|
|
includes=[path_from_root('tests', 'zlib')],
|
|
force_c=True)
|
|
|
|
def zzztest_glibc(self):
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 1
|
|
global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1
|
|
global CORRECT_ROUNDINGS; CORRECT_ROUNDINGS = 1
|
|
|
|
self.do_run(r'''
|
|
#include <stdio.h>
|
|
int main() { printf("hai\n"); return 1; }
|
|
''',
|
|
libraries=[self.get_library('glibc', [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-sanity-checks'])],
|
|
includes=[path_from_root('tests', 'glibc', 'include')])
|
|
|
|
def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long
|
|
global SAFE_HEAP, SAFE_HEAP_LINES, USE_TYPED_ARRAYS, LLVM_OPTS
|
|
|
|
if LLVM_OPTS: SAFE_HEAP = 0 # Optimizations make it so we do not have debug info on the line we need to ignore
|
|
|
|
if USE_TYPED_ARRAYS == 2: return self.skip('We have slightly different rounding here for some reason. TODO: activate this')
|
|
|
|
if SAFE_HEAP:
|
|
# Ignore bitfield warnings
|
|
SAFE_HEAP = 3
|
|
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(),
|
|
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
|
|
|
|
def test_poppler(self):
|
|
global RELOOP, LLVM_OPTS, USE_TYPED_ARRAYS, QUANTUM_SIZE
|
|
|
|
# llvm-link failure when using clang, LLVM bug 9498, still relevant?
|
|
if RELOOP or LLVM_OPTS: return self.skip('TODO')
|
|
if USE_TYPED_ARRAYS == 2 or QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix')
|
|
|
|
USE_TYPED_ARRAYS = 0 # XXX bug - we fail with this FIXME
|
|
|
|
global SAFE_HEAP; SAFE_HEAP = 0 # Has variable object
|
|
|
|
#global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1
|
|
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0
|
|
|
|
#global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 1
|
|
#global CHECK_SIGNS; CHECK_SIGNS = 1
|
|
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 1
|
|
global CORRECT_SIGNS_LINES
|
|
CORRECT_SIGNS_LINES = ['parseargs.cc:171', 'BuiltinFont.cc:64', 'NameToCharCode.cc:115', 'GooHash.cc:368',
|
|
'Stream.h:469', 'PDFDoc.cc:1064', 'Lexer.cc:201', 'Splash.cc:1130', 'XRef.cc:997',
|
|
'vector:714', 'Lexer.cc:259', 'Splash.cc:438', 'Splash.cc:532', 'GfxFont.cc:1152',
|
|
'Gfx.cc:3838', 'Splash.cc:3162', 'Splash.cc:3163', 'Splash.cc:3164', 'Splash.cc:3153',
|
|
'Splash.cc:3159', 'SplashBitmap.cc:80', 'SplashBitmap.cc:81', 'SplashBitmap.cc:82',
|
|
'Splash.cc:809', 'Splash.cc:805', 'GooHash.cc:379',
|
|
# FreeType
|
|
't1load.c:1850', 'psconv.c:104', 'psconv.c:185', 'psconv.c:366', 'psconv.c:399',
|
|
'ftcalc.c:308', 't1parse.c:405', 'psconv.c:431', 'ftcalc.c:555', 't1objs.c:458',
|
|
't1decode.c:595', 't1decode.c:606', 'pstables.h:4048', 'pstables.h:4055', 'pstables.h:4066',
|
|
'pshglob.c:166', 'ftobjs.c:2548', 'ftgrays.c:1190', 'psmodule.c:116', 'psmodule.c:119',
|
|
'psobjs.c:195', 'pshglob.c:165', 'ttload.c:694', 'ttmtx.c:195', 'sfobjs.c:957',
|
|
'sfobjs.c:958', 'ftstream.c:369', 'ftstream.c:372', 'ttobjs.c:1007'] # And many more...
|
|
|
|
global COMPILER_TEST_OPTS; COMPILER_TEST_OPTS += [
|
|
'-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'),
|
|
]
|
|
|
|
global INVOKE_RUN; 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()
|
|
|
|
def post(filename):
|
|
# To avoid loading this large file to memory and altering it, we simply append to the end
|
|
src = open(filename, 'a')
|
|
src.write(
|
|
'''
|
|
FS.createDataFile('/', 'paper.pdf', eval(read('paper.pdf.js')), true, false);
|
|
FS.root.write = true;
|
|
run();
|
|
print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents));
|
|
'''
|
|
)
|
|
src.close()
|
|
|
|
#fontconfig = self.get_library('fontconfig', [os.path.join('src', '.libs', 'libfontconfig.a')]) # Used in file, but not needed, mostly
|
|
|
|
freetype = self.get_freetype()
|
|
|
|
poppler = self.get_library('poppler',
|
|
[os.path.join('poppler', '.libs', 'libpoppler.so.13.0.0.bc'),
|
|
os.path.join('goo', '.libs', 'libgoo.a.bc'),
|
|
os.path.join('fofi', '.libs', 'libfofi.a.bc'),
|
|
os.path.join('splash', '.libs', 'libsplash.a.bc'),
|
|
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'])
|
|
|
|
# Combine libraries
|
|
|
|
combined = os.path.join(self.get_building_dir(), 'combined.bc')
|
|
self.do_link([freetype, poppler], combined)
|
|
|
|
self.do_ll_run(combined,
|
|
lambda: map(ord, open(path_from_root('tests', 'poppler', 'ref.ppm'), 'r').read()).__str__().replace(' ', ''),
|
|
args='-scale-to 512 paper.pdf filename'.split(' '),
|
|
post_build=post)
|
|
#, build_ll_hook=self.do_autodebug)
|
|
|
|
def test_openjpeg(self):
|
|
global USE_TYPED_ARRAYS
|
|
global CORRECT_SIGNS
|
|
if USE_TYPED_ARRAYS == 2:
|
|
CORRECT_SIGNS = 1
|
|
else:
|
|
CORRECT_SIGNS = 2
|
|
global CORRECT_SIGNS_LINES
|
|
CORRECT_SIGNS_LINES = ["mqc.c:566", "mqc.c:317"]
|
|
|
|
original_j2k = path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.j2k')
|
|
|
|
def post(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{PRE_RUN_ADDITIONS}}',
|
|
'''FS.createDataFile('/', 'image.j2k', %s, true, false);FS.root.write = true;''' % line_splitter(str(
|
|
map(ord, open(original_j2k, 'rb').read())
|
|
))
|
|
).replace(
|
|
'// {{POST_RUN_ADDITIONS}}',
|
|
'''print("Data: " + JSON.stringify(FS.root.contents['image.raw'].contents));'''
|
|
)
|
|
open(filename, 'w').write(src)
|
|
|
|
lib = self.get_library('openjpeg',
|
|
[os.path.join('bin', 'libopenjpeg.so.1.4.0.bc'),
|
|
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
|
|
cache=False) # We need opj_config.h and other generated files, so cannot cache just the .bc
|
|
|
|
# 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
|
|
m = re.search('\[[\d, -]*\]', output)
|
|
try:
|
|
js_data = eval(m.group(0))
|
|
except AttributeError:
|
|
print 'Failed to find proper image output in: ' + output
|
|
raise
|
|
|
|
js_data = map(lambda x: x if x >= 0 else 256+x, js_data) # Our output may be signed, so unsign it
|
|
|
|
# Get the correct output
|
|
true_data = open(path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.raw'), 'rb').read()
|
|
|
|
# Compare them
|
|
assert(len(js_data) == len(true_data))
|
|
num = len(js_data)
|
|
diff_total = js_total = true_total = 0
|
|
for i in range(num):
|
|
js_total += js_data[i]
|
|
true_total += ord(true_data[i])
|
|
diff_total += abs(js_data[i] - ord(true_data[i]))
|
|
js_mean = js_total/float(num)
|
|
true_mean = true_total/float(num)
|
|
diff_mean = diff_total/float(num)
|
|
|
|
image_mean = 83.265
|
|
#print '[image stats:', js_mean, image_mean, true_mean, diff_mean, num, ']'
|
|
assert abs(js_mean - image_mean) < 0.01
|
|
assert abs(true_mean - image_mean) < 0.01
|
|
assert diff_mean < 0.01
|
|
|
|
return output
|
|
|
|
self.do_run(open(path_from_root('tests', 'openjpeg', 'codec', 'j2k_to_image.c'), 'r').read(),
|
|
'Successfully generated', # The real test for valid output is in image_compare
|
|
'-i image.j2k -o image.raw'.split(' '),
|
|
libraries=[lib],
|
|
includes=[path_from_root('tests', 'openjpeg', 'libopenjpeg'),
|
|
path_from_root('tests', 'openjpeg', 'codec'),
|
|
path_from_root('tests', 'openjpeg', 'common'),
|
|
os.path.join(self.get_building_dir(), 'openjpeg')],
|
|
force_c=True,
|
|
post_build=post,
|
|
output_nicerizer=image_compare)# build_ll_hook=self.do_autodebug)
|
|
|
|
def test_python(self):
|
|
global QUANTUM_SIZE, USE_TYPED_ARRAYS
|
|
if QUANTUM_SIZE == 1 or USE_TYPED_ARRAYS == 2: return self.skip('TODO: make this work')
|
|
|
|
# Overflows in string_hash
|
|
global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1
|
|
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0
|
|
global RELOOP; RELOOP = 0 # Too slow; we do care about typed arrays and OPTIMIZE though
|
|
global SAFE_HEAP; SAFE_HEAP = 0 # Has bitfields which are false positives. Also the PyFloat_Init tries to detect endianness.
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 1 # Not sure why, but needed
|
|
global EXPORTED_FUNCTIONS; EXPORTED_FUNCTIONS = ['_main', '_PyRun_SimpleStringFlags'] # for the demo
|
|
|
|
self.do_ll_run(path_from_root('tests', 'python', 'python.ll'),
|
|
'hello python world!\n[0, 2, 4, 6]\n5\n22\n5.470000',
|
|
args=['-S', '-c' '''print "hello python world!"; print [x*2 for x in range(4)]; t=2; print 10-3-t; print (lambda x: x*2)(11); print '%f' % 5.47'''],
|
|
extra_emscripten_args=['-m'])
|
|
|
|
# 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):
|
|
global QUANTUM_SIZE
|
|
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0
|
|
if 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', '')
|
|
print "Testing case '%s'..." % shortname
|
|
output_file = path_from_root('tests', 'cases', shortname + '.txt')
|
|
if 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)
|
|
|
|
# Autodebug the code
|
|
def do_autodebug(self, filename):
|
|
output = Popen(['python', AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
assert 'Success.' in output, output
|
|
self.prep_ll_run(filename, filename+'.o.ll.ll', force_recompile=True) # rebuild .bc
|
|
|
|
def test_autodebug(self):
|
|
if LLVM_OPTS: return self.skip('LLVM opts mess us up')
|
|
|
|
# Run a test that should work, generating some code
|
|
self.test_structs()
|
|
|
|
filename = os.path.join(self.get_dir(), 'src.cpp')
|
|
self.do_autodebug(filename)
|
|
|
|
# Compare to each other, and to expected output
|
|
self.do_ll_run(path_from_root('tests', filename+'.o.ll.ll'))
|
|
|
|
# Test using build_ll_hook
|
|
src = '''
|
|
#include <stdio.h>
|
|
|
|
char cache[256], *next = cache;
|
|
|
|
int main()
|
|
{
|
|
cache[10] = 25;
|
|
next[20] = 51;
|
|
int x = cache[10];
|
|
double y = 11.52;
|
|
printf("*%d,%d,%.2f*\\n", x, cache[20], y);
|
|
return 0;
|
|
}
|
|
'''
|
|
self.do_run(src, build_ll_hook=self.do_autodebug)
|
|
self.do_run(src, 'AD:', build_ll_hook=self.do_autodebug)
|
|
|
|
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)
|
|
|
|
def test_profiling(self):
|
|
global PROFILE; PROFILE = 1
|
|
global INVOKE_RUN; INVOKE_RUN = 0
|
|
|
|
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;
|
|
}
|
|
'''
|
|
|
|
def post(filename):
|
|
src = open(filename, 'a')
|
|
src.write('''
|
|
startProfiling();
|
|
run();
|
|
stopProfiling();
|
|
printProfiling();
|
|
print('*ok*');
|
|
''')
|
|
src.close()
|
|
|
|
# Using build_ll_hook forces a recompile, which leads to DFE being done even without opts
|
|
self.do_run(src, ': __Z6inner1i (5000)\n*ok*', post_build=post)
|
|
|
|
### Integration tests
|
|
|
|
def test_scriptaclass(self):
|
|
header_filename = os.path.join(self.get_dir(), 'header.h')
|
|
header = '''
|
|
struct ScriptMe {
|
|
int value;
|
|
ScriptMe(int val);
|
|
int getVal(); // XXX Sadly, inlining these will result in LLVM not
|
|
// producing any code for them (when just building
|
|
// as a library)
|
|
void mulVal(int mul);
|
|
};
|
|
'''
|
|
h = open(header_filename, 'w')
|
|
h.write(header)
|
|
h.close()
|
|
|
|
src = '''
|
|
#include "header.h"
|
|
|
|
ScriptMe::ScriptMe(int val) : value(val) { }
|
|
int ScriptMe::getVal() { return value; }
|
|
void ScriptMe::mulVal(int mul) { value *= mul; }
|
|
'''
|
|
|
|
# Way 1: use demangler and namespacer
|
|
|
|
script_src = '''
|
|
var sme = Module._.ScriptMe.__new__(83); // malloc(sizeof(ScriptMe)), ScriptMe::ScriptMe(sme, 83) / new ScriptMe(83) (at addr sme)
|
|
Module._.ScriptMe.mulVal(sme, 2); // ScriptMe::mulVal(sme, 2) sme.mulVal(2)
|
|
print('*' + Module._.ScriptMe.getVal(sme) + '*');
|
|
_free(sme);
|
|
print('*ok*');
|
|
'''
|
|
def post(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)
|
|
|
|
# Way 2: use CppHeaderParser
|
|
|
|
global RUNTIME_TYPE_INFO; RUNTIME_TYPE_INFO = 1
|
|
|
|
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; }
|
|
};
|
|
|
|
class Child2 : public Parent {
|
|
public:
|
|
Child2() : Parent(9) { printf("Child2:%d\\n", value); };
|
|
int getValCube() { return value*value*value; }
|
|
static void printStatic() { printf("*static*\\n"); }
|
|
|
|
virtual void virtualFunc() { printf("*virtualf*\\n"); }
|
|
virtual void virtualFunc2() { printf("*virtualf2*\\n"); }
|
|
static void runVirtualFunc(Child2 *self) { self->virtualFunc(); };
|
|
private:
|
|
void doSomethingSecret() { printf("security breached!\\n"); }; // we should not be able to do this
|
|
};
|
|
'''
|
|
open(header_filename, 'w').write(header)
|
|
|
|
basename = os.path.join(self.get_dir(), 'bindingtest')
|
|
output = Popen([BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
#print output
|
|
assert 'Traceback' not in output, 'Failure in binding generation: ' + output
|
|
|
|
src = '''
|
|
#include "header.h"
|
|
|
|
Parent::Parent(int val) : value(val) { printf("Parent:%d\\n", val); }
|
|
void Parent::mulVal(int mul) { value *= mul; }
|
|
|
|
#include "bindingtest.cpp"
|
|
'''
|
|
|
|
script_src_2 = '''
|
|
var sme = new Parent(42);
|
|
sme.mulVal(2);
|
|
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));
|
|
|
|
print('c1 v2');
|
|
|
|
c1 = new Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2
|
|
print(c1.getVal());
|
|
c1.mulVal(2);
|
|
print(c1.getVal());
|
|
print(c1.getValSqr());
|
|
print(c1.getValSqr(3));
|
|
|
|
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);
|
|
|
|
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*');
|
|
'''
|
|
|
|
def post2(filename):
|
|
src = open(filename, 'r').read().replace(
|
|
'// {{MODULE_ADDITIONS}',
|
|
'''load('bindingtest.js')''' + '\n\n' + script_src_2 + '\n\n' +
|
|
'// {{MODULE_ADDITIONS}'
|
|
)
|
|
open(filename, 'w').write(src)
|
|
self.do_run(src, '''*
|
|
84
|
|
c1
|
|
Parent:7
|
|
Child1:7
|
|
7
|
|
14
|
|
196
|
|
588
|
|
14
|
|
28
|
|
c1 v2
|
|
Parent:16
|
|
Child1:15
|
|
15
|
|
30
|
|
900
|
|
2700
|
|
c2
|
|
Parent:9
|
|
Child2:9
|
|
9
|
|
18
|
|
5832
|
|
0
|
|
0
|
|
1
|
|
*static*
|
|
*virtualf*
|
|
*virtualf*
|
|
*virtualf2*
|
|
Parent:9
|
|
Child2:9
|
|
*js virtualf replacement*
|
|
*js virtualf replacement*
|
|
*js virtualf2 replacement*
|
|
*virtualf*
|
|
*virtualf*
|
|
*virtualf2*
|
|
*ok*
|
|
''', post_build=post2)
|
|
|
|
def test_typeinfo(self):
|
|
global RUNTIME_TYPE_INFO; RUNTIME_TYPE_INFO = 1
|
|
global QUANTUM_SIZE
|
|
if 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;
|
|
}
|
|
'''
|
|
|
|
def post(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
|
|
RUNTIME_TYPE_INFO = 0
|
|
self.do_run(src, 'No type info.', post_build=post)
|
|
|
|
### Tests for tools
|
|
|
|
def test_closure_compiler(self):
|
|
src = '''
|
|
#include<stdio.h>
|
|
int main() {
|
|
printf("*closured*\\n");
|
|
return 0;
|
|
}
|
|
'''
|
|
|
|
def add_cc(filename):
|
|
Popen(['java', '-jar', CLOSURE_COMPILER,
|
|
'--compilation_level', 'ADVANCED_OPTIMIZATIONS',
|
|
'--formatting', 'PRETTY_PRINT',
|
|
'--variable_map_output_file', filename + '.vars',
|
|
'--js', filename, '--js_output_file', filename + '.cc.js'], stdout=PIPE, stderr=STDOUT).communicate()
|
|
assert not re.search('function \w\(', open(filename, 'r').read()) # closure generates this kind of stuff - functions with single letters. Normal doesn't.
|
|
src = open(filename + '.cc.js', 'r').read()
|
|
assert re.search('function \w\(', src) # see before
|
|
assert 'function _main()' not in src # closure should have wiped it out
|
|
open(filename, 'w').write(src)
|
|
self.do_run(src, '*closured*', post_build=add_cc)
|
|
|
|
def test_safe_heap(self):
|
|
global SAFE_HEAP, SAFE_HEAP_LINES
|
|
|
|
if not SAFE_HEAP: return self.skip('We need SAFE_HEAP to test SAFE_HEAP')
|
|
if LLVM_OPTS: return self.skip('LLVM can optimize away the intermediate |x|')
|
|
src = '''
|
|
#include<stdio.h>
|
|
int main() {
|
|
int *x = new int;
|
|
*x = 20;
|
|
float *y = (float*)x;
|
|
printf("%f\\n", *y);
|
|
printf("*ok*\\n");
|
|
return 0;
|
|
}
|
|
'''
|
|
|
|
try:
|
|
self.do_run(src, '*nothingatall*')
|
|
except Exception, e:
|
|
# This test *should* fail, by throwing this exception
|
|
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
|
|
|
|
# And we should not fail if we disable checking on that line
|
|
|
|
SAFE_HEAP = 3
|
|
SAFE_HEAP_LINES = ["src.cpp:7"]
|
|
|
|
self.do_run(src, '*ok*')
|
|
|
|
# But if we disable the wrong lines, we still fail
|
|
|
|
SAFE_HEAP_LINES = ["src.cpp:99"]
|
|
|
|
try:
|
|
self.do_run(src, '*nothingatall*')
|
|
except Exception, e:
|
|
# This test *should* fail, by throwing this exception
|
|
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
|
|
|
|
# And reverse the checks with = 2
|
|
|
|
SAFE_HEAP = 2
|
|
SAFE_HEAP_LINES = ["src.cpp:99"]
|
|
|
|
self.do_run(src, '*ok*')
|
|
|
|
def test_check_overflow(self):
|
|
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 1
|
|
global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 0
|
|
|
|
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*')
|
|
except Exception, e:
|
|
# This test *should* fail, by throwing this exception
|
|
assert 'Too many corrections' in str(e), str(e)
|
|
assert 'CHECK_OVERFLOW' in str(e), str(e)
|
|
|
|
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:
|
|
def post(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)
|
|
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)
|
|
self.do_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)
|
|
|
|
def test_linespecific(self):
|
|
global CHECK_SIGNS; CHECK_SIGNS = 0
|
|
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0
|
|
global CORRECT_SIGNS, CORRECT_OVERFLOWS, CORRECT_ROUNDINGS, CORRECT_SIGNS_LINES, CORRECT_OVERFLOWS_LINES, CORRECT_ROUNDINGS_LINES
|
|
|
|
# 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!
|
|
}
|
|
'''
|
|
|
|
CORRECT_SIGNS = 0
|
|
self.do_run(src, '*1*') # This is a fail - we expect 0
|
|
|
|
CORRECT_SIGNS = 1
|
|
self.do_run(src, '*0*') # Now it will work properly
|
|
|
|
# And now let's fix just that one line
|
|
CORRECT_SIGNS = 2
|
|
CORRECT_SIGNS_LINES = ["src.cpp:9"]
|
|
self.do_run(src, '*0*')
|
|
|
|
# Fixing the wrong line should not work
|
|
CORRECT_SIGNS = 2
|
|
CORRECT_SIGNS_LINES = ["src.cpp:3"]
|
|
self.do_run(src, '*1*')
|
|
|
|
# And reverse the checks with = 2
|
|
CORRECT_SIGNS = 3
|
|
CORRECT_SIGNS_LINES = ["src.cpp:3"]
|
|
self.do_run(src, '*0*')
|
|
CORRECT_SIGNS = 3
|
|
CORRECT_SIGNS_LINES = ["src.cpp:9"]
|
|
self.do_run(src, '*1*')
|
|
|
|
# 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*'
|
|
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)
|
|
|
|
CORRECT_OVERFLOWS = 1
|
|
self.do_run(src, correct) # Now it will work properly
|
|
|
|
# And now let's fix just that one line
|
|
CORRECT_OVERFLOWS = 2
|
|
CORRECT_OVERFLOWS_LINES = ["src.cpp:6"]
|
|
self.do_run(src, correct)
|
|
|
|
# Fixing the wrong line should not work
|
|
CORRECT_OVERFLOWS = 2
|
|
CORRECT_OVERFLOWS_LINES = ["src.cpp:3"]
|
|
try:
|
|
self.do_run(src, correct)
|
|
raise Exception('UNEXPECTED-PASS')
|
|
except Exception, e:
|
|
assert 'UNEXPECTED' not in str(e), str(e)
|
|
assert 'Expected to find' in str(e), str(e)
|
|
|
|
# And reverse the checks with = 2
|
|
CORRECT_OVERFLOWS = 3
|
|
CORRECT_OVERFLOWS_LINES = ["src.cpp:3"]
|
|
self.do_run(src, correct)
|
|
CORRECT_OVERFLOWS = 3
|
|
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)
|
|
|
|
# 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");
|
|
}
|
|
'''
|
|
|
|
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
|
|
|
|
CORRECT_ROUNDINGS = 1
|
|
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
|
|
|
|
CORRECT_ROUNDINGS = 2
|
|
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
|
|
|
|
# And reverse the check with = 2
|
|
CORRECT_ROUNDINGS = 3
|
|
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*')
|
|
|
|
def test_autooptimize(self):
|
|
global CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CHECK_SIGNS, CORRECT_SIGNS, AUTO_OPTIMIZE
|
|
|
|
AUTO_OPTIMIZE = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = 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);
|
|
|
|
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 #
|
|
assert 'Overflow|src.cpp:6 : 60 hits, %20 failures' in output, 'no indication of Overflow corrections'
|
|
assert 'UnSign|src.cpp:13 : 6 hits, %17 failures' in output, 'no indication of Sign corrections'
|
|
return output
|
|
|
|
self.do_run(src, '*186854335,63*\n', output_nicerizer=check)
|
|
|
|
|
|
# Generate tests for all our compilers
|
|
def make_run(name, compiler, llvm_opts, embetter, quantum_size, typed_arrays):
|
|
exec('''
|
|
class %s(T):
|
|
def tearDown(self):
|
|
super(%s, self).tearDown()
|
|
|
|
def setUp(self):
|
|
super(%s, self).setUp()
|
|
|
|
global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, ASSERTIONS, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS, CORRECT_ROUNDINGS, CORRECT_ROUNDINGS_LINES, INVOKE_RUN, SAFE_HEAP_LINES, INIT_STACK, AUTO_OPTIMIZE, RUNTIME_TYPE_INFO, DISABLE_EXCEPTION_CATCHING, PROFILE, TOTAL_MEMORY, FAST_MEMORY
|
|
|
|
COMPILER = %r
|
|
llvm_opts = %d
|
|
embetter = %d
|
|
quantum_size = %d
|
|
USE_TYPED_ARRAYS = %d
|
|
INVOKE_RUN = 1
|
|
RELOOP = OPTIMIZE = embetter
|
|
QUANTUM_SIZE = quantum_size
|
|
ASSERTIONS = 1-embetter
|
|
SAFE_HEAP = 1-(embetter and llvm_opts)
|
|
LLVM_OPTS = llvm_opts
|
|
AUTO_OPTIMIZE = 0
|
|
CHECK_OVERFLOWS = 1-(embetter or llvm_opts)
|
|
CORRECT_OVERFLOWS = 1-(embetter and llvm_opts)
|
|
CORRECT_SIGNS = 0
|
|
CORRECT_ROUNDINGS = 0
|
|
CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = []
|
|
CHECK_SIGNS = 0 #1-(embetter or llvm_opts)
|
|
INIT_STACK = 0
|
|
RUNTIME_TYPE_INFO = 0
|
|
DISABLE_EXCEPTION_CATCHING = 0
|
|
PROFILE = 0
|
|
TOTAL_MEMORY = FAST_MEMORY = None
|
|
|
|
if QUANTUM_SIZE == 1 or USE_TYPED_ARRAYS == 2:
|
|
RELOOP = 0 # XXX Would be better to use this, but it isn't really what we test in these cases, and is very slow
|
|
|
|
if LLVM_OPTS:
|
|
self.pick_llvm_opts(3)
|
|
|
|
COMPILER_TEST_OPTS = ['-g']
|
|
|
|
os.chdir(self.get_dir()) # Ensure the directory exists and go there
|
|
|
|
TT = %s
|
|
''' % (fullname, fullname, fullname, compiler, llvm_opts, embetter, quantum_size, typed_arrays, fullname))
|
|
return TT
|
|
|
|
for llvm_opts in [0,1]:
|
|
for name, compiler, quantum, embetter, typed_arrays in [
|
|
('clang', CLANG, 1, 0, 0),
|
|
('clang', CLANG, 4, 0, 0),
|
|
('clang', CLANG, 1, 1, 1),
|
|
('clang', CLANG, 4, 1, 1),
|
|
('clang', CLANG, 4, 1, 2),
|
|
]:
|
|
fullname = '%s_%d_%d%s%s' % (
|
|
name, 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(%r,%r,%d,%d,%d,%d)' % (fullname, fullname, compiler, llvm_opts, embetter, quantum, typed_arrays))
|
|
|
|
del T # T is just a shape for the specific subclasses, we don't test it itself
|
|
|
|
class OtherTests(RunnerCore):
|
|
def test_eliminator(self):
|
|
coffee = path_from_root('tools', 'eliminator', 'node_modules', 'coffee-script', 'bin', 'coffee')
|
|
eliminator = path_from_root('tools', 'eliminator', 'eliminator.coffee')
|
|
input = open(path_from_root('tools', 'eliminator', 'eliminator-test.js')).read()
|
|
expected = open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read()
|
|
output = Popen([coffee, eliminator], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(input)[0]
|
|
self.assertEquals(output, expected)
|
|
|
|
else:
|
|
# Benchmarks. Run them with argument |benchmark|. To run a specific test, do
|
|
# |benchmark.test_X|.
|
|
|
|
print "Running Emscripten benchmarks..."
|
|
|
|
sys.argv = filter(lambda x: x != 'benchmark', sys.argv)
|
|
|
|
assert(os.path.exists(CLOSURE_COMPILER))
|
|
|
|
try:
|
|
index = SPIDERMONKEY_ENGINE.index("options('strict')")
|
|
SPIDERMONKEY_ENGINE = SPIDERMONKEY_ENGINE[:index-1] + SPIDERMONKEY_ENGINE[index+1:] # closure generates non-strict
|
|
except:
|
|
pass
|
|
|
|
COMPILER = CLANG
|
|
JS_ENGINE = SPIDERMONKEY_ENGINE
|
|
#JS_ENGINE = V8_ENGINE
|
|
|
|
global COMPILER_TEST_OPTS; COMPILER_TEST_OPTS = []
|
|
|
|
QUANTUM_SIZE = 1
|
|
RELOOP = OPTIMIZE = 1
|
|
USE_TYPED_ARRAYS = 1
|
|
ASSERTIONS = SAFE_HEAP = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = INIT_STACK = AUTO_OPTIMIZE = RUNTIME_TYPE_INFO = 0
|
|
INVOKE_RUN = 1
|
|
CORRECT_SIGNS = 0
|
|
CORRECT_ROUNDINGS = 0
|
|
CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = []
|
|
DISABLE_EXCEPTION_CATCHING = 1
|
|
if USE_TYPED_ARRAYS:
|
|
TOTAL_MEMORY = 100*1024*1024 # XXX Needed for dlmalloc. TODO: Test other values
|
|
FAST_MEMORY = 10*1024*1024
|
|
|
|
TEST_REPS = 4
|
|
TOTAL_TESTS = 6
|
|
|
|
tests_done = 0
|
|
total_times = map(lambda x: 0., range(TOTAL_TESTS))
|
|
total_native_times = map(lambda x: 0., range(TOTAL_TESTS))
|
|
|
|
class benchmark(RunnerCore):
|
|
def print_stats(self, times, native_times, normalize_by_native=False):
|
|
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)
|
|
|
|
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)
|
|
|
|
if not normalize_by_native:
|
|
final = mean / mean_native
|
|
else:
|
|
final = 0
|
|
for i in range(len(times)):
|
|
final += times[i]/native_times[i]
|
|
final /= len(times)
|
|
|
|
print
|
|
print ' JavaScript : mean: %.3f (+-%.3f) seconds (max: %.3f, min: %.3f, noise/signal: %.3f) (%d runs)' % (mean, std, max(times), min(times), std/mean, TEST_REPS)
|
|
print ' Native (gcc): mean: %.3f (+-%.3f) seconds (max: %.3f, min: %.3f, noise/signal: %.3f) JS is %.2f X slower' % (mean_native, std_native, max(native_times), min(native_times), std_native/mean_native, final)
|
|
|
|
def do_benchmark(self, src, args=[], expected_output='FAIL', main_file=None, llvm_opts=False, handpicked=False):
|
|
global USE_TYPED_ARRAYS, LLVM_OPTS
|
|
|
|
LLVM_OPTS = llvm_opts
|
|
if LLVM_OPTS:
|
|
self.pick_llvm_opts(3, handpicked)
|
|
|
|
dirname = self.get_dir()
|
|
filename = os.path.join(dirname, 'src.cpp')
|
|
self.build(src, dirname, filename, main_file=main_file)
|
|
|
|
final_filename = filename + '.o.js'
|
|
|
|
for post in POST_OPTIMIZATIONS:
|
|
if post == 'closure':
|
|
# Something like this (adjust memory as needed):
|
|
# java -Xmx1024m -jar CLOSURE_COMPILER --compilation_level ADVANCED_OPTIMIZATIONS --variable_map_output_file src.cpp.o.js.vars --js src.cpp.o.js --js_output_file src.cpp.o.cc.js
|
|
|
|
cc_output = Popen(['java', '-jar', CLOSURE_COMPILER,
|
|
'--compilation_level', 'ADVANCED_OPTIMIZATIONS',
|
|
'--formatting', 'PRETTY_PRINT',
|
|
'--variable_map_output_file', final_filename + '.vars',
|
|
'--js', final_filename, '--js_output_file', final_filename + '.cc.js'], stdout=PIPE, stderr=STDOUT).communicate()[0]
|
|
if 'ERROR' in cc_output:
|
|
raise Exception('Error in cc output: ' + cc_output)
|
|
final_filename += '.cc.js'
|
|
elif post == 'eliminator':
|
|
coffee = path_from_root('tools', 'eliminator', 'node_modules', 'coffee-script', 'bin', 'coffee')
|
|
eliminator = path_from_root('tools', 'eliminator', 'eliminator.coffee')
|
|
input = open(final_filename, 'r').read()
|
|
output = Popen([coffee, eliminator], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(input)[0]
|
|
final_filename += '.el.js'
|
|
f = open(final_filename, 'w')
|
|
f.write(output)
|
|
f.close()
|
|
else:
|
|
raise Exception('Unknown post-optimization: ' + post)
|
|
|
|
# Run JS
|
|
global total_times, tests_done
|
|
times = []
|
|
for i in range(TEST_REPS):
|
|
start = time.time()
|
|
js_output = self.run_generated_code(JS_ENGINE, final_filename, args, check_timeout=False)
|
|
curr = time.time()-start
|
|
times.append(curr)
|
|
total_times[tests_done] += curr
|
|
if i == 0:
|
|
# Sanity check on output
|
|
self.assertContained(expected_output, js_output)
|
|
|
|
# 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)
|
|
total_native_times[tests_done] += curr
|
|
|
|
self.print_stats(times, native_times)
|
|
|
|
tests_done += 1
|
|
if tests_done == TOTAL_TESTS:
|
|
print
|
|
print 'Total stats:'
|
|
self.print_stats(total_times, total_native_times, True)
|
|
|
|
def test_primes(self):
|
|
global POST_OPTIMIZATIONS; POST_OPTIMIZATIONS = ['eliminator', 'closure']
|
|
|
|
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;
|
|
}
|
|
'''
|
|
self.do_benchmark(src, [], 'lastprime: 1297001.', llvm_opts=True, handpicked=False)
|
|
|
|
def test_memops(self):
|
|
global POST_OPTIMIZATIONS; POST_OPTIMIZATIONS = ['eliminator', 'closure']
|
|
|
|
# memcpy would also be interesting, however native code uses SSE/NEON/etc. and is much, much faster than JS can be
|
|
src = '''
|
|
#include<stdio.h>
|
|
#include<string.h>
|
|
#include<stdlib.h>
|
|
int main() {
|
|
int N = 1024*1024;
|
|
int M = 190;
|
|
int final = 0;
|
|
char *buf = (char*)malloc(N);
|
|
for (int t = 0; t < M; t++) {
|
|
for (int i = 0; i < N; i++)
|
|
buf[i] = (i + final)%256;
|
|
for (int i = 0; i < N; i++)
|
|
final += buf[i] & 1;
|
|
final = final % 1000;
|
|
}
|
|
printf("final: %d.\\n", final);
|
|
return 1;
|
|
}
|
|
'''
|
|
self.do_benchmark(src, [], 'final: 720.', llvm_opts=True, handpicked=True)
|
|
|
|
def test_fannkuch(self):
|
|
global POST_OPTIMIZATIONS; POST_OPTIMIZATIONS = ['eliminator', 'closure']
|
|
|
|
src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read()
|
|
self.do_benchmark(src, ['10'], 'Pfannkuchen(10) = 38.', llvm_opts=True, handpicked=True)
|
|
|
|
def test_fasta(self):
|
|
global POST_OPTIMIZATIONS; POST_OPTIMIZATIONS = ['eliminator']
|
|
|
|
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''',
|
|
llvm_opts=True, handpicked=False)
|
|
|
|
def test_raytrace(self):
|
|
global POST_OPTIMIZATIONS; POST_OPTIMIZATIONS = ['closure']
|
|
|
|
global QUANTUM_SIZE, USE_TYPED_ARRAYS
|
|
old_quantum = QUANTUM_SIZE
|
|
old_use_typed_arrays = USE_TYPED_ARRAYS
|
|
QUANTUM_SIZE = 1
|
|
USE_TYPED_ARRAYS = 0 # Rounding errors with TA2 are too big in this very rounding-sensitive code. However, TA2 is much faster (2X)
|
|
|
|
src = open(path_from_root('tests', 'raytrace.cpp'), 'r').read().replace('double', 'float') # benchmark with floats
|
|
self.do_benchmark(src, ['7', '256'], '256 256', llvm_opts=True, handpicked=False)
|
|
|
|
QUANTUM_SIZE = old_quantum
|
|
USE_TYPED_ARRAYS = old_use_typed_arrays
|
|
|
|
def test_dlmalloc(self):
|
|
global POST_OPTIMIZATIONS; POST_OPTIMIZATIONS = ['eliminator']
|
|
|
|
global COMPILER_TEST_OPTS; COMPILER_TEST_OPTS = ['-g']
|
|
global CORRECT_SIGNS; CORRECT_SIGNS = 2
|
|
global CORRECT_SIGNS_LINES; CORRECT_SIGNS_LINES = ['src.cpp:' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]]
|
|
|
|
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_benchmark(src, ['400', '400'], '*400,0*', llvm_opts=True, handpicked=True)
|
|
|
|
if __name__ == '__main__':
|
|
sys.argv = [sys.argv[0]] + ['-v'] + sys.argv[1:] # Verbose output by default
|
|
for cmd in [CLANG, LLVM_DIS, SPIDERMONKEY_ENGINE[0], V8_ENGINE[0]]:
|
|
if not os.path.exists(cmd) and not os.path.exists(cmd + '.exe'): # .exe extension required for Windows
|
|
print 'WARNING: Cannot find', cmd
|
|
unittest.main()
|
|
|