def test_emit_all_features(self): p = shared.run_process(shared.WASM_OPT + ['--emit-target-features', '-all', '-o', '-'], input="(module)", check=False, capture_output=True, decode_output=False) self.assertEqual(p.returncode, 0) p2 = shared.run_process(shared.WASM_OPT + ['--print-features', '-o', os.devnull], input=p.stdout, check=False, capture_output=True) self.assertEqual(p2.returncode, 0) self.assertEqual([ '--enable-threads', '--enable-mutable-globals', '--enable-nontrapping-float-to-int', '--enable-simd', '--enable-bulk-memory', '--enable-sign-ext', '--enable-exception-handling', '--enable-tail-call', '--enable-reference-types', '--enable-multivalue', '--enable-gc', '--enable-memory64', '--enable-typed-function-references', ], p2.stdout.splitlines())
def test_empty_data(self): try: temp = tempfile.NamedTemporaryFile(delete=False).name shared.run_process(shared.WASM_OPT + ['-ttf', temp], capture_output=True) finally: os.unlink(temp)
def test_superset_even_without_detect_features(self): # It is ok to enable additional features past what is in the section, # even without passing --detect-features (which is now a no-op). path = self.input_path('signext_target_feature.wasm') shared.run_process( shared.WASM_OPT + ['--print', '--enable-simd', '-o', os.devnull, path])
def test_superset(self): # It is ok to enable additional features past what is in the section. shared.run_process(shared.WASM_OPT + [ '--print', '--detect-features', '-mvp', '--enable-simd', '--enable-sign-ext', self.input_path('signext_target_feature.wasm') ])
def test(input_file): shared.run_process(shared.WASM_OPT + [input_file, '--asyncify', '-o', 'a.wasm']) shared.run_process(shared.WASM_DIS + ['a.wasm', '-o', 'a.wat']) output = shared.run_process(shared.WASM_SHELL + ['a.wat'], capture_output=True).stdout with open(self.input_path('asyncify-pure.txt'), 'r') as f: self.assert_equal_ignoring_line_endings(f.read(), output)
def check_feature(self, module, error, flag): p = run_process(WASM_OPT + ['--mvp-features', '--print'], input=module, check=False, capture_output=True) self.assertIn(error, p.stderr) self.assertIn('Fatal: error in validating input', p.stderr) self.assertNotEqual(p.returncode, 0) p = run_process(WASM_OPT + ['--mvp-features', flag, '--print'], input=module, check=False, capture_output=True) self.assertEqual(p.returncode, 0)
def test_asyncify_pure_wasm(self): run_process(WASM_OPT + [ self.input_path('asyncify-pure.wast'), '--asyncify', '-o', 'a.wasm' ]) run_process(WASM_DIS + ['a.wasm', '-o', 'a.wast']) output = run_process(WASM_SHELL + ['a.wast'], capture_output=True).stdout with open(self.input_path('asyncify-pure.txt'), 'r') as f: self.assertEqual(f.read(), output)
def test_no_crash(self): # run dwarf processing on some interesting large files, too big to be # worth putting in passes where the text output would be massive. We # just check that no assertion are hit. path = self.input_path('dwarf') for name in os.listdir(path): args = [os.path.join(path, name)] + \ ['-g', '--dwarfdump', '--roundtrip', '--dwarfdump'] shared.run_process(shared.WASM_OPT + args, capture_output=True)
def check_feature(self, module, error, flag): p = run_process(WASM_OPT + ['--mvp-features', '--print', '-o', os.devnull], input=module, check=False, capture_output=True) self.assertIn(error, p.stderr) self.assertIn('Fatal: error in validating input', p.stderr) self.assertNotEqual(p.returncode, 0) p = run_process(WASM_OPT + ['--mvp-features', flag, '--print', '-o', os.devnull], input=module, check=False, capture_output=True) self.assertEqual(p.returncode, 0)
def test_dwarf_incompatibility(self): warning = 'not fully compatible with DWARF' path = self.input_path(os.path.join('dwarf', 'cubescript.wasm')) args = [path, '-g'] # flatten warns err = shared.run_process(shared.WASM_OPT + args + ['--flatten'], stderr=subprocess.PIPE).stderr self.assertIn(warning, err) # safe passes do not err = shared.run_process(shared.WASM_OPT + args + ['--metrics'], stderr=subprocess.PIPE).stderr self.assertNotIn(warning, err)
def test_stack_ir_opts(self): path = self.input_path('stack_ir.wat') opt = shared.run_process(shared.WASM_OPT + [path, '-O', '--generate-stack-ir', '--optimize-stack-ir', '--print-stack-ir', '-o', 'a.wasm'], capture_output=True).stdout nonopt = shared.run_process(shared.WASM_OPT + [path, '-O', '--generate-stack-ir', '--print-stack-ir', '-o', 'b.wasm'], capture_output=True).stdout # see a difference in the printed stack IR (the optimizations let us # remove a pair of local.set/get) self.assertNotEqual(opt, nonopt) self.assertLess(len(opt), len(nonopt)) # see a difference in the actual emitted wasm binary. opt_size = os.path.getsize('a.wasm') nonopt_size = os.path.getsize('b.wasm') self.assertLess(opt_size, nonopt_size)
def test_asyncify_too_many_locals(self): # With 64K+ locals we cannot run the liveness analysis optimization, but # should at least not fatally error. temp = tempfile.NamedTemporaryFile().name with open(temp, 'w') as f: f.write('(module\n') f.write(' (import "env" "foo" (func $import))\n') f.write(' (func $many-locals\n') for i in range(65 * 1024): f.write(f' (local $x{i} i32)\n') f.write(' (call $import)\n') f.write(' )\n') f.write(')\n') shared.run_process(shared.WASM_OPT + [temp, '--asyncify'])
def test(args): print(args) run_process(WASM_OPT + args + [self.input_path('bysyncify-sleep.wast'), '--bysyncify', '-o', 'a.wasm']) run_process(WASM_OPT + args + [self.input_path('bysyncify-coroutine.wast'), '--bysyncify', '-o', 'b.wasm']) run_process(WASM_OPT + args + [self.input_path('bysyncify-stackOverflow.wast'), '--bysyncify', '-o', 'c.wasm']) print(' file size: %d' % os.path.getsize('a.wasm')) run_process([NODEJS, self.input_path('bysyncify.js')])
def test_symbolmap(self): input_wasm = self.input_path('hello_world.wat') # write the symbol map to a file args = [input_wasm, '--symbolmap=out.symbols'] shared.run_process(shared.WASM_OPT + args) with open('out.symbols') as f: file_output = f.read() # write the symbol map to stdout args = [input_wasm, '--symbolmap'] stdout_output = shared.run_process(shared.WASM_OPT + args, capture_output=True).stdout # ignore whitespace in the comparison as on windows stdout gets an \r self.assertEqual(file_output.strip(), stdout_output.strip()) # the wat contains a single function "add" self.assertIn('0:add', file_output)
def check_feature(self, module, error, flag, const_flags=[]): p = shared.run_process(shared.WASM_OPT + ['--mvp-features', '--print', '-o', os.devnull] + const_flags, input=module, check=False, capture_output=True) self.assertIn(error, p.stderr) self.assertIn('Fatal: error validating input', p.stderr) self.assertNotEqual(p.returncode, 0) p = shared.run_process( shared.WASM_OPT + ['--mvp-features', '--print', '-o', os.devnull] + const_flags + [flag], input=module, check=False, capture_output=True) self.assertEqual(p.returncode, 0)
def test_empty_initial(self): # generate fuzz from random data data = self.input_path('random_data.txt') a = shared.run_process(shared.WASM_OPT + ['-ttf', '--print', data], stdout=subprocess.PIPE).stdout # generate fuzz from random data with initial empty wasm empty_wasm = self.input_path('empty.wasm') b = shared.run_process( shared.WASM_OPT + ['-ttf', '--print', data, '--initial-fuzz=' + empty_wasm], stdout=subprocess.PIPE).stdout # an empty initial wasm causes no changes self.assertEqual(a, b)
def check_invalid(self, module, error): p = shared.run_process(shared.WASM_OPT + ['--experimental-poppy', '--print', '-o', os.devnull], input=module, check=False, capture_output=True) self.assertIn(error, p.stderr) self.assertIn('Fatal: error validating input', p.stderr) self.assertNotEqual(p.returncode, 0)
def test_wrapper(self): # the wrapper C code should only call the hang limit initializer if # that is present. empty_wasm = self.input_path('empty.wasm') args = [empty_wasm, '--emit-wasm2c-wrapper=output.c'] shared.run_process(shared.WASM_OPT + args) with open('output.c') as f: normal_output = f.read() # running with ttf generates a new wasm for fuzzing, which always # includes the hang limit initializer function shared.run_process(shared.WASM_OPT + args + ['-ttf']) with open('output.c') as f: ttf_output = f.read() hang_limit_name = 'hangLimitInitializer' self.assertIn(hang_limit_name, ttf_output) self.assertNotIn(hang_limit_name, normal_output)
def disassemble(self, filename): path = self.input_path(filename) p = run_process(WASM_OPT + ['--print', '-o', os.devnull, path], check=False, capture_output=True) self.assertEqual(p.returncode, 0) self.assertEqual(p.stderr, '') return p.stdout
def test_bad_datacount(self): path = self.input_path('bulkmem_bad_datacount.wasm') p = run_process(WASM_OPT + ['-g', '-o', '-', path], check=False, capture_output=True) self.assertNotEqual(p.returncode, 0) self.assertIn('Number of segments does not agree with DataCount section', p.stderr)
def disassemble(self, filename): path = os.path.join(options.binaryen_test, 'unit', 'input', filename) p = run_process(WASM_OPT + ['--print', '-o', os.devnull, path], check=False, capture_output=True) self.assertEqual(p.returncode, 0) self.assertEqual(p.stderr, '') return p.stdout
def roundtrip(self, filename, opts=[]): path = self.input_path(filename) p = run_process(WASM_OPT + ['-g', '-o', 'a.wasm', path] + opts) self.assertEqual(p.returncode, 0) with open(path, 'rb') as f: with open('a.wasm', 'rb') as g: self.assertEqual(g.read(), f.read())
def test_bad_datacount(self): path = self.input_path('bulkmem_bad_datacount.wasm') p = shared.run_process(shared.WASM_OPT + ['-g', '-o', '-', path], check=False, capture_output=True) self.assertNotEqual(p.returncode, 0) self.assertIn('Number of segments does not agree with DataCount section', p.stderr)
def test_asyncify_list_bad(self): for arg, warning in [ ('--pass-arg=asyncify-removelist@nonexistent', 'nonexistent'), ('--pass-arg=asyncify-onlylist@nonexistent', 'nonexistent'), ('--pass-arg=asyncify-removelist@main', None), ('--pass-arg=asyncify-onlylist@main', None), ('--pass-arg=asyncify-removelist@m*n', None), ('--pass-arg=asyncify-onlylist@m*n', None), ('--pass-arg=asyncify-onlylist@main*', None), ('--pass-arg=asyncify-onlylist@*main', None), ('--pass-arg=asyncify-removelist@non*existent', 'non*existent'), ('--pass-arg=asyncify-onlylist@non*existent', 'non*existent'), ('--pass-arg=asyncify-onlylist@DOS_ReadFile(unsigned short, unsigned char*, unsigned short*, bool)', None), ]: print(arg, warning) err = shared.run_process(shared.WASM_OPT + [ '-q', self.input_path('asyncify-pure.wat'), '--asyncify', arg ], stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.strip() if warning: self.assertIn('warning', err) self.assertIn(warning, err) else: self.assertNotIn('warning', err)
def test_emit_all_features(self): p = run_process(WASM_OPT + ['--emit-target-features', '-all', '-o', '-'], input="(module)", check=False, capture_output=True) self.assertEqual(p.returncode, 0) p2 = run_process(WASM_OPT + ['--print-features', '-o', os.devnull], input=p.stdout, check=False, capture_output=True) self.assertEqual(p2.returncode, 0) self.assertEqual([ '--enable-threads', '--enable-bulk-memory', '--enable-exception-handling', '--enable-mutable-globals', '--enable-nontrapping-float-to-int', '--enable-sign-ext', '--enable-simd', '--enable-tail-call' ], p2.stdout.split())
def test_asyncify_pure_wasm(self): def test(input_file): shared.run_process(shared.WASM_OPT + [input_file, '--asyncify', '-o', 'a.wasm']) shared.run_process(shared.WASM_DIS + ['a.wasm', '-o', 'a.wat']) output = shared.run_process(shared.WASM_SHELL + ['a.wat'], capture_output=True).stdout with open(self.input_path('asyncify-pure.txt'), 'r') as f: self.assertEqual(f.read(), output) # test wat input wat = self.input_path('asyncify-pure.wat') test(wat) # test wasm input shared.run_process(shared.WASM_AS + [wat, '-o', 'a.wasm']) test('a.wasm')
def check_features(self, filename, features, opts=[]): path = self.input_path(filename) cmd = WASM_OPT + ['--print-features', '-o', os.devnull, path] + opts p = run_process(cmd, check=False, capture_output=True) self.assertEqual(p.returncode, 0) self.assertEqual(p.stderr, '') self.assertEqual(p.stdout.split('\n')[:-1], ['--enable-' + f for f in features])
def test_em_asm_mangled_string(self): input_dir = os.path.dirname(__file__) p = run_process(WASM_EMSCRIPTEN_FINALIZE + [ os.path.join(input_dir, 'input', 'em_asm_mangled_string.wast'), '-o', os.devnull, '--global-base=1024' ], check=False, capture_output=True) self.assertNotEqual(p.returncode, 0) self.assertIn('Fatal: local.get of unknown in arg0 of call to $emscripten_asm_const_int (used by EM_ASM* macros) in function $main.', p.stderr) self.assertIn('This might be caused by aggressive compiler transformations. Consider using EM_JS instead.', p.stderr)
def check_features(self, filename, features, opts=[]): path = self.input_path(filename) cmd = WASM_OPT + ['--print-features', '-o', os.devnull, path] + opts p = run_process(cmd, check=False, capture_output=True) self.assertEqual(p.returncode, 0) self.assertEqual(p.stderr, '') self.assertEqual( p.stdout.split('\n')[:-1], ['--enable-' + f for f in features])
def roundtrip(self, filename, opts=[]): path = self.input_path(filename) p = run_process(WASM_OPT + ['-g', '-o', '-', path] + opts, check=False, capture_output=True) self.assertEqual(p.returncode, 0) self.assertEqual(p.stderr, '') with open(path, 'rb') as f: self.assertEqual(str(p.stdout), str(f.read()))
def test_incompatible_features_forced(self): path = self.input_path('signext_target_feature.wasm') p = run_process( WASM_OPT + ['--print', '--detect-features', '-mvp', '--enable-simd', '-o', os.devnull, path], check=False, capture_output=True ) self.assertNotEqual(p.returncode, 0) self.assertIn('all used features should be allowed', p.stderr)
def test_incompatible_features_forced(self): path = self.input_path('signext_target_feature.wasm') p = shared.run_process( shared.WASM_OPT + ['--print', '--detect-features', '-mvp', '--enable-simd', '-o', os.devnull, path], check=False, capture_output=True ) self.assertNotEqual(p.returncode, 0) self.assertIn('all used features should be allowed', p.stderr)
def test_large_segment_unmergeable(self): data = '\n'.join('(data (i32.const %i) "A")' % i for i in range(100001)) module = '(module (memory 256 256) %s)' % data opts = ['--memory-packing', '--enable-bulk-memory', '--print', '-o', os.devnull] p = run_process(WASM_OPT + opts, input=module, check=False, capture_output=True) self.assertEqual(p.returncode, 0) self.assertIn('Some VMs may not accept this binary', p.stderr) self.assertIn('Run the limit-segments pass to merge segments.', p.stderr)
def test_explicit_detect_features(self): path = os.path.join(options.binaryen_test, 'unit', 'input', 'signext_target_feature.wasm') p = run_process( WASM_OPT + ['--print', '-mvp', '--detect-features', '-o', os.devnull, path], check=False, capture_output=True ) self.assertEqual(p.returncode, 0) self.assertEqual(p.stderr, '')
def test_incompatible_features(self): path = self.input_path('signext_target_feature.wasm') p = run_process( WASM_OPT + ['--print', '--enable-simd', '-o', os.devnull, path], check=False, capture_output=True ) self.assertNotEqual(p.returncode, 0) self.assertIn('Fatal: module features do not match specified features. ' + 'Use --detect-features to resolve.', p.stderr)
def test_incompatible_features(self): path = self.input_path('signext_target_feature.wasm') p = run_process(WASM_OPT + ['--print', '--enable-simd', '-o', os.devnull, path], check=False, capture_output=True) self.assertNotEqual(p.returncode, 0) self.assertIn( 'Fatal: module features do not match specified features. ' + 'Use --detect-features to resolve.', p.stderr)
def test_large_segment(self): data = '"' + (('A' + ('\\00' * 9)) * 100001) + '"' module = ''' (module (memory 256 256) (data (i32.const 0) %s) ) ''' % data opts = ['--memory-packing', '--disable-bulk-memory', '--print', '-o', os.devnull] p = run_process(WASM_OPT + opts, input=module, check=False, capture_output=True) output = [ '(data (i32.const 999970) "A")', '(data (i32.const 999980) "A")', '(data (i32.const 999990) "A' + ('\\00' * 9) + 'A")' ] self.assertEqual(p.returncode, 0) for line in output: self.assertIn(line, p.stdout)