diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/embind/shell.html | 6 | ||||
-rwxr-xr-x | tests/fuzz/creduce_tester.py | 54 | ||||
-rwxr-xr-x | tests/fuzz/csmith_driver.py | 30 | ||||
-rw-r--r-- | tests/hello_world_gles_shell.html | 3 | ||||
-rwxr-xr-x | tests/runner.py | 544 |
5 files changed, 367 insertions, 270 deletions
diff --git a/tests/embind/shell.html b/tests/embind/shell.html index 6664ec78..c3655e03 100644 --- a/tests/embind/shell.html +++ b/tests/embind/shell.html @@ -85,10 +85,6 @@ }; Module.setStatus('Downloading...'); </script> - <script type='text/javascript'> - - {{{ SCRIPT_CODE }}} - - </script> + <script type='text/javascript'>{{{ SCRIPT_CODE }}}</script> </body> </html> diff --git a/tests/fuzz/creduce_tester.py b/tests/fuzz/creduce_tester.py index c3460e9d..d5618c2e 100755 --- a/tests/fuzz/creduce_tester.py +++ b/tests/fuzz/creduce_tester.py @@ -1,53 +1,53 @@ #!/usr/bin/python ''' -Runs csmith, a C fuzzer, and looks for bugs +Usage: creduce ./creduce_tester.py newfail1.c ''' -import os, sys, difflib +import os, sys from subprocess import Popen, PIPE, STDOUT sys.path += [os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'tools')] -import shared +import shared, jsrun + +# creduce will only pass the filename of the C file as the first arg, so other +# configuration options will have to be hardcoded. +CSMITH_CFLAGS = ['-I', os.path.join(os.environ['CSMITH_PATH'], 'runtime')] +ENGINE = shared.JS_ENGINES[0] +EMCC_ARGS = ['-O2', '-s', 'ASM_JS=1', '-s', 'PRECISE_I64_MATH=1', '-s', + 'PRECISE_I32_MUL=1'] filename = sys.argv[1] +obj_filename = os.path.splitext(filename)[0] +js_filename = obj_filename + '.js' print 'testing file', filename -print '2) Compile natively' -shared.try_delete(filename) -shared.execute([shared.CLANG_CC, '-O2', filename + '.c', '-o', filename] + CSMITH_CFLAGS, stderr=PIPE) -assert os.path.exists(filename) -print '3) Run natively' try: - correct = shared.timeout_run(Popen([filename], stdout=PIPE, stderr=PIPE), 3) + print '2) Compile natively' + shared.check_execute([shared.CLANG_CC, '-O2', filename, '-o', obj_filename] + CSMITH_CFLAGS) + print '3) Run natively' + correct = jsrun.timeout_run(Popen([obj_filename], stdout=PIPE, stderr=PIPE), 3) except Exception, e: print 'Failed or infinite looping in native, skipping', e - notes['invalid'] += 1 - os.exit(0) # boring + sys.exit(1) # boring print '4) Compile JS-ly and compare' def try_js(args): - shared.try_delete(filename + '.js') - shared.execute([shared.EMCC, '-O2', '-s', 'ASM_JS=1', '-s', 'PRECISE_I64_MATH=1', '-s', 'PRECISE_I32_MUL=1', filename + '.c', '-o', filename + '.js'] + CSMITH_CFLAGS + args, stderr=PIPE) - assert os.path.exists(filename + '.js') - js = shared.run_js(filename + '.js', stderr=PIPE, engine=engine1) - assert correct == js, ''.join([a.rstrip()+'\n' for a in difflib.unified_diff(correct.split('\n'), js.split('\n'), fromfile='expected', tofile='actual')]) + shared.check_execute([shared.EMCC] + EMCC_ARGS + CSMITH_CFLAGS + args + + [filename, '-o', js_filename]) + js = shared.run_js(js_filename, stderr=PIPE, engine=ENGINE) + assert correct == js # Try normally, then try unaligned because csmith does generate nonportable code that requires x86 alignment -ok = False -normal = True -for args, note in [([], None), (['-s', 'UNALIGNED_MEMORY=1'], 'unaligned')]: +# If you are sure that alignment is not the cause, disable it for a faster reduction +for args in [[]]: try: try_js(args) - ok = True - if note: - notes[note] += 1 break except Exception, e: - print e - normal = False -if not ok: sys.exit(1) - -sys.exit(0) # boring + pass +else: + sys.exit(0) +sys.exit(1) # boring diff --git a/tests/fuzz/csmith_driver.py b/tests/fuzz/csmith_driver.py index b60e67f7..c987a3be 100755 --- a/tests/fuzz/csmith_driver.py +++ b/tests/fuzz/csmith_driver.py @@ -1,11 +1,14 @@ #!/usr/bin/python ''' -Runs csmith, a C fuzzer, and looks for bugs +Runs csmith, a C fuzzer, and looks for bugs. + +CSMITH_PATH should be set to something like /usr/local/include/csmith ''' import os, sys, difflib, shutil -from subprocess import Popen, PIPE, STDOUT +from distutils.spawn import find_executable +from subprocess import check_call, Popen, PIPE, STDOUT, CalledProcessError sys.path += [os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'tools')] import shared @@ -15,8 +18,11 @@ engine2 = eval('shared.' + sys.argv[2]) if len(sys.argv) > 2 else None print 'testing js engines', engine1, engine2 -CSMITH = os.path.expanduser('~/Dev/csmith/src/csmith') -CSMITH_CFLAGS = ['-I' + os.path.expanduser('~/Dev/csmith/runtime/')] +CSMITH = os.environ.get('CSMITH') or find_executable('csmith') +assert CSMITH, 'Could not find CSmith on your PATH. Please set the environment variable CSMITH.' +CSMITH_PATH = os.environ.get('CSMITH_PATH') +assert CSMITH_PATH, 'Please set the environment variable CSMITH_PATH.' +CSMITH_CFLAGS = ['-I', os.path.join(CSMITH_PATH, 'runtime')] filename = os.path.join(shared.CANONICAL_TEMP_DIR, 'fuzzcode') @@ -31,7 +37,7 @@ fails = 0 while 1: print 'Tried %d, notes: %s' % (tried, notes) print '1) Generate C' - shared.execute([CSMITH, '--no-volatiles', '--no-math64', '--no-packed-struct'],# + + check_call([CSMITH, '--no-volatiles', '--no-math64', '--no-packed-struct'],# + #['--max-block-depth', '2', '--max-block-size', '2', '--max-expr-complexity', '2', '--max-funcs', '2'], stdout=open(filename + '.c', 'w')) #shutil.copyfile(filename + '.c', 'testcase%d.c' % tried) @@ -41,11 +47,11 @@ while 1: print '2) Compile natively' shared.try_delete(filename) - shared.execute([shared.CLANG_CC, '-O2', filename + '.c', '-o', filename + '1'] + CSMITH_CFLAGS, stderr=PIPE) # + shared.EMSDK_OPTS - shared.execute([shared.CLANG_CC, '-O2', '-emit-llvm', '-c', '-Xclang', '-triple=i386-pc-linux-gnu', filename + '.c', '-o', filename + '.bc'] + CSMITH_CFLAGS + shared.EMSDK_OPTS, stderr=PIPE) - shared.execute([shared.path_from_root('tools', 'nativize_llvm.py'), filename + '.bc'], stdout=PIPE, stderr=PIPE) + shared.check_execute([shared.CLANG_CC, '-O2', filename + '.c', '-o', filename + '1'] + CSMITH_CFLAGS) # + shared.EMSDK_OPTS + shared.check_execute([shared.CLANG_CC, '-O2', '-emit-llvm', '-c', '-Xclang', '-triple=i386-pc-linux-gnu', filename + '.c', '-o', filename + '.bc'] + CSMITH_CFLAGS + shared.EMSDK_OPTS) + shared.check_execute([shared.path_from_root('tools', 'nativize_llvm.py'), filename + '.bc']) shutil.move(filename + '.bc.run', filename + '2') - shared.execute([shared.CLANG_CC, filename + '.c', '-o', filename + '3'] + CSMITH_CFLAGS, stderr=PIPE) + shared.check_execute([shared.CLANG_CC, filename + '.c', '-o', filename + '3'] + CSMITH_CFLAGS) print '3) Run natively' try: correct1 = shared.jsrun.timeout_run(Popen([filename + '1'], stdout=PIPE, stderr=PIPE), 3) @@ -65,7 +71,7 @@ while 1: def try_js(args): shared.try_delete(filename + '.js') print '(compile)' - shared.execute([shared.EMCC, '-O2', '-s', 'ASM_JS=1', filename + '.c', '-o', filename + '.js'] + CSMITH_CFLAGS + args, stderr=PIPE) + shared.check_execute([shared.EMCC, '-O2', '-s', 'ASM_JS=1', filename + '.c', '-o', filename + '.js'] + CSMITH_CFLAGS + args) assert os.path.exists(filename + '.js') print '(run)' js = shared.run_js(filename + '.js', stderr=PIPE, engine=engine1, check_timeout=True) @@ -91,7 +97,7 @@ while 1: print "EMSCRIPTEN BUG" notes['embug'] += 1 fails += 1 - shutil.copyfile('fuzzcode.c', 'newfail%d.c' % fails) + shutil.copyfile(filename + '.c', 'newfail%d.c' % fails) continue #if not ok: # try: # finally, try with safe heap. if that is triggered, this is nonportable code almost certainly @@ -118,7 +124,7 @@ while 1: print "ODIN VALIDATION BUG" notes['embug'] += 1 fails += 1 - shutil.copyfile('fuzzcode.c', 'newfail%d.c' % fails) + shutil.copyfile(filename + '.c', 'newfail%d.c' % fails) continue js2 = js2.replace('\nwarning: Successfully compiled asm.js code\n', '') diff --git a/tests/hello_world_gles_shell.html b/tests/hello_world_gles_shell.html index 4abee90c..2459d755 100644 --- a/tests/hello_world_gles_shell.html +++ b/tests/hello_world_gles_shell.html @@ -48,9 +48,8 @@ } Module.postRun = doTest; - // The compiled code - {{{ SCRIPT_CODE }}} </script> + <script>{{{ SCRIPT_CODE }}}</script> </body> </html> diff --git a/tests/runner.py b/tests/runner.py index 2c459f6f..2ce72240 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -170,16 +170,13 @@ class RunnerCore(unittest.TestCase): post1 = post_build post2 = None - def run_post(post): - if not post: return - exec post in locals() - shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js') - process(filename + '.o.js') - if self.emcc_args is None: Building.emscripten(filename, append_ext=True, extra_args=extra_emscripten_args) - run_post(post1) - run_post(post2) + if post1: + exec post1 in locals() + shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js') + process(filename + '.o.js') + if post2: post2(filename + '.o.js') else: transform_args = [] if post1: @@ -196,7 +193,7 @@ process(sys.argv[1]) transform.close() transform_args = ['--js-transform', "%s %s" % (PYTHON, transform_filename)] Building.emcc(filename + '.o.ll', Settings.serialize() + self.emcc_args + transform_args + Building.COMPILER_TEST_OPTS, filename + '.o.js') - run_post(post2) + if post2: post2(filename + '.o.js') # Build JavaScript code from source code def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[], post_build=None): @@ -233,11 +230,11 @@ process(sys.argv[1]) os.remove(f + '.o') except: pass - args = [PYTHON, EMCC] + Building.COMPILER_TEST_OPTS + \ + args = [PYTHON, EMCC] + Building.COMPILER_TEST_OPTS + Settings.serialize() + \ ['-I', dirname, '-I', os.path.join(dirname, 'include')] + \ map(lambda include: '-I' + include, includes) + \ ['-c', f, '-o', f + '.o'] - output = Popen(args, stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] + output = Popen(args, stdout=PIPE, stderr=self.stderr_redirect if not DEBUG else None).communicate()[0] assert os.path.exists(f + '.o'), 'Source compilation error: ' + output # Link all files @@ -438,7 +435,7 @@ process(sys.argv[1]) sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv) -test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1', 's_1_0', 's_1_1'] +test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1'] test_index = 0 @@ -2107,12 +2104,12 @@ Succeeded! } return int(&x); // both for the number, and forces x to not be nativized } - int main() + int main(int argc, char **argv) { // We should get the same value for the first and last - stack has unwound - int x1 = test(0); + int x1 = test(argc - 2); int x2 = test(100); - int x3 = test(0); + int x3 = test((argc - 2) / 4); printf("*%d,%d*\\n", x3-x1, x2 != x1); return 0; } @@ -4082,7 +4079,7 @@ def process(filename): #include <assert.h> #include "emscripten.h" - int main() + int main(int argc, char **argv) { char *buf1 = (char*)malloc(100); char *data1 = "hello"; @@ -4096,6 +4093,8 @@ def process(filename): int totalMemory = emscripten_run_script_int("TOTAL_MEMORY"); char *buf3 = (char*)malloc(totalMemory+1); + buf3[argc] = (int)buf2; + if (argc % 7 == 6) printf("%d\n", memcpy(buf3, buf1, argc)); char *buf4 = (char*)malloc(100); float *buf5 = (float*)malloc(100); //printf("totalMemory: %d bufs: %d,%d,%d,%d,%d\n", totalMemory, buf1, buf2, buf3, buf4, buf5); @@ -4233,6 +4232,8 @@ def process(filename): ''', args=['34962', '26214', '35040']) def test_indirectbr(self): + Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) + src = ''' #include <stdio.h> int main(void) { @@ -7553,11 +7554,11 @@ def process(filename): return 0; } ''' - self.do_run(src, '''www.cheezburger.com : 1 : 4 + self.do_run(src, '''www.cheezburger.com : 2 : 4 * -84.29.1.0. -fail.on.this.never.work : 1 : 4 +fail.on.this.never.work : 2 : 4 * -84.29.2.0. -localhost : 1 : 4 +localhost : 2 : 4 * -84.29.3.0. ''') @@ -8220,7 +8221,7 @@ void*:16 if self.run_name == 'o2': self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage - Building.COMPILER_TEST_OPTS = [] # remove -g, so we have one test without it by default + Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code... # Overflows happen in hash loop @@ -8252,7 +8253,7 @@ void*:16 def test_gcc_unmangler(self): Settings.NAMED_GLOBALS = 1 # test coverage for this - Building.COMPILER_TEST_OPTS = ['-I' + path_from_root('third_party')] + Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('third_party')] self.do_run(open(path_from_root('third_party', 'gcc_demangler.c')).read(), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj']) @@ -8367,6 +8368,8 @@ def process(filename): force_c=True) def test_zlib(self): + if not Settings.USE_TYPED_ARRAYS == 2: return self.skip('works in general, but cached build will be optimized and fail, so disable this') + if Settings.ASM_JS: self.banned_js_engines = [NODE_JS] # TODO investigate @@ -8475,6 +8478,8 @@ def process(filename): def test_openjpeg(self): if self.emcc_args is None: return self.skip('needs libc for getopt') + Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default + if Settings.USE_TYPED_ARRAYS == 2: Settings.CORRECT_SIGNS = 1 else: @@ -8596,6 +8601,7 @@ def process(filename): def test_python(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') + if not self.is_le32(): return self.skip('fails on non-le32') # FIXME #Settings.EXPORTED_FUNCTIONS += ['_PyRun_SimpleStringFlags'] # for the demo @@ -8695,6 +8701,7 @@ def process(filename): def test_autodebug(self): if Building.LLVM_OPTS: return self.skip('LLVM opts mess us up') + Building.COMPILER_TEST_OPTS += ['--llvm-opts', '0'] # Run a test that should work, generating some code self.test_structs() @@ -8973,13 +8980,11 @@ def process(filename): self.do_run(src, output) shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('normal.js')) - self.emcc_args.append('-s') - self.emcc_args.append('ASM_JS=0') + Settings.ASM_JS = 0 Settings.PGO = 1 self.do_run(src, output) + Settings.ASM_JS = 1 Settings.PGO = 0 - self.emcc_args.append('-s') - self.emcc_args.append('ASM_JS=1') shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgo.js')) pgo_output = run_js(self.in_dir('pgo.js')).split('\n')[1] @@ -9215,97 +9220,92 @@ def process(filename): src.close() ''' - post3 = ''' -def process(filename): - script_src_2 = \'\'\' - var sme = new Module.Parent(42); - sme.mulVal(2); - Module.print('*') - Module.print(sme.getVal()); - - Module.print('c1'); - - var c1 = new Module.Child1(); - Module.print(c1.getVal()); - c1.mulVal(2); - Module.print(c1.getVal()); - Module.print(c1.getValSqr()); - Module.print(c1.getValSqr(3)); - Module.print(c1.getValTimes()); // default argument should be 1 - Module.print(c1.getValTimes(2)); - - Module.print('c1 v2'); - - c1 = new Module.Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2 - Module.print(c1.getVal()); - c1.mulVal(2); - Module.print(c1.getVal()); - Module.print(c1.getValSqr()); - Module.print(c1.getValSqr(3)); - - Module.print('c2') - - var c2 = new Module.Child2(); - Module.print(c2.getVal()); - c2.mulVal(2); - Module.print(c2.getVal()); - Module.print(c2.getValCube()); - var succeeded; - try { - succeeded = 0; - Module.print(c2.doSomethingSecret()); // should fail since private - succeeded = 1; - } catch(e) {} - Module.print(succeeded); - try { - succeeded = 0; - Module.print(c2.getValSqr()); // function from the other class - succeeded = 1; - } catch(e) {} - Module.print(succeeded); - try { - succeeded = 0; - c2.getValCube(); // sanity - succeeded = 1; - } catch(e) {} - Module.print(succeeded); - - Module.Child2.prototype.printStatic(); // static calls go through the prototype - - // virtual function - c2.virtualFunc(); - Module.Child2.prototype.runVirtualFunc(c2); - c2.virtualFunc2(); - -''' + (''' - // extend the class from JS - var c3 = new Module.Child2; - Module.customizeVTable(c3, [{ - original: Module.Child2.prototype.virtualFunc, - replacement: function() { - Module.print('*js virtualf replacement*'); - } - }, { - original: Module.Child2.prototype.virtualFunc2, - replacement: function() { - Module.print('*js virtualf2 replacement*'); - } - }]); - c3.virtualFunc(); - Module.Child2.prototype.runVirtualFunc(c3); - c3.virtualFunc2(); - - c2.virtualFunc(); // original should remain the same - Module.Child2.prototype.runVirtualFunc(c2); - c2.virtualFunc2(); -''') + ''' - - Module.print('*ok*'); - \'\'\' - src = open(filename, 'a') - src.write(script_src_2 + '\\n') - src.close() -''' + def post3(filename): + script_src_2 = ''' + var sme = new Module.Parent(42); + sme.mulVal(2); + Module.print('*') + Module.print(sme.getVal()); + + Module.print('c1'); + + var c1 = new Module.Child1(); + Module.print(c1.getVal()); + c1.mulVal(2); + Module.print(c1.getVal()); + Module.print(c1.getValSqr()); + Module.print(c1.getValSqr(3)); + Module.print(c1.getValTimes()); // default argument should be 1 + Module.print(c1.getValTimes(2)); + + Module.print('c1 v2'); + + c1 = new Module.Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2 + Module.print(c1.getVal()); + c1.mulVal(2); + Module.print(c1.getVal()); + Module.print(c1.getValSqr()); + Module.print(c1.getValSqr(3)); + + Module.print('c2') + + var c2 = new Module.Child2(); + Module.print(c2.getVal()); + c2.mulVal(2); + Module.print(c2.getVal()); + Module.print(c2.getValCube()); + var succeeded; + try { + succeeded = 0; + Module.print(c2.doSomethingSecret()); // should fail since private + succeeded = 1; + } catch(e) {} + Module.print(succeeded); + try { + succeeded = 0; + Module.print(c2.getValSqr()); // function from the other class + succeeded = 1; + } catch(e) {} + Module.print(succeeded); + try { + succeeded = 0; + c2.getValCube(); // sanity + succeeded = 1; + } catch(e) {} + Module.print(succeeded); + + Module.Child2.prototype.printStatic(); // static calls go through the prototype + + // virtual function + c2.virtualFunc(); + Module.Child2.prototype.runVirtualFunc(c2); + c2.virtualFunc2(); + + // extend the class from JS + var c3 = new Module.Child2; + Module.customizeVTable(c3, [{ + original: Module.Child2.prototype.virtualFunc, + replacement: function() { + Module.print('*js virtualf replacement*'); + } + }, { + original: Module.Child2.prototype.virtualFunc2, + replacement: function() { + Module.print('*js virtualf2 replacement*'); + } + }]); + c3.virtualFunc(); + Module.Child2.prototype.runVirtualFunc(c3); + c3.virtualFunc2(); + + c2.virtualFunc(); // original should remain the same + Module.Child2.prototype.runVirtualFunc(c2); + c2.virtualFunc2(); + Module.print('*ok*'); + ''' + src = open(filename, 'a') + src.write(script_src_2 + '\n') + src.close() Settings.RESERVED_FUNCTION_POINTERS = 20 @@ -9349,7 +9349,7 @@ Child2:9 *virtualf* *virtualf2*''') + ''' *ok* -''', post_build=[post2, post3]) +''', post_build=(post2, post3)) def test_scriptaclass_2(self): if self.emcc_args is None: return self.skip('requires emcc') @@ -9578,25 +9578,138 @@ def process(filename): } ''' try: - post = r''' -def process(filename): - lines = open(filename, 'r').readlines() - lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines) - found_line_num = any(('//@line 7 "' in line) for line in lines) - found_filename = any(('src.cpp"\n' in line) for line in lines) - assert found_line_num, 'Must have debug info with the line number' - assert found_filename, 'Must have debug info with the filename' -''' - self.do_run(src, '*nothingatall*', post_build=post) + self.do_run(src, '*nothingatall*') except Exception, e: # This test *should* fail - assert 'Assertion failed' in str(e), str(e) + assert 'Assertion failed: x < 15' in str(e), str(e) + + lines = open('src.cpp.o.js', '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' + + def test_source_map(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") + if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') + if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') + + src = ''' + #include <stdio.h> + #include <assert.h> + + __attribute__((noinline)) int foo() { + printf("hi"); // line 6 + return 1; // line 7 + } + + int main() { + printf("%d", foo()); // line 11 + return 0; // line 12 + } + ''' + + dirname = self.get_dir() + src_filename = os.path.join(dirname, 'src.cpp') + out_filename = os.path.join(dirname, 'a.out.js') + no_maps_filename = os.path.join(dirname, 'no-maps.out.js') + + with open(src_filename, 'w') as f: f.write(src) + assert '-g4' not in Building.COMPILER_TEST_OPTS + Building.emcc(src_filename, Settings.serialize() + self.emcc_args + + Building.COMPILER_TEST_OPTS, out_filename) + # the file name may find its way into the generated code, so make sure we + # can do an apples-to-apples comparison by compiling with the same file name + shutil.move(out_filename, no_maps_filename) + with open(no_maps_filename) as f: no_maps_file = f.read() + no_maps_file = re.sub(' *//@.*$', '', no_maps_file, flags=re.MULTILINE) + Building.COMPILER_TEST_OPTS.append('-g4') + + def build_and_check(): + import json + Building.emcc(src_filename, Settings.serialize() + self.emcc_args + + Building.COMPILER_TEST_OPTS, out_filename, stderr=PIPE) + with open(out_filename) as f: out_file = f.read() + # after removing the @line and @sourceMappingURL comments, the build + # result should be identical to the non-source-mapped debug version. + # this is worth checking because the parser AST swaps strings for token + # objects when generating source maps, so we want to make sure the + # optimizer can deal with both types. + out_file = re.sub(' *//@.*$', '', out_file, flags=re.MULTILINE) + self.assertIdentical(no_maps_file, out_file) + map_filename = out_filename + '.map' + data = json.load(open(map_filename, 'r')) + self.assertIdentical(out_filename, data['file']) + self.assertIdentical(src_filename, data['sources'][0]) + self.assertIdentical(src, data['sourcesContent'][0]) + mappings = json.loads(jsrun.run_js( + path_from_root('tools', 'source-maps', 'sourcemap2json.js'), + tools.shared.NODE_JS, [map_filename])) + seen_lines = set() + for m in mappings: + self.assertIdentical(src_filename, m['source']) + seen_lines.add(m['originalLine']) + # ensure that all the 'meaningful' lines in the original code get mapped + assert seen_lines.issuperset([6, 7, 11, 12]) + + # EMCC_DEBUG=2 causes lots of intermediate files to be written, and so + # serves as a stress test for source maps because it needs to correlate + # line numbers across all those files. + old_emcc_debug = os.environ.get('EMCC_DEBUG', None) + os.environ.pop('EMCC_DEBUG', None) + try: + build_and_check() + os.environ['EMCC_DEBUG'] = '2' + build_and_check() + finally: + if old_emcc_debug is not None: + os.environ['EMCC_DEBUG'] = old_emcc_debug + else: + os.environ.pop('EMCC_DEBUG', None) + + def test_exception_source_map(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") + if '-g4' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g4') + if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') + + src = ''' + #include <stdio.h> + + __attribute__((noinline)) void foo(int i) { + if (i < 10) throw i; // line 5 + } + + int main() { + int i; + scanf("%d", &i); + foo(i); + return 0; + } + ''' + + def post(filename): + import json + map_filename = filename + '.map' + mappings = json.loads(jsrun.run_js( + path_from_root('tools', 'source-maps', 'sourcemap2json.js'), + tools.shared.NODE_JS, [map_filename])) + with open(filename) as f: lines = f.readlines() + for m in mappings: + if m['originalLine'] == 5 and '__cxa_throw' in lines[m['generatedLine']]: + return + assert False, 'Must label throw statements with line numbers' + + dirname = self.get_dir() + self.build(src, dirname, os.path.join(dirname, 'src.cpp'), post_build=(None, post)) def test_linespecific(self): if Settings.ASM_JS: return self.skip('asm always has corrections on') if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') - if self.emcc_args: self.emcc_args += ['--llvm-opts', '0'] # llvm full opts make the expected failures here not happen + if self.emcc_args: + self.emcc_args += ['--llvm-opts', '0'] # llvm full opts make the expected failures here not happen + Building.COMPILER_TEST_OPTS += ['--llvm-opts', '0'] Settings.CHECK_SIGNS = 0 Settings.CHECK_OVERFLOWS = 0 @@ -9902,7 +10015,7 @@ finalizing 3 (global == 0) ''') # Generate tests for everything - def make_run(fullname, name=-1, compiler=-1, llvm_opts=0, embetter=0, quantum_size=0, typed_arrays=0, emcc_args=None, env='{}'): + def make_run(fullname, name=-1, compiler=-1, embetter=0, quantum_size=0, typed_arrays=0, emcc_args=None, env='{}'): exec(''' class %s(T): run_name = '%s' @@ -9937,9 +10050,18 @@ class %s(T): Building.LLVM_OPTS = 0 if '-O2' in self.emcc_args: Building.COMPILER_TEST_OPTS = [] # remove -g in -O2 tests, for more coverage + #Building.COMPILER_TEST_OPTS += self.emcc_args + for arg in self.emcc_args: + if arg.startswith('-O'): + Building.COMPILER_TEST_OPTS.append(arg) # so bitcode is optimized too, this is for cpp to ll + else: + try: + key, value = arg.split('=') + Settings[key] = value # forward -s K=V + except: + pass return - llvm_opts = %d # 1 is yes, 2 is yes and unsafe embetter = %d quantum_size = %d # TODO: Move much of these to a init() function in shared.py, and reuse that @@ -9949,14 +10071,13 @@ class %s(T): Settings.MICRO_OPTS = embetter Settings.QUANTUM_SIZE = quantum_size Settings.ASSERTIONS = 1-embetter - Settings.SAFE_HEAP = 1-(embetter and llvm_opts) - Building.LLVM_OPTS = llvm_opts - Settings.CHECK_OVERFLOWS = 1-(embetter or llvm_opts) - Settings.CORRECT_OVERFLOWS = 1-(embetter and llvm_opts) + Settings.SAFE_HEAP = 1-embetter + Settings.CHECK_OVERFLOWS = 1-embetter + Settings.CORRECT_OVERFLOWS = 1-embetter Settings.CORRECT_SIGNS = 0 Settings.CORRECT_ROUNDINGS = 0 Settings.CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = [] - Settings.CHECK_SIGNS = 0 #1-(embetter or llvm_opts) + Settings.CHECK_SIGNS = 0 #1-embetter Settings.RUNTIME_TYPE_INFO = 0 Settings.DISABLE_EXCEPTION_CATCHING = 0 Settings.INCLUDE_FULL_LIBRARY = 0 @@ -9965,12 +10086,10 @@ class %s(T): Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2) Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0 Settings.PRECISE_I64_MATH = 0 - Settings.NAMED_GLOBALS = 0 if not (embetter and llvm_opts) else 1 - - Building.pick_llvm_opts(3) + Settings.NAMED_GLOBALS = 0 if not embetter else 1 TT = %s -''' % (fullname, fullname, env, fullname, fullname, compiler, str(emcc_args), llvm_opts, embetter, quantum_size, typed_arrays, fullname)) +''' % (fullname, fullname, env, fullname, fullname, compiler, str(emcc_args), embetter, quantum_size, typed_arrays, fullname)) return TT # Make one run with the defaults @@ -9989,16 +10108,14 @@ TT = %s exec('''asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env='{"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"}')''') # Make custom runs with various options - for compiler, quantum, embetter, typed_arrays, llvm_opts in [ - (CLANG, 4, 0, 0, 0), - (CLANG, 4, 0, 0, 1), - (CLANG, 4, 1, 1, 0), - (CLANG, 4, 1, 1, 1), + for compiler, quantum, embetter, typed_arrays in [ + (CLANG, 4, 0, 0), + (CLANG, 4, 1, 1), ]: - fullname = 's_%d_%d%s%s' % ( - llvm_opts, embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays) + fullname = 's_0_%d%s%s' % ( + embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays) ) - exec('%s = make_run(fullname, %r,%r,%d,%d,%d,%d)' % (fullname, fullname, compiler, llvm_opts, embetter, quantum, typed_arrays)) + exec('%s = make_run(fullname, %r,%r,%d,%d,%d)' % (fullname, fullname, compiler, embetter, quantum, typed_arrays)) del T # T is just a shape for the specific subclasses, we don't test it itself @@ -10132,23 +10249,25 @@ Options that are modified or new in %s include: (['-o', 'something.js', '-O3', '-s', 'ASM_JS=0'], 3, None, 1, 1), # and, test compiling to bitcode first (['-o', 'something.bc'], 0, [], 0, 0), - (['-o', 'something.bc'], 0, ['-O0'], 0, 0), - (['-o', 'something.bc'], 1, ['-O1'], 0, 0), - (['-o', 'something.bc'], 2, ['-O2'], 0, 0), - (['-o', 'something.bc'], 3, ['-O3', '-s', 'ASM_JS=0'], 1, 0), - (['-O1', '-o', 'something.bc'], 0, [], 0, 0), # -Ox is ignored and warned about + (['-o', 'something.bc', '-O0'], 0, [], 0, 0), + (['-o', 'something.bc', '-O1'], 1, ['-O1'], 0, 0), + (['-o', 'something.bc', '-O2'], 2, ['-O2'], 0, 0), + (['-o', 'something.bc', '-O3'], 3, ['-O3', '-s', 'ASM_JS=0'], 1, 0), + (['-O1', '-o', 'something.bc'], 1, [], 0, 0), ]: print params, opt_level, bc_params, closure, has_malloc self.clear() keep_debug = '-g' in params - output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params, + args = [PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params + print '..', args + output = Popen(args, stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] if bc_params is not None: - if '-O1' in params and 'something.bc' in params: - assert '-Ox flags ignored, since not generating JavaScript' in output[1] assert os.path.exists('something.bc'), output[1] - output = Popen([PYTHON, compiler, 'something.bc', '-o', 'something.js'] + bc_params, stdout=PIPE, stderr=PIPE).communicate() + bc_args = [PYTHON, compiler, 'something.bc', '-o', 'something.js'] + bc_params + print '....', bc_args + output = Popen(bc_args, stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists('something.js'), output[1] assert ('Applying some potentially unsafe optimizations!' in output[1]) == (opt_level >= 3), 'unsafe warning should appear in opt >= 3' self.assertContained('hello, world!', run_js('something.js')) @@ -11397,64 +11516,6 @@ seeked= file. code = open('a.out.js').read() assert 'SAFE_HEAP' in code, 'valid -s option had an effect' - def test_optimize_normally(self): - assert not os.environ.get('EMCC_OPTIMIZE_NORMALLY') - assert not os.environ.get('EMCC_DEBUG') - - for optimize_normally in [0, 1]: - print optimize_normally - try: - if optimize_normally: os.environ['EMCC_OPTIMIZE_NORMALLY'] = '1' - os.environ['EMCC_DEBUG'] = '1' - - open(self.in_dir('main.cpp'), 'w').write(r''' - extern "C" { - void something(); - } - - int main() { - something(); - return 0; - } - ''') - open(self.in_dir('supp.cpp'), 'w').write(r''' - #include <stdio.h> - - extern "C" { - void something() { - printf("yello\n"); - } - } - ''') - out, err = Popen([PYTHON, EMCC, self.in_dir('main.cpp'), '-O2', '-o', 'main.o'], stdout=PIPE, stderr=PIPE).communicate() - assert ("emcc: LLVM opts: ['-O3']" in err) == optimize_normally - assert (' with -O3 since EMCC_OPTIMIZE_NORMALLY defined' in err) == optimize_normally - - out, err = Popen([PYTHON, EMCC, self.in_dir('supp.cpp'), '-O2', '-o', 'supp.o'], stdout=PIPE, stderr=PIPE).communicate() - assert ("emcc: LLVM opts: ['-O3']" in err) == optimize_normally - assert (' with -O3 since EMCC_OPTIMIZE_NORMALLY defined' in err) == optimize_normally - - out, err = Popen([PYTHON, EMCC, self.in_dir('main.o'), self.in_dir('supp.o'), '-O2', '-o', 'both.o'], stdout=PIPE, stderr=PIPE).communicate() - assert "emcc: LLVM opts: ['-O3']" not in err - assert ' with -O3 since EMCC_OPTIMIZE_NORMALLY defined' not in err - assert ('despite EMCC_OPTIMIZE_NORMALLY since not sou |