diff options
-rw-r--r-- | .flake8 | 5 | ||||
-rwxr-xr-x | scripts/gen-emscripten-exported-json.py | 126 | ||||
-rwxr-xr-x | scripts/sha256sum.py | 22 | ||||
-rwxr-xr-x | src/wasm2c_tmpl.py | 86 | ||||
-rw-r--r-- | test/find_exe.py | 64 | ||||
-rwxr-xr-x | test/gen-spec-js.py | 902 | ||||
-rwxr-xr-x | test/gen-wasm.py | 308 | ||||
-rwxr-xr-x | test/run-roundtrip.py | 303 | ||||
-rwxr-xr-x | test/run-spec-wasm2c.py | 698 | ||||
-rwxr-xr-x | test/run-tests.py | 1415 | ||||
-rwxr-xr-x | test/update-spec-tests.py | 104 | ||||
-rw-r--r-- | test/utils.py | 258 |
12 files changed, 2145 insertions, 2146 deletions
@@ -1,8 +1,5 @@ [flake8] ignore = - E111, # indentation not a multiple of 4 (we use 2)) - E114, # comment indentation not a multiple of 4 E501, # line too long - E121, # continuation line under-indented for hanging indent W504 # line break after binary operator -exclude = ./third_party +exclude = ./third_party ./out diff --git a/scripts/gen-emscripten-exported-json.py b/scripts/gen-emscripten-exported-json.py index 0b23c0ee..7f5246f2 100755 --- a/scripts/gen-emscripten-exported-json.py +++ b/scripts/gen-emscripten-exported-json.py @@ -32,78 +32,78 @@ from utils import Executable, Error # noqa: E402 def FindFiles(cmake_build_dir): - result = [] - for root, dirs, files in os.walk(cmake_build_dir): - for file_ in files: - path = os.path.join(root, file_) - if file_ == 'libwabt.a' or re.search(r'emscripten-helpers', file_): - result.append(path) - return result + result = [] + for root, dirs, files in os.walk(cmake_build_dir): + for file_ in files: + path = os.path.join(root, file_) + if file_ == 'libwabt.a' or re.search(r'emscripten-helpers', file_): + result.append(path) + return result def GetNM(emscripten_dir): - python = Executable(sys.executable) - stdout = python.RunWithArgsForStdout( - '-c', 'import tools.shared; print(tools.shared.LLVM_ROOT)', - cwd=emscripten_dir) - return Executable(os.path.join(stdout.strip(), 'llvm-nm')) + python = Executable(sys.executable) + stdout = python.RunWithArgsForStdout( + '-c', 'import tools.shared; print(tools.shared.LLVM_ROOT)', + cwd=emscripten_dir) + return Executable(os.path.join(stdout.strip(), 'llvm-nm')) def ProcessFile(nm, file_): - names = [] - for line in nm.RunWithArgsForStdout(file_).splitlines(): - line = line.rstrip() - if not line.lstrip(): - continue - if line.endswith(':'): - # displaying the archive name, e.g. "foo.c.o:" - continue - # line looks like: - # - # -------- d yycheck - # -------- t wabt_strndup_ - # U wabt_parse_int64 - # -------- T wabt_offsetof_allocator_alloc - # - # we want to only keep the "T" names; the extern function symbols defined - # in the object. - type_ = line[9] - if type_ != 'T': - continue - name = line[11:] - names.append('_' + name) - return names + names = [] + for line in nm.RunWithArgsForStdout(file_).splitlines(): + line = line.rstrip() + if not line.lstrip(): + continue + if line.endswith(':'): + # displaying the archive name, e.g. "foo.c.o:" + continue + # line looks like: + # + # -------- d yycheck + # -------- t wabt_strndup_ + # U wabt_parse_int64 + # -------- T wabt_offsetof_allocator_alloc + # + # we want to only keep the "T" names; the extern function symbols defined + # in the object. + type_ = line[9] + if type_ != 'T': + continue + name = line[11:] + names.append('_' + name) + return names def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('-o', '--output', metavar='PATH', help='output file.') - parser.add_argument('-v', '--verbose', - help='print more diagnostic messages.', - action='store_true') - parser.add_argument('--emscripten-dir', metavar='PATH', - help='emscripten directory', - default=DEFAULT_EMSCRIPTEN_DIR) - parser.add_argument('cmake_build_dir', metavar='cmake_build_dir') - options = parser.parse_args(args) - nm = GetNM(options.emscripten_dir) - - names = [] - for file_ in FindFiles(options.cmake_build_dir): - names.extend(ProcessFile(nm, file_)) - - out_data = json.dumps(sorted(names), separators=(',\n', ':\n')) - if options.output: - with open(options.output, 'w') as out_file: - out_file.write(out_data) - else: - print out_data - return 0 + parser = argparse.ArgumentParser() + parser.add_argument('-o', '--output', metavar='PATH', help='output file.') + parser.add_argument('-v', '--verbose', + help='print more diagnostic messages.', + action='store_true') + parser.add_argument('--emscripten-dir', metavar='PATH', + help='emscripten directory', + default=DEFAULT_EMSCRIPTEN_DIR) + parser.add_argument('cmake_build_dir', metavar='cmake_build_dir') + options = parser.parse_args(args) + nm = GetNM(options.emscripten_dir) + + names = [] + for file_ in FindFiles(options.cmake_build_dir): + names.extend(ProcessFile(nm, file_)) + + out_data = json.dumps(sorted(names), separators=(',\n', ':\n')) + if options.output: + with open(options.output, 'w') as out_file: + out_file.write(out_data) + else: + print out_data + return 0 if __name__ == '__main__': - try: - sys.exit(main(sys.argv[1:])) - except Error as e: - sys.stderr.write(str(e) + '\n') - sys.exit(1) + try: + sys.exit(main(sys.argv[1:])) + except Error as e: + sys.stderr.write(str(e) + '\n') + sys.exit(1) diff --git a/scripts/sha256sum.py b/scripts/sha256sum.py index ff457d7a..5caedf5f 100755 --- a/scripts/sha256sum.py +++ b/scripts/sha256sum.py @@ -21,19 +21,19 @@ import sys def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('file', nargs='?') - options = parser.parse_args(args) + parser = argparse.ArgumentParser() + parser.add_argument('file', nargs='?') + options = parser.parse_args(args) - if options.file is None: - parser.error('Expected a file') + if options.file is None: + parser.error('Expected a file') - m = hashlib.sha256() - with open(options.file, 'rb') as f: - m.update(f.read()) - print('%s %s' % (m.hexdigest(), options.file)) - return 0 + m = hashlib.sha256() + with open(options.file, 'rb') as f: + m.update(f.read()) + print('%s %s' % (m.hexdigest(), options.file)) + return 0 if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + sys.exit(main(sys.argv[1:])) diff --git a/src/wasm2c_tmpl.py b/src/wasm2c_tmpl.py index 6f9c8225..b854d887 100755 --- a/src/wasm2c_tmpl.py +++ b/src/wasm2c_tmpl.py @@ -18,64 +18,64 @@ from __future__ import print_function import argparse try: - from cStringIO import StringIO + from cStringIO import StringIO except ImportError: - from io import StringIO + from io import StringIO import os import sys def EscapeCString(s): - out = '' - for b in bytearray(s.encode('utf-8')): - if b in (34, 92): - # " or \ - out += '\\' + chr(b) - elif b == 10: - # newline - out += '\\n' - elif 32 <= b <= 127: - # printable char - out += chr(b) - else: - # non-printable; write as \xab - out += '\\x%02x' % b + out = '' + for b in bytearray(s.encode('utf-8')): + if b in (34, 92): + # " or \ + out += '\\' + chr(b) + elif b == 10: + # newline + out += '\\n' + elif 32 <= b <= 127: + # printable char + out += chr(b) + else: + # non-printable; write as \xab + out += '\\x%02x' % b - return out + return out def main(args): - arg_parser = argparse.ArgumentParser() - arg_parser.add_argument('-o', '--output', metavar='PATH', - help='output file.') - arg_parser.add_argument('file', help='input file.') - options = arg_parser.parse_args(args) + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument('-o', '--output', metavar='PATH', + help='output file.') + arg_parser.add_argument('file', help='input file.') + options = arg_parser.parse_args(args) - section_name = None - output = StringIO() + section_name = None + output = StringIO() - output.write('/* Generated from \'%s\' by wasm2c_tmpl.py, do not edit! */\n' % - os.path.basename(options.file)) + output.write('/* Generated from \'%s\' by wasm2c_tmpl.py, do not edit! */\n' % + os.path.basename(options.file)) - with open(options.file) as f: - for line in f.readlines(): - if line.startswith('%%'): - if section_name is not None: - output.write(';\n\n') - section_name = line[2:-1] - output.write('const char SECTION_NAME(%s)[] =\n' % section_name) - else: - output.write('"%s"\n' % EscapeCString(line)) + with open(options.file) as f: + for line in f.readlines(): + if line.startswith('%%'): + if section_name is not None: + output.write(';\n\n') + section_name = line[2:-1] + output.write('const char SECTION_NAME(%s)[] =\n' % section_name) + else: + output.write('"%s"\n' % EscapeCString(line)) - output.write(';\n') - if options.output: - with open(options.output, 'w') as outf: - outf.write(output.getvalue()) - else: - sys.stdout.write(output.getvalue()) + output.write(';\n') + if options.output: + with open(options.output, 'w') as outf: + outf.write(output.getvalue()) + else: + sys.stdout.write(output.getvalue()) - return 0 + return 0 if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + sys.exit(main(sys.argv[1:])) diff --git a/test/find_exe.py b/test/find_exe.py index 44a82bf4..80ea407d 100644 --- a/test/find_exe.py +++ b/test/find_exe.py @@ -34,79 +34,79 @@ GEN_SPEC_JS_PY = os.path.join(SCRIPT_DIR, 'gen-spec-js.py') def GetDefaultPath(): - return os.path.join(REPO_ROOT_DIR, 'bin') + return os.path.join(REPO_ROOT_DIR, 'bin') def GetDefaultExe(basename): - result = os.path.join(GetDefaultPath(), basename) - if IS_WINDOWS: - result += '.exe' - return result + result = os.path.join(GetDefaultPath(), basename) + if IS_WINDOWS: + result += '.exe' + return result def FindExeWithFallback(name, default_exe_list, override_exe=None): - result = override_exe - if result is not None: - if os.path.isdir(result): - result = os.path.join(result, name) - if IS_WINDOWS and os.path.splitext(result)[1] != '.exe': - result += '.exe' - if os.path.exists(result): - return os.path.abspath(result) - raise Error('%s executable not found.\nsearch path: %s\n' % (name, result)) + result = override_exe + if result is not None: + if os.path.isdir(result): + result = os.path.join(result, name) + if IS_WINDOWS and os.path.splitext(result)[1] != '.exe': + result += '.exe' + if os.path.exists(result): + return os.path.abspath(result) + raise Error('%s executable not found.\nsearch path: %s\n' % (name, result)) - for result in default_exe_list: - if os.path.exists(result): - return os.path.abspath(result) + for result in default_exe_list: + if os.path.exists(result): + return os.path.abspath(result) - raise Error('%s executable not found.\n%s\n' % - (name, '\n'.join('search path: %s' % path - for path in default_exe_list))) + raise Error('%s executable not found.\n%s\n' % + (name, '\n'.join('search path: %s' % path + for path in default_exe_list))) def FindExecutable(basename, override=None): - return FindExeWithFallback(basename, [GetDefaultExe(basename)], override) + return FindExeWithFallback(basename, [GetDefaultExe(basename)], override) def GetWat2WasmExecutable(override=None): - return FindExecutable('wat2wasm', override) + return FindExecutable('wat2wasm', override) def GetWast2JsonExecutable(override=None): - return FindExecutable('wast2json', override) + return FindExecutable('wast2json', override) def GetWasm2WatExecutable(override=None): - return FindExecutable('wasm2wat', override) + return FindExecutable('wasm2wat', override) def GetWasmdumpExecutable(override=None): - return FindExecutable('wasm-objdump', override) + return FindExecutable('wasm-objdump', override) def GetWasmInterpExecutable(override=None): - return FindExecutable('wasm-interp', override) + return FindExecutable('wasm-interp', override) def GetSpectestInterpExecutable(override=None): - return FindExecutable('spectest-interp', override) + return FindExecutable('spectest-interp', override) def GetWasmOpcodeCntExecutable(override=None): - return FindExecutable('wasm-opcodecnt', override) + return FindExecutable('wasm-opcodecnt', override) def GetWatDesugarExecutable(override=None): - return FindExecutable('wat-desugar', override) + return FindExecutable('wat-desugar', override) def GetWasmValidateExecutable(override=None): - return FindExecutable('wasm-validate', override) + return FindExecutable('wasm-validate', override) def GetWasm2CExecutable(override=None): - return FindExecutable('wasm2c', override) + return FindExecutable('wasm2c', override) def GetWasmStripExecutable(override=None): - return FindExecutable('wasm-strip', override) + return FindExecutable('wasm-strip', override) diff --git a/test/gen-spec-js.py b/test/gen-spec-js.py index 1cd5510c..7cd5c219 100755 --- a/test/gen-spec-js.py +++ b/test/gen-spec-js.py @@ -17,9 +17,9 @@ import argparse try: - from cStringIO import StringIO + from cStringIO import StringIO except ImportError: - from io import StringIO + from io import StringIO import json import os import re @@ -49,516 +49,516 @@ F64_QUIET_NAN_TAG = 0x8000000000000 def I32ToJS(value): - # JavaScript will return all i32 values as signed. - if value >= 2**31: - value -= 2**32 - return str(value) + # JavaScript will return all i32 values as signed. + if value >= 2**31: + value -= 2**32 + return str(value) def IsNaNF32(f32_bits): - return (F32_INF < f32_bits < F32_NEG_ZERO) or (f32_bits > F32_NEG_INF) + return (F32_INF < f32_bits < F32_NEG_ZERO) or (f32_bits > F32_NEG_INF) def ReinterpretF32(f32_bits): - return struct.unpack('<f', struct.pack('<I', f32_bits))[0] + return struct.unpack('<f', struct.pack('<I', f32_bits))[0] def NaNF32ToString(f32_bits): - result = '-' if f32_bits & F32_SIGN_BIT else '' - result += 'nan' - sig = f32_bits & F32_SIG_MASK - if sig != F32_QUIET_NAN_TAG: - result += ':0x%x' % sig - return result + result = '-' if f32_bits & F32_SIGN_BIT else '' + result += 'nan' + sig = f32_bits & F32_SIG_MASK + if sig != F32_QUIET_NAN_TAG: + result += ':0x%x' % sig + return result def F32ToWasm(f32_bits): - if IsNaNF32(f32_bits): - return 'f32.const %s' % NaNF32ToString(f32_bits) - elif f32_bits == F32_INF: - return 'f32.const infinity' - elif f32_bits == F32_NEG_INF: - return 'f32.const -infinity' - else: - return 'f32.const %s' % float.hex(ReinterpretF32(f32_bits)) + if IsNaNF32(f32_bits): + return 'f32.const %s' % NaNF32ToString(f32_bits) + elif f32_bits == F32_INF: + return 'f32.const infinity' + elif f32_bits == F32_NEG_INF: + return 'f32.const -infinity' + else: + return 'f32.const %s' % float.hex(ReinterpretF32(f32_bits)) def F32ToJS(f32_bits): - assert not IsNaNF32(f32_bits) - if f32_bits == F32_INF: - return 'Infinity' - elif f32_bits == F32_NEG_INF: - return '-Infinity' - else: - return 'f32(%s)' % ReinterpretF32(f32_bits) + assert not IsNaNF32(f32_bits) + if f32_bits == F32_INF: + return 'Infinity' + elif f32_bits == F32_NEG_INF: + return '-Infinity' + else: + return 'f32(%s)' % ReinterpretF32(f32_bits) def IsNaNF64(f64_bits): - return (F64_INF < f64_bits < F64_NEG_ZERO) or (f64_bits > F64_NEG_INF) + return (F64_INF < f64_bits < F64_NEG_ZERO) or (f64_bits > F64_NEG_INF) def ReinterpretF64(f64_bits): - return struct.unpack('<d', struct.pack('<Q', f64_bits))[0] + return struct.unpack('<d', struct.pack('<Q', f64_bits))[0] def NaNF64ToString(f64_bits): - result = '-' if f64_bits & F64_SIGN_BIT else '' - result += 'nan' - sig = f64_bits & F64_SIG_MASK - if sig != F64_QUIET_NAN_TAG: - result += ':0x%x' % sig - return result + result = '-' if f64_bits & F64_SIGN_BIT else '' + result += 'nan' + sig = f64_bits & F64_SIG_MASK + if sig != F64_QUIET_NAN_TAG: + result += ':0x%x' % sig + return result def F64ToWasm(f64_bits): - if IsNaNF64(f64_bits): - return 'f64.const %s' % NaNF64ToString(f64_bits) - elif f64_bits == F64_INF: - return 'f64.const infinity' - elif f64_bits == F64_NEG_INF: - return 'f64.const -infinity' - else: - return 'f64.const %s' % float.hex(ReinterpretF64(f64_bits)) + if IsNaNF64(f64_bits): + return 'f64.const %s' % NaNF64ToString(f64_bits) + elif f64_bits == F64_INF: + return 'f64.const infinity' + elif f64_bits == F64_NEG_INF: + return 'f64.const -infinity' + else: + return 'f64.const %s' % float.hex(ReinterpretF64(f64_bits)) def F64ToJS(f64_bits): - assert not IsNaNF64(f64_bits) - if f64_bits == F64_INF: - return 'Infinity' - elif f64_bits == F64_NEG_INF: - return '-Infinity' - else: - # Use repr to get full precision - return repr(ReinterpretF64(f64_bits)) + assert not IsNaNF64(f64_bits) + if f64_bits == F64_INF: + return 'Infinity' + elif f64_bits == F64_NEG_INF: + return '-Infinity' + else: + # Use repr to get full precision + return repr(ReinterpretF64(f64_bits)) def UnescapeWasmString(s): - # Wat allows for more escape characters than this, but we assume that - # wasm2wat will only use the \xx escapes. - result = '' - i = 0 - while i < len(s): - c = s[i] - if c == '\\': - x = s[i + 1:i + 3] - if len(x) != 2: - raise Error('String with invalid escape: \"%s\"' % s) - result += chr(int(x, 16)) - i += 3 - else: - result += c - i += 1 - return result + # Wat allows for more escape characters than this, but we assume that + # wasm2wat will only use the \xx escapes. + result = '' + i = 0 + while i < len(s): + c = s[i] + if c == '\\': + x = s[i + 1:i + 3] + if len(x) != 2: + raise Error('String with invalid escape: \"%s\"' % s) + result += chr(int(x, 16)) + i += 3 + else: + result += c + i += 1 + return result def EscapeJSString(s): - result = '' - for c in s: - if 32 <= ord(c) < 127 and c not in '"\\': - result += c - else: - result += '\\x%02x' % ord(c) - return result + result = '' + for c in s: + if 32 <= ord(c) < 127 and c not in '"\\': + result += c + else: + result += '\\x%02x' % ord(c) + return result def IsValidJSConstant(const): - type_ = const['type'] - if type_ == 'i32': - return True - elif type_ == 'i64': - return False - elif type_ == 'f32': - return not IsNaNF32(int(const['value'])) - elif type_ == 'f64': - return not IsNaNF64(int(const['value'])) + type_ = const['type'] + if type_ == 'i32': + return True + elif type_ == 'i64': + return False + elif type_ == 'f32': + return not IsNaNF32(int(const['value'])) + elif type_ == 'f64': + return not IsNaNF64(int(const['value'])) def IsValidJSAction(action): - return all(IsValidJSConstant(x) for x in action.get('args', [])) + return all(IsValidJSConstant(x) for x in action.get('args', [])) def IsValidJSCommand(command): - type_ = command['type'] - action = command['action'] - if type_ == 'assert_return': - expected = command['expected'] - return (IsValidJSAction(action) and - all(IsValidJSConstant(x) for x in expected)) - elif type_ in ('assert_return_canonical_nan', 'assert_return_arithmetic_nan', - 'assert_trap', 'assert_exhaustion'): - return IsValidJSAction(action) + type_ = command['type'] + action = command['action'] + if type_ == 'assert_return': + expected = command['expected'] + return (IsValidJSAction(action) and + all(IsValidJSConstant(x) for x in expected)) + elif type_ in ('assert_return_canonical_nan', 'assert_return_arithmetic_nan', + 'assert_trap', 'assert_exhaustion'): + return IsValidJSAction(action) def CollectInvalidModuleCommands(commands): - modules = [] - module_map = {} - for command in commands: - if command['type'] == 'module': - pair = (command, []) - modules.append(pair) - module_name = command.get('name') - if module_name: - module_map[module_name] = pair - elif command['type'] in ('assert_return', 'assert_return_canonical_nan', - 'assert_return_arithmetic_nan', 'assert_trap', - 'assert_exhaustion'): - if IsValidJSCommand(command): - continue - - action = command['action'] - module_name = action.get('module') - if module_name: - pair = module_map[module_name] - else: - pair = modules[-1] - pair[1].append(command) - return modules + modules = [] + module_map = {} + for command in commands: + if command['type'] == 'module': + pair = (command, []) + modules.append(pair) + module_name = command.get('name') + if module_name: + module_map[module_name] = pair + elif command['type'] in ('assert_return', 'assert_return_canonical_nan', + 'assert_return_arithmetic_nan', 'assert_trap', + 'assert_exhaustion'): + if IsValidJSCommand(command): + continue + + action = command['action'] + module_name = action.get('module') + if module_name: + pair = module_map[module_name] + else: + pair = modules[-1] + pair[1].append(command) + return modules class ModuleExtender(object): - def __init__(self, wat2wasm, wasm2wat, temp_dir): - self.wat2wasm = wat2wasm - self.wasm2wat = wasm2wat - self.temp_dir = temp_dir - self.lines = [] - self.exports = {} - - def Extend(self, wasm_path, commands): - wat_path = self._RunWasm2Wat(wasm_path) - with open(wat_path) as wat_file: - wat = wat_file.read() - - self.lines = [] - self.exports = self._GetExports(wat) - for i, command in enumerate(commands): - self._Command(i, command) - - wat = wat[:wat.rindex(')')] + '\n\n' - wat += '\n'.join(self.lines) + ')' - # print wat - with open(wat_path, 'w') as wat_file: - wat_file.write(wat) - return self._RunWat2Wasm(wat_path) - - def _Command(self, index, command): - command_type = command['type'] - new_field = 'assert_%d' % index - if command_type == 'assert_return': - self.lines.append('(func (export "%s")' % new_field) - self.lines.append('block') - self._Action(command['action']) - for expected in command['expected']: - self._Reinterpret(expected['type']) - self._Constant(expected) - self._Reinterpret(expected['type']) - self._Eq(expected['type']) - self.lines.extend(['i32.eqz', 'br_if 0']) - self.lines.extend(['return', 'end', 'unreachable', ')']) - elif command_type == 'assert_return_canonical_nan': - self._AssertReturnNan(new_field, command, True) - elif command_type == 'assert_return_arithmetic_nan': - self._AssertReturnNan(new_field, command, False) - elif command_type in ('assert_trap', 'assert_exhaustion'): - self.lines.append('(func (export "%s")' % new_field) - self._Action(command['action']) - self.lines.extend(['br 0', ')']) - else: - raise Error('Unexpected command: %s' % command_type) - - # Update command to point to the new exported function. - command['action']['field'] = new_field - command['action']['args'] = [] - command['expected'] = [] - - def _AssertReturnNan(self, new_field, command, canonical): - type_ = command['expected'][0]['type'] - self.lines.append('(func (export "%s")' % new_field) - self.lines.append('block') - self._Action(command['action']) - self._Reinterpret(type_) - self._NanBitmask(canonical, type_) - self._And(type_) - self._QuietNan(type_) - self._Eq(type_) - self.lines.extend( - ['i32.eqz', 'br_if 0', 'return', 'end', 'unreachable', ')']) - - # Change the command to assert_return, it won't return NaN anymore. - command['type'] = 'assert_return' - - def _GetExports(self, wat): - result = {} - pattern = r'^\s*\(export \"(.*?)\"\s*\((\w+) (\d+)' - for name, type_, index in re.findall(pattern, wat, re.MULTILINE): - result[UnescapeWasmString(name)] = (type_, index) - return result - - def _Action(self, action): - export = self.exports[action['field']] - if action['type'] == 'invoke': - for arg in action['args']: - self._Constant(arg) - self.lines.append('call %s' % export[1]) - elif action['type'] == 'get': - self.lines.append('get_global %s' % export[1]) - else: - raise Error('Unexpected action: %s' % action['type']) - - def _Reinterpret(self, type_): - self.lines.extend({ - 'i32': [], - 'i64': [], - 'f32': ['i32.reinterpret/f32'], - 'f64': ['i64.reinterpret/f64'] - }[type_]) - - def _Eq(self, type_): - self.lines.append({ - 'i32': 'i32.eq', - 'i64': 'i64.eq', - 'f32': 'i32.eq', - 'f64': 'i64.eq' - }[type_]) - - def _And(self, type_): - self.lines.append({ - 'i32': 'i32.and', - 'i64': 'i64.and', - 'f32': 'i32.and', - 'f64': 'i64.and' - }[type_]) - - def _NanBitmask(self, canonical, type_): - # When checking for canonical NaNs, the value can differ only in the sign - # bit from +nan. For arithmetic NaNs, the sign bit and the rest of the tag - # can differ as well. - assert(type_ in ('f32', 'f64')) - if not canonical: - return self._QuietNan(type_) - - if type_ == 'f32': - line = 'i32.const 0x7fffffff' - else: - line = 'i64.const 0x7fffffffffffffff' - self.lines.append(line) - - def _QuietNan(self, type_): - assert(type_ in ('f32', 'f64')) - if type_ == 'f32': - line = 'i32.const 0x%x' % F32_QUIET_NAN - else: - line = 'i64.const 0x%x' % F64_QUIET_NAN - self.lines.append(line) - - def _Constant(self, const): - inst = None - type_ = const['type'] - if type_ == 'i32': - inst = 'i32.const %s' % const['value'] - elif type_ == 'i64': - inst = 'i64.const %s' % const['value'] - elif type_ == 'f32': - inst = F32ToWasm(int(const['value'])) - elif type_ == 'f64': - inst = F64ToWasm(int(const['value'])) - self.lines.append(inst) - - def _RunWasm2Wat(self, wasm_path): - wat_path = ChangeDir(ChangeExt(wasm_path, '.wat'), self.temp_dir) - self.wasm2wat.RunWithArgs(wasm_path, '-o', wat_path) - return wat_path - - def _RunWat2Wasm(self, wat_path): - wasm_path = ChangeDir(ChangeExt(wat_path, '.wasm'), self.temp_dir) - self.wat2wasm.RunWithArgs(wat_path, '-o', wasm_path) - return wasm_path + def __init__(self, wat2wasm, wasm2wat, temp_dir): + self.wat2wasm = wat2wasm + self.wasm2wat = wasm2wat + self.temp_dir = temp_dir + self.lines = [] + self.exports = {} + + def Extend(self, wasm_path, commands): + wat_path = self._RunWasm2Wat(wasm_path) + with open(wat_path) as wat_file: + wat = wat_file.read() + + self.lines = [] + self.exports = self._GetExports(wat) + for i, command in enumerate(commands): + self._Command(i, command) + + wat = wat[:wat.rindex(')')] + '\n\n' + wat += '\n'.join(self.lines) + ')' + # print wat + with open(wat_path, 'w') as wat_file: + wat_file.write(wat) + return self._RunWat2Wasm(wat_path) + + def _Command(self, index, command): + command_type = command['type'] + new_field = 'assert_%d' % index + if command_type == 'assert_return': + self.lines.append('(func (export "%s")' % new_field) + self.lines.append('block') + self._Action(command['action']) + for expected in command['expected']: + self._Reinterpret(expected['type']) + self._Constant(expected) + self._Reinterpret(expected['type']) + self._Eq(expected['type']) + self.lines.extend(['i32.eqz', 'br_if 0']) + self.lines.extend(['return', 'end', 'unreachable', ')']) + elif command_type == 'assert_return_canonical_nan': + self._AssertReturnNan(new_field, command, True) + elif command_type == 'assert_return_arithmetic_nan': + self._AssertReturnNan(new_field, command, False) + elif command_type in ('assert_trap', 'assert_exhaustion'): + self.lines.append('(func (export "%s")' % new_field) + self._Action(command['action']) + self.lines.extend(['br 0', ')']) + else: + raise Error('Unexpected command: %s' % command_type) + + # Update command to point to the new exported function. + command['action']['field'] = new_field + command['action']['args'] = [] + command['expected'] = [] + + def _AssertReturnNan(self, new_field, command, canonical): + type_ = command['expected'][0]['type'] + self.lines.append('(func (export "%s")' % new_field) + self.lines.append('block') + self._Action(command['action']) + self._Reinterpret(type_) + self._NanBitmask(canonical, type_) + self._And(type_) + self._QuietNan(type_) + self._Eq(type_) + self.lines.extend( + ['i32.eqz', 'br_if 0', 'return', 'end', 'unreachable', ')']) + + # Change the command to assert_return, it won't return NaN anymore. + command['type'] = 'assert_return' + + def _GetExports(self, wat): + result = {} + pattern = r'^\s*\(export \"(.*?)\"\s*\((\w+) (\d+)' + for name, type_, index in re.findall(pattern, wat, re.MULTILINE): + result[UnescapeWasmString(name)] = (type_, index) + return result + + def _Action(self, action): + export = self.exports[action['field']] + if action['type'] == 'invoke': + for arg in action['args']: + self._Constant(arg) + self.lines.append('call %s' % export[1]) + elif action['type'] == 'get': + self.lines.append('get_global %s' % export[1]) + else: + raise Error('Unexpected action: %s' % action['type']) + + def _Reinterpret(self, type_): + self.lines.extend({ + 'i32': [], + 'i64': [], + 'f32': ['i32.reinterpret/f32'], + 'f64': ['i64.reinterpret/f64'] + }[type_]) + + def _Eq(self, type_): + self.lines.append({ + 'i32': 'i32.eq', + 'i64': 'i64.eq', + 'f32': 'i32.eq', + 'f64': 'i64.eq' + }[type_]) + + def _And(self, type_): + self.lines.append({ + 'i32': 'i32.and', + 'i64': 'i64.and', + 'f32': 'i32.and', + 'f64': 'i64.and' + }[type_]) + + def _NanBitmask(self, canonical, type_): + # When checking for canonical NaNs, the value can differ only in the sign + # bit from +nan. For arithmetic NaNs, the sign bit and the rest of the tag + # can differ as well. + assert(type_ in ('f32', 'f64')) + if not canonical: + return self._QuietNan(type_) + + if type_ == 'f32': + line = 'i32.const 0x7fffffff' + else: + line = 'i64.const 0x7fffffffffffffff' + self.lines.append(line) + + def _QuietNan(self, type_): + assert(type_ in ('f32', 'f64')) + if type_ == 'f32': + line = 'i32.const 0x%x' % F32_QUIET_NAN + else: + line = 'i64.const 0x%x' % F64_QUIET_NAN + self.lines.append(line) + + def _Constant(self, const): + inst = None + type_ = const['type'] + if type_ == 'i32': + inst = 'i32.const %s' % const['value'] + elif type_ == 'i64': + inst = 'i64.const %s' % const['value'] + elif type_ == 'f32': + inst = F32ToWasm(int(const['value'])) + elif type_ == 'f64': + inst = F64ToWasm(int(const['value'])) + self.lines.append(inst) + + def _RunWasm2Wat(self, wasm_path): + wat_path = ChangeDir(ChangeExt(wasm_path, '.wat'), self.temp_dir) + self.wasm2wat.RunWithArgs(wasm_path, '-o', wat_path) + return wat_path + + def _RunWat2Wasm(self, wat_path): + wasm_path = ChangeDir(ChangeExt(wat_path, '.wasm'), self.temp_dir) + self.wat2wasm.RunWithArgs(wat_path, '-o', wasm_path) + return wasm_path class JSWriter(object): - def __init__(self, base_dir, spec_json, out_file): - self.base_dir = base_dir - self.source_filename = os.path.basename(spec_json['source_filename']) - self.commands = spec_json['commands'] - self.out_file = out_file - self.module_idx = 0 - - def Write(self): - for command in self.commands: - self._WriteCommand(command) - - def _WriteFileAndLine(self, command): - self.out_file.write('// %s:%d\n' % (self.source_filename, command['line'])) - - def _WriteCommand(self, command): - command_funcs = { - 'module': self._WriteModuleCommand, - 'action': self._WriteActionCommand, - 'register': self._WriteRegisterCommand, - 'assert_malformed': self._WriteAssertModuleCommand, - 'assert_invalid': self._WriteAssertModuleCommand, - 'assert_unlinkable': self._WriteAssertModuleCommand, - 'assert_uninstantiable': self._WriteAssertModuleCommand, - 'assert_return': self._WriteAssertReturnCommand, - 'assert_return_canonical_nan': self._WriteAssertActionCommand, - 'assert_return_arithmetic_nan': self._WriteAssertActionCommand, - 'assert_trap': self._WriteAssertActionCommand, - 'assert_exhaustion': self._WriteAssertActionCommand, - } - - func = command_funcs.get(command['type']) - if func is None: - raise Error('Unexpected type: %s' % command['type']) - self._WriteFileAndLine(command) - func(command) - self.out_file.write('\n') - - def _ModuleIdxName(self): - return '$%d' % self.module_idx - - def _WriteModuleCommand(self, command): - self.module_idx += 1 - idx_name = self._ModuleIdxName() - - self.out_file.write('let %s = instance("%s");\n' % - (idx_name, self._Module(command['filename']))) - if 'name' in command: - self.out_file.write('let %s = %s;\n' % (command['name'], idx_name)) - - def _WriteActionCommand(self, command): - self.out_file.write('%s;\n' % self._Action(command['action'])) - - def _WriteRegisterCommand(self, command): - self.out_file.write('register("%s", %s)\n' % ( - command['as'], command.get('name', self._ModuleIdxName()))) - - def _WriteAssertModuleCommand(self, command): - # Don't bother writing out text modules; they can't be parsed by JS. - if command['module_type'] == 'binary': - self.out_file.write('%s("%s");\n' % (command['type'], - self._Module(command['filename']))) - - def _WriteAssertReturnCommand(self, command): - expected = command['expected'] - if len(expected) == 1: - self.out_file.write('assert_return(() => %s, %s);\n' % - (self._Action(command['action']), - self._ConstantList(expected))) - elif len(expected) == 0: - self._WriteAssertActionCommand(command) - else: - raise Error('Unexpected result with multiple values: %s' % expected) - - def _WriteAssertActionCommand(self, command): - self.out_file.write('%s(() => %s);\n' % (command['type'], - self._Action(command['action']))) + def __init__(self, base_dir, spec_json, out_file): + self.base_dir = base_dir + self.source_filename = os.path.basename(spec_json['source_filename']) + self.commands = spec_json['commands'] + self.out_file = out_file + self.module_idx = 0 + + def Write(self): + for command in self.commands: + self._WriteCommand(command) + + def _WriteFileAndLine(self, command): + self.out_file.write('// %s:%d\n' % (self.source_filename, command['line'])) + + def _WriteCommand(self, command): + command_funcs = { + 'module': self._WriteModuleCommand, + 'action': self._WriteActionCommand, + 'register': self._WriteRegisterCommand, + 'assert_malformed': self._WriteAssertModuleCommand, + 'assert_invalid': self._WriteAssertModuleCommand, + 'assert_unlinkable': self._WriteAssertModuleCommand, + 'assert_uninstantiable': self._WriteAssertModuleCommand, + 'assert_return': self._WriteAssertReturnCommand, + 'assert_return_canonical_nan': self._WriteAssertActionCommand, + 'assert_return_arithmetic_nan': self._WriteAssertActionCommand, + 'assert_trap': self._WriteAssertActionCommand, + 'assert_exhaustion': self._WriteAssertActionCommand, + } + + func = command_funcs.get(command['type']) + if func is None: + raise Error('Unexpected type: %s' % command['type']) + self._WriteFileAndLine(command) + func(command) + self.out_file.write('\n') + + def _ModuleIdxName(self): + return '$%d' % self.module_idx + + def _WriteModuleCommand(self, command): + self.module_idx += 1 + idx_name = self._ModuleIdxName() + + self.out_file.write('let %s = instance("%s");\n' % + (idx_name, self._Module(command['filename']))) + if 'name' in command: + self.out_file.write('let %s = %s;\n' % (command['name'], idx_name)) + + def _WriteActionCommand(self, command): + self.out_file.write('%s;\n' % self._Action(command['action'])) + + def _WriteRegisterCommand(self, command): + self.out_file.write('register("%s", %s)\n' % ( + command['as'], command.get('name', self._ModuleIdxName()))) + + def _WriteAssertModuleCommand(self, command): + # Don't bother writing out text modules; they can't be parsed by JS. + if command['module_type'] == 'binary': + self.out_file.write('%s("%s");\n' % (command['type'], + self._Module(command['filename']))) + + def _WriteAssertReturnCommand(self, command): + expected = command['expected'] + if len(expected) == 1: + self.out_file.write('assert_return(() => %s, %s);\n' % + (self._Action(command['action']), + self._ConstantList(expected))) + elif len(expected) == 0: + self._WriteAssertActionCommand(command) + else: + raise Error('Unexpected result with multiple values: %s' % expected) + + def _WriteAssertActionCommand(self, command): + self.out_file.write('%s(() => %s);\n' % (command['type'], + self._Action(command['action']))) + + def _Module(self, filename): + with open(os.path.join(self.base_dir, filename), 'rb') as wasm_file: + return ''.join('\\x%02x' % c for c in bytearray(wasm_file.read())) + + def _Constant(self, const): + assert IsValidJSConstant(const), 'Invalid JS const: %s' % const + type_ = const['type'] + value = int(const['value']) + if type_ == 'i32': + return I32ToJS(value) + elif type_ == 'f32': + return F32ToJS(value) + elif type_ == 'f64': + return F64ToJS(value) + else: + assert False + + def _ConstantList(self, consts): + return ', '.join(self._Constant(const) for const in consts) + + def _Action(self, action): + type_ = action['type'] + module = action.get('module', self._ModuleIdxName()) + field = EscapeJSString(action['field']) + if type_ == 'invoke': + args = '[%s]' % self._ConstantList(action.get('args', [])) + return 'call(%s, "%s", %s)' % (module, field, args) + elif type_ == 'get': + return 'get(%s, "%s")' % (module, field) + else: + raise Error('Unexpected action type: %s' % type_) - def _Module(self, filename): - with open(os.path.join(self.base_dir, filename), 'rb') as wasm_file: - return ''.join('\\x%02x' % c for c in bytearray(wasm_file.read())) - def _Constant(self, const): - assert IsValidJSConstant(const), 'Invalid JS const: %s' % const - type_ = const['type'] - value = int(const['value']) - if type_ == 'i32': - return I32ToJS(value) - elif type_ == 'f32': - return F32ToJS(value) - elif type_ == 'f64': - return F64ToJS(value) - else: - assert False - - def _ConstantList(self, consts): - return ', '.join(self._Constant(const) for const in consts) - - def _Action(self, action): - type_ = action['type'] - module = action.get('module', self._ModuleIdxName()) - field = EscapeJSString(action['field']) - if type_ == 'invoke': - args = '[%s]' % self._ConstantList(action.get('args', [])) - return 'call(%s, "%s", %s)' % (module, field, args) - elif type_ == 'get': - return 'get(%s, "%s")' % (module, field) +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument('-o', '--output', metavar='PATH', help='output file.') + parser.add_argument('-P', '--prefix', metavar='PATH', help='prefix file.', + default=os.path.join(SCRIPT_DIR, 'gen-spec-prefix.js')) + parser.add_argument('--bindir', metavar='PATH', + default=find_exe.GetDefaultPath(), + help='directory to search for all executables.') + parser.add_argument('--temp-dir', metavar='PATH', + help='set the directory that temporary wasm/wat' + ' files are written.') + parser.add_argument('--no-error-cmdline', + help='don\'t display the subprocess\'s commandline when' + ' an error occurs', dest='error_cmdline', + action='store_false') + parser.add_argument('-p', '--print-cmd', + help='print the commands that are run.', + action='store_true') + parser.add_argument('file', help='spec json file.') + options = parser.parse_args(args) + + wat2wasm = Executable( + find_exe.GetWat2WasmExecutable(options.bindir), + error_cmdline=options.error_cmdline) + wasm2wat = Executable( + find_exe.GetWasm2WatExecutable(options.bindir), + error_cmdline=options.error_cmdline) + + wat2wasm.verbose = options.print_cmd + wasm2wat.verbose = options.print_cmd + + with open(options.file) as json_file: + json_dir = os.path.dirname(options.file) + spec_json = json.load(json_file) + all_commands = spec_json['commands'] + + # modules is a list of pairs: [(module_command, [assert_command, ...]), ...] + modules = CollectInvalidModuleCommands(all_commands) + + with utils.TempDirectory(options.temp_dir, 'gen-spec-js-') as temp_dir: + extender = ModuleExtender(wat2wasm, wasm2wat, temp_dir) + for module_command, assert_commands in modules: + if assert_commands: + wasm_path = os.path.join(json_dir, module_command['filename']) + new_module_filename = extender.Extend(wasm_path, assert_commands) + module_command['filename'] = os.path.relpath(new_module_filename, + json_dir) + + output = StringIO() + if options.prefix: + with open(options.prefix) as prefix_file: + output.write(prefix_file.read()) + output.write('\n') + + JSWriter(json_dir, spec_json, output).Write() + + if options.output: + out_file = open(options.output, 'w') else: - raise Error('Unexpected action type: %s' % type_) + out_file = sys.stdout + try: + out_file.write(output.getvalue()) + finally: + out_file.close() -def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('-o', '--output', metavar='PATH', help='output file.') - parser.add_argument('-P', '--prefix', metavar='PATH', help='prefix file.', - default=os.path.join(SCRIPT_DIR, 'gen-spec-prefix.js')) - parser.add_argument('--bindir', metavar='PATH', - default=find_exe.GetDefaultPath(), - help='directory to search for all executables.') - parser.add_argument('--temp-dir', metavar='PATH', - help='set the directory that temporary wasm/wat' - ' files are written.') - parser.add_argument('--no-error-cmdline', - help='don\'t display the subprocess\'s commandline when' - ' an error occurs', dest='error_cmdline', - action='store_false') - parser.add_argument('-p', '--print-cmd', - help='print the commands that are run.', - action='store_true') - parser.add_argument('file', help='spec json file.') - options = parser.parse_args(args) - - wat2wasm = Executable( - find_exe.GetWat2WasmExecutable(options.bindir), - error_cmdline=options.error_cmdline) - wasm2wat = Executable( - find_exe.GetWasm2WatExecutable(options.bindir), - error_cmdline=options.error_cmdline) - - wat2wasm.verbose = options.print_cmd - wasm2wat.verbose = options.print_cmd - - with open(options.file) as json_file: - json_dir = os.path.dirname(options.file) - spec_json = json.load(json_file) - all_commands = spec_json['commands'] - - # modules is a list of pairs: [(module_command, [assert_command, ...]), ...] - modules = CollectInvalidModuleCommands(all_commands) - - with utils.TempDirectory(options.temp_dir, 'gen-spec-js-') as temp_dir: - extender = ModuleExtender(wat2wasm, wasm2wat, temp_dir) - for module_command, assert_commands in modules: - if assert_commands: - wasm_path = os.path.join(json_dir, module_command['filename']) - new_module_filename = extender.Extend(wasm_path, assert_commands) - module_command['filename'] = os.path.relpath(new_module_filename, - json_dir) - - output = StringIO() - if options.prefix: - with open(options.prefix) as prefix_file: - output.write(prefix_file.read()) - output.write('\n') - - JSWriter(json_dir, spec_json, output).Write() - - if options.output: - out_file = open(options.output, 'w') - else: - out_file = sys.stdout - - try: - out_file.write(output.getvalue()) - finally: - out_file.close() - - return 0 + return 0 if __name__ == '__main__': - try: - sys.exit(main(sys.argv[1:])) - except Error as e: - sys.stderr.write(str(e) + '\n') - sys.exit(1) + try: + sys.exit(main(sys.argv[1:])) + except Error as e: + sys.stderr.write(str(e) + '\n') + sys.exit(1) diff --git a/test/gen-wasm.py b/test/gen-wasm.py index 4135435e..cef26a8f 100755 --- a/test/gen-wasm.py +++ b/test/gen-wasm.py @@ -31,21 +31,21 @@ PLY_DIR = os.path.join(ROOT_DIR, 'third_party', 'ply') sys.path.append(PLY_DIR) try: - import ply.lex as lex - import ply.yacc as yacc + import ply.lex as lex + import ply.yacc as yacc except ImportError: - raise Error('Unable to import ply. Did you run "git submodule update"?') + raise Error('Unable to import ply. Did you run "git submodule update"?') # ply stuff ################################################################### NAMED_VALUES = { - 'i32': 0x7f, # -1 - 'i64': 0x7e, # -2 - 'f32': 0x7d, # -3 - 'f64': 0x7c, # -4 - 'v128': 0x7b, # -5 - 'anyfunc': 0x70, # -0x10 - 'function': 0x60, # -0x20 - 'void': 0x40, # -0x40 + 'i32': 0x7f, # -1 + 'i64': 0x7e, # -2 + 'f32': 0x7d, # -3 + 'f64': 0x7c, # -4 + 'v128': 0x7b, # -5 + 'anyfunc': 0x70, # -0x10 + 'function': 0x60, # -0x20 + 'void': 0x40, # -0x40 'magic': (0, 0x61, 0x73, 0x6d), 'version': (1, 0, 0, 0), @@ -294,52 +294,52 @@ t_ignore = ' \t' def t_COMMENT(t): - r';;.*' - pass + r';;.*' + pass def t_INT(t): - r'\-?(0[xX][0-9a-fA-F]+|[0-9]+)' - if t.value.lower().startswith('0x'): - t.value = int(t.value, 16) - else: - t.value = int(t.value) + r'\-?(0[xX][0-9a-fA-F]+|[0-9]+)' + if t.value.lower().startswith('0x'): + t.value = int(t.value, 16) + else: + t.value = int(t.value) - if 0 <= t.value < 256: - t.type = 'BYTE' - return t + if 0 <= t.value < 256: + t.type = 'BYTE' + return t def t_FLOAT(t): - r'\-?([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)([eE][0-9]+)?' - t.value = float(t.value) - return t + r'\-?([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)([eE][0-9]+)?' + t.value = float(t.value) + return t def t_STRING(t): - r'\'[^\']*\'|\"[^\"]*\"' - t.value = t.value[1:-1] - return t + r'\'[^\']*\'|\"[^\"]*\"' + t.value = t.value[1:-1] + return t def t_NAME(t): - r'[a-zA-Z][a-zA-Z0-9_\.\/]*' - if t.value in NAMED_VALUES: - t.type = 'NAMED_VALUE' - t.value = NAMED_VALUES[t.value] - elif t.value in keywords: - t.type = keywords[t.value] - return t + r'[a-zA-Z][a-zA-Z0-9_\.\/]*' + if t.value in NAMED_VALUES: + t.type = 'NAMED_VALUE' + t.value = NAMED_VALUES[t.value] + elif t.value in keywords: + t.type = keywords[t.value] + return t def t_newline(t): - r'\n+' - t.lexer.lineno += len(t.value) + r'\n+' + t.lexer.lineno += len(t.value) def t_error(t): - print("Illegal character '%s'" % t.value[0]) - t.lexer.skip(1) + print("Illegal character '%s'" % t.value[0]) + t.lexer.skip(1) lexer = lex.lex() @@ -348,166 +348,166 @@ lexer = lex.lex() def LebLoop(data, v, cond): - while True: - byte = v & 0x7f - v >>= 7 - if cond(v, byte): - data.append(byte) - break - else: - data.append(byte | 0x80) + while True: + byte = v & 0x7f + v >>= 7 + if cond(v, byte): + data.append(byte) + break + else: + data.append(byte | 0x80) def WriteUnsignedLeb(data, v, max_size): - result = [] - LebLoop(result, v, lambda v, byte: v == 0) - assert len(result) <= max_size - data.extend(result) + result = [] + LebLoop(result, v, lambda v, byte: v == 0) + assert len(result) <= max_size + data.extend(result) def WriteLebU32(data, v): - WriteUnsignedLeb(data, v, 5) + WriteUnsignedLeb(data, v, 5) def WriteSignedLeb(data, v, max_size): - result = [] - if v < 0: - LebLoop(result, v, lambda v, byte: v == -1 and byte & 0x40) - else: - LebLoop(result, v, lambda v, byte: v == 0 and not byte & 0x40) - assert len(result) <= max_size - data.extend(result) + result = [] + if v < 0: + LebLoop(result, v, lambda v, byte: v == -1 and byte & 0x40) + else: + LebLoop(result, v, lambda v, byte: v == 0 and not byte & 0x40) + assert len(result) <= max_size + data.extend(result) def WriteLebI32(data, v): - WriteSignedLeb(data, v, 5) + WriteSignedLeb(data, v, 5) def WriteLebI64(data, v): - WriteSignedLeb(data, v, 10) + WriteSignedLeb(data, v, 10) def WriteF32(data, v): - data.extend(ord(b) for b in struct.pack('<f', v)) + data.extend(ord(b) for b in struct.pack('<f', v)) def WriteF64(data, v): - data.extend(ord(b) for b in struct.pack('<d', v)) + data.extend(ord(b) for b in struct.pack('<d', v)) def WriteString(data, s): - data.extend(ord(c) for c in s) + data.extend(ord(c) for c in s) def p_data_byte(p): - 'data : data BYTE' - p[0] = p[1] - p[0].append(p[2]) + 'data : data BYTE' + p[0] = p[1] + p[0].append(p[2]) def p_data_name(p): - '''data : data NAME LBRACKET data RBRACKET + '''data : data NAME LBRACKET data RBRACKET | data FUNC LBRACKET data RBRACKET''' - p[0] = p[1] - # name is only used for documentation - p[0].extend(p[4]) + p[0] = p[1] + # name is only used for documentation + p[0].extend(p[4]) def p_data_named_value(p): - 'data : data NAMED_VALUE' - p[0] = p[1] - if type(p[2]) is tuple: - p[0].extend(p[2]) - else: - p[0].append(p[2]) + 'data : data NAMED_VALUE' + p[0] = p[1] + if type(p[2]) is tuple: + p[0].extend(p[2]) + else: + p[0].append(p[2]) def p_data_section(p): - 'data : data SECTION LPAREN NAMED_VALUE RPAREN LBRACE data RBRACE' - p[0] = p[1] - section_data = p[7] - p[0].append(p[4]) - WriteLebU32(p[0], len(section_data)) - p[0].extend(section_data) + 'data : data SECTION LPAREN NAMED_VALUE RPAREN LBRACE data RBRACE' + p[0] = p[1] + section_data = p[7] + p[0].append(p[4]) + WriteLebU32(p[0], len(section_data)) + p[0].extend(section_data) def p_data_user_section(p): - 'data : data SECTION LPAREN STRING RPAREN LBRACE data RBRACE' - p[0] = p[1] - name = p[4] - section_data = p[7] - p[0].append(0) # 0 is the section code for "user" - section_name_data = [] - WriteLebU32(section_name_data, len(name)) - WriteString(section_name_data, name) - WriteLebU32(p[0], len(section_name_data) + len(section_data)) - p[0].extend(section_name_data) - p[0].extend(section_data) + 'data : data SECTION LPAREN STRING RPAREN LBRACE data RBRACE' + p[0] = p[1] + name = p[4] + section_data = p[7] + p[0].append(0) # 0 is the section code for "user" + section_name_data = [] + WriteLebU32(section_name_data, len(name)) + WriteString(section_name_data, name) + WriteLebU32(p[0], len(section_name_data) + len(section_data)) + p[0].extend(section_name_data) + p[0].extend(section_data) def p_data_func(p): - 'data : data FUNC LBRACE data RBRACE' - p[0] = p[1] - func_data = p[4] - func_data.append(0xb) # end opcode - WriteLebU32(p[0], len(func_data)) - p[0].extend(func_data) + 'data : data FUNC LBRACE data RBRACE' + p[0] = p[1] + func_data = p[4] + func_data.append(0xb) # end opcode + WriteLebU32(p[0], len(func_data)) + p[0].extend(func_data) def p_data_str(p): - 'data : data STR LPAREN STRING RPAREN' - p[0] = p[1] - s = p[4] - WriteLebU32(p[0], len(s)) - WriteString(p[0], s) + 'data : data STR LPAREN STRING RPAREN' + p[0] = p[1] + s = p[4] + WriteLebU32(p[0], len(s)) + WriteString(p[0], s) def p_data_leb_i32(p): - '''data : data LEB_I32 LPAREN INT RPAREN - | data LEB_I32 LPAREN BYTE RPAREN''' - p[0] = p[1] - WriteLebI32(p[0], p[4]) + '''data : data LEB_I32 LPAREN INT RPAREN + | data LEB_I32 LPAREN BYTE RPAREN''' + p[0] = p[1] + WriteLebI32(p[0], p[4]) def p_data_leb_i64(p): - '''data : data LEB_I64 LPAREN INT RPAREN - | data LEB_I64 LPAREN BYTE RPAREN''' - p[0] = p[1] - WriteLebI64(p[0], p[4]) + '''data : data LEB_I64 LPAREN INT RPAREN + | data LEB_I64 LPAREN BYTE RPAREN''' + p[0] = p[1] + WriteLebI64(p[0], p[4]) def p_data_leb_u32(p): - '''data : data LEB_U32 LPAREN INT RPAREN - | data LEB_U32 LPAREN BYTE RPAREN''' - p[0] = p[1] - WriteLebU32(p[0], p[4]) + '''data : data LEB_U32 LPAREN INT RPAREN + | data LEB_U32 LPAREN BYTE RPAREN''' + p[0] = p[1] + WriteLebU32(p[0], p[4]) def p_data_f32(p): - 'data : data F32 LPAREN FLOAT RPAREN' - p[0] = p[1] - WriteF32(p[0], p[4]) + 'data : data F32 LPAREN FLOAT RPAREN' + p[0] = p[1] + WriteF32(p[0], p[4]) def p_data_f64(p): - 'data : data F64 LPAREN FLOAT RPAREN' - p[0] = p[1] - WriteF64(p[0], p[4]) + 'data : data F64 LPAREN FLOAT RPAREN' + p[0] = p[1] + WriteF64(p[0], p[4]) def p_data_string(p): - 'data : data STRING' - p[0] = p[1] - WriteString(p[0], p[2]) + 'data : data STRING' + p[0] = p[1] + WriteString(p[0], p[2]) def p_data_empty(p): - 'data :' - p[0] = [] + 'data :' + p[0] = [] def p_error(p): - raise Error('%d: syntax error, %s' % (p.lineno, p)) + raise Error('%d: syntax error, %s' % (p.lineno, p)) parser = yacc.yacc(debug=False, tabmodule='gen_wasm', @@ -517,35 +517,35 @@ parser = yacc.yacc(debug=False, tabmodule='gen_wasm', def Run(input_file_name): - with open(input_file_name) as input_file: - input_data = input_file.read() - data = parser.parse(input_data) - # convert to bytes - data = bytearray(data) - return data + with open(input_file_name) as input_file: + input_data = input_file.read() + data = parser.parse(input_data) + # convert to bytes + data = bytearray(data) + return data def main(args): - arg_parser = argparse.ArgumentParser() - arg_parser.add_argument('-o', '--output', metavar='PATH', - help='output file.') - arg_parser.add_argument('-v', '--verbose', - help='print more diagnotic messages.', - action='store_true') - arg_parser.add_argument('file', help='input file.') - options = arg_parser.parse_args(args) - data = Run(options.file) - if options.output: - with open(options.output, 'wb') as output_file: - output_file.write(data) - else: - sys.stdout.writelines(Hexdump(data)) - return 0 + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument('-o', '--output', metavar='PATH', + help='output file.') + arg_parser.add_argument('-v', '--verbose', + help='print more diagnotic messages.', + action='store_true') + arg_parser.add_argument('file', help='input file.') + options = arg_parser.parse_args(args) + data = Run(options.file) + if options.output: + with open(options.output, 'wb') as output_file: + output_file.write(data) + else: + sys.stdout.writelines(Hexdump(data)) + return 0 if __name__ == '__main__': - try: - sys.exit(main(sys.argv[1:])) - except Error as e: - sys.stderr.write(str(e) + '\n') - sys.exit(1) + try: + sys.exit(main(sys.argv[1:])) + except Error as e: + sys.stderr.write(str(e) + '\n') + sys.exit(1) diff --git a/test/run-roundtrip.py b/test/run-roundtrip.py index e64febe8..61c01533 100755 --- a/test/run-roundtrip.py +++ b/test/run-roundtrip.py @@ -30,166 +30,167 @@ SKIPPED = 2 def FilesAreEqual(filename1, filename2, verbose=False): - try: - with open(filename1, 'rb') as file1: - data1 = file1.read() - - with open(filename2, 'rb') as file2: - data2 = file2.read() - except OSError as e: - return (ERROR, str(e)) - - if data1 != data2: - msg = 'files differ' - if verbose: - hexdump1 = utils.Hexdump(data1) - hexdump2 = utils.Hexdump(data2) - diff_lines = [] - for line in (difflib.unified_diff(hexdump1, hexdump2, fromfile=filename1, - tofile=filename2)): - diff_lines.append(line) - msg += ''.join(diff_lines) - msg += '\n' - return (ERROR, msg) - return (OK, '') + try: + with open(filename1, 'rb') as file1: + data1 = file1.read() + + with open(filename2, 'rb') as file2: + data2 = file2.read() + except OSError as e: + return (ERROR, str(e)) + + if data1 != data2: + msg = 'files differ' + if verbose: + hexdump1 = utils.Hexdump(data1) + hexdump2 = utils.Hexdump(data2) + diff_lines = [] + for line in difflib.unified_diff(hexdump1, hexdump2, + fromfile=filename1, + tofile=filename2): + diff_lines.append(line) + msg += ''.join(diff_lines) + msg += '\n' + return (ERROR, msg) + return (OK, '') def TwoRoundtrips(wat2wasm, wasm2wat, out_dir, filename, verbose): - basename = os.path.basename(filename) - basename_noext = os.path.splitext(basename)[0] - wasm1_file = os.path.join(out_dir, basename_noext + '-1.wasm') - wast2_file = os.path.join(out_dir, basename_noext + '-2.wast') - wasm3_file = os.path.join(out_dir, basename_noext + '-3.wasm') - try: - wat2wasm.RunWithArgs('-o', wasm1_file, filename) - except Error: - # if the file doesn't parse properly, just skip it (it may be a "bad-*" - # test) - return (SKIPPED, None) - try: - wasm2wat.RunWithArgs('-o', wast2_file, wasm1_file) - wat2wasm.RunWithArgs('-o', wasm3_file, wast2_file) - except Error as e: - return (ERROR, str(e)) - return FilesAreEqual(wasm1_file, wasm3_file, verbose) + basename = os.path.basename(filename) + basename_noext = os.path.splitext(basename)[0] + wasm1_file = os.path.join(out_dir, basename_noext + '-1.wasm') + wast2_file = os.path.join(out_dir, basename_noext + '-2.wast') + wasm3_file = os.path.join(out_dir, basename_noext + '-3.wasm') + try: + wat2wasm.RunWithArgs('-o', wasm1_file, filename) + except Error: + # if the file doesn't parse properly, just skip it (it may be a "bad-*" + # test) + return (SKIPPED, None) + try: + wasm2wat.RunWithArgs('-o', wast2_file, wasm1_file) + wat2wasm.RunWithArgs('-o', wasm3_file, wast2_file) + except Error as e: + return (ERROR, str(e)) + return FilesAreEqual(wasm1_file, wasm3_file, verbose) def OneRoundtripToStdout(wat2wasm, wasm2wat, out_dir, filename, verbose): - basename = os.path.basename(filename) - basename_noext = os.path.splitext(basename)[0] - wasm_file = os.path.join(out_dir, basename_noext + '.wasm') - try: - wat2wasm.RunWithArgs('-o', wasm_file, filename) - except Error: - # if the file doesn't parse properly, just skip it (it may be a "bad-*" - # test) - return (SKIPPED, None) - try: - wasm2wat.RunWithArgs(wasm_file) - except Error as e: - return (ERROR, str(e)) - return (OK, '') + basename = os.path.basename(filename) + basename_noext = os.path.splitext(basename)[0] + wasm_file = os.path.join(out_dir, basename_noext + '.wasm') + try: + wat2wasm.RunWithArgs('-o', wasm_file, filename) + except Error: + # if the file doesn't parse properly, just skip it (it may be a "bad-*" + # test) + return (SKIPPED, None) + try: + wasm2wat.RunWithArgs(wasm_file) + except Error as e: + return (ERROR, str(e)) + return (OK, '') def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('-v', '--verbose', help='print more diagnotic messages.', - action='store_true') - parser.add_argument('-o', '--out-dir', metavar='PATH', - help='output directory for files.') - parser.add_argument('--bindir', metavar='PATH', - default=find_exe.GetDefaultPath(), - help='directory to search for all executables.') - parser.add_argument('--stdout', action='store_true', - help='do one roundtrip and write wast output to stdout') - parser.add_argument('--no-error-cmdline', - help='don\'t display the subprocess\'s commandline when ' - 'an error occurs', dest='error_cmdline', - action='store_false') - parser.add_argument('-p', '--print-cmd', - help='print the commands that are run.', - action='store_true') - parser.add_argument('--no-check', action='store_true') - parser.add_argument('--debug-names', action='store_true') - parser.add_argument('--generate-names', action='store_true') - parser.add_argument('--fold-exprs', action='store_true') - parser.add_argument('--enable-exceptions', action='store_true') - parser.add_argument('--enable-saturating-float-to-int', action='store_true') - parser.add_argument('--enable-threads', action='store_true') - parser.add_argument('--enable-simd', action='store_true') - parser.add_argument('--enable-sign-extension', action='store_true') - parser.add_argument('--enable-multi-value', action='store_true') - parser.add_argument('--enable-bulk-memory', action='store_true') - parser.add_argument('--enable-tail-call', action='store_true') - parser.add_argument('--enable-reference-types', action='store_true') - parser.add_argument('--inline-exports', action='store_true') - parser.add_argument('--inline-imports', action='store_true') - parser.add_argument('file', help='test file.') - options = parser.parse_args(args) - - wat2wasm = utils.Executable( - find_exe.GetWat2WasmExecutable(options.bindir), - error_cmdline=options.error_cmdline) - wat2wasm.AppendOptionalArgs({ - '--debug-names': options.debug_names, - '--enable-exceptions': options.enable_exceptions, - '--enable-multi-value': options.enable_multi_value, - '--enable-saturating-float-to-int': - options.enable_saturating_float_to_int, - '--enable-sign-extension': options.enable_sign_extension, - '--enable-simd': options.enable_simd, - '--enable-threads': options.enable_threads, - '--enable-bulk-memory': options.enable_bulk_memory, - '--enable-tail-call': options.enable_tail_call, - '--enable-reference-types': options.enable_reference_types, - '--no-check': options.no_check, - }) - - wasm2wat = utils.Executable( - find_exe.GetWasm2WatExecutable(options.bindir), - error_cmdline=options.error_cmdline) - wasm2wat.AppendOptionalArgs({ - '--fold-exprs': options.fold_exprs, - '--enable-exceptions': options.enable_exceptions, - '--enable-multi-value': options.enable_multi_value, - '--enable-saturating-float-to-int': - options.enable_saturating_float_to_int, - '--enable-sign-extension': options.enable_sign_extension, - '--enable-simd': options.enable_simd, - '--enable-bulk-memory': options.enable_bulk_memory, - '--enable-tail-call': options.enable_tail_call, - '--enable-reference-types': options.enable_reference_types, - '--enable-threads': options.enable_threads, - '--inline-exports': options.inline_exports, - '--inline-imports': options.inline_imports, - '--no-debug-names': not options.debug_names, - '--generate-names': options.generate_names, - '--no-check': options.no_check, - }) - - wat2wasm.verbose = options.print_cmd - wasm2wat.verbose = options.print_cmd - - filename = options.file - if not os.path.exists(filename): - sys.stderr.write('File not found: %s\n' % filename) - return ERROR - - with utils.TempDirectory(options.out_dir, 'roundtrip-') as out_dir: - if options.stdout: - result, msg = OneRoundtripToStdout(wat2wasm, wasm2wat, out_dir, - filename, options.verbose) - else: - result, msg = TwoRoundtrips(wat2wasm, wasm2wat, out_dir, filename, - options.verbose) - if result == ERROR: - sys.stderr.write(msg) - return result + parser = argparse.ArgumentParser() + parser.add_argument('-v', '--verbose', help='print more diagnotic messages.', + action='store_true') + parser.add_argument('-o', '--out-dir', metavar='PATH', + help='output directory for files.') + parser.add_argument('--bindir', metavar='PATH', + default=find_exe.GetDefaultPath(), + help='directory to search for all executables.') + parser.add_argument('--stdout', action='store_true', + help='do one roundtrip and write wast output to stdout') + parser.add_argument('--no-error-cmdline', + help='don\'t display the subprocess\'s commandline when ' + 'an error occurs', dest='error_cmdline', + action='store_false') + parser.add_argument('-p', '--print-cmd', + help='print the commands that are run.', + action='store_true') + parser.add_argument('--no-check', action='store_true') + parser.add_argument('--debug-names', action='store_true') + parser.add_argument('--generate-names', action='store_true') + parser.add_argument('--fold-exprs', action='store_true') + parser.add_argument('--enable-exceptions', action='store_true') + parser.add_argument('--enable-saturating-float-to-int', action='store_true') + parser.add_argument('--enable-threads', action='store_true') + parser.add_argument('--enable-simd', action='store_true') + parser.add_argument('--enable-sign-extension', action='store_true') + parser.add_argument('--enable-multi-value', action='store_true') + parser.add_argument('--enable-bulk-memory', action='store_true') + parser.add_argument('--enable-tail-call', action='store_true') + parser.add_argument('--enable-reference-types', action='store_true') + parser.add_argument('--inline-exports', action='store_true') + parser.add_argument('--inline-imports', action='store_true') + parser.add_argument('file', help='test file.') + options = parser.parse_args(args) + + wat2wasm = utils.Executable( + find_exe.GetWat2WasmExecutable(options.bindir), + error_cmdline=options.error_cmdline) + wat2wasm.AppendOptionalArgs({ + '--debug-names': options.debug_names, + '--enable-exceptions': options.enable_exceptions, + '--enable-multi-value': options.enable_multi_value, + '--enable-saturating-float-to-int': + options.enable_saturating_float_to_int, + '--enable-sign-extension': options.enable_sign_extension, + '--enable-simd': options.enable_simd, + '--enable-threads': options.enable_threads, + '--enable-bulk-memory': options.enable_bulk_memory, + '--enable-tail-call': options.enable_tail_call, + '--enable-reference-types': options.enable_reference_types, + '--no-check': options.no_check, + }) + + wasm2wat = utils.Executable( + find_exe.GetWasm2WatExecutable(options.bindir), + error_cmdline=options.error_cmdline) + wasm2wat.AppendOptionalArgs({ + '--fold-exprs': options.fold_exprs, + '--enable-exceptions': options.enable_exceptions, + '--enable-multi-value': options.enable_multi_value, + '--enable-saturating-float-to-int': + options.enable_saturating_float_to_int, + '--enable-sign-extension': options.enable_sign_extension, + '--enable-simd': options.enable_simd, + '--enable-bulk-memory': options.enable_bulk_memory, + '--enable-tail-call': options.enable_tail_call, + '--enable-reference-types': options.enable_reference_types, + '--enable-threads': options.enable_threads, + '--inline-exports': options.inline_exports, + '--inline-imports': options.inline_imports, + '--no-debug-names': not options.debug_names, + '--generate-names': options.generate_names, + '--no-check': options.no_check, + }) + + wat2wasm.verbose = options.print_cmd + wasm2wat.verbose = options.print_cmd + + filename = options.file + if not os.path.exists(filename): + sys.stderr.write('File not found: %s\n' % filename) + return ERROR + + with utils.TempDirectory(options.out_dir, 'roundtrip-') as out_dir: + if options.stdout: + result, msg = OneRoundtripToStdout(wat2wasm, wasm2wat, out_dir, + filename, options.verbose) + else: + result, msg = TwoRoundtrips(wat2wasm, wasm2wat, out_dir, filename, + options.verbose) + if result == ERROR: + sys.stderr.write(msg) + return result if __name__ == '__main__': - try: - sys.exit(main(sys.argv[1:])) - except Error as e: - sys.stderr.write(str(e) + '\n') - sys.exit(1) + try: + sys.exit(main(sys.argv[1:])) + except Error as e: + sys.stderr.write(str(e) + '\n') + sys.exit(1) diff --git a/test/run-spec-wasm2c.py b/test/run-spec-wasm2c.py index 2f986f73..7ec17e24 100755 --- a/test/run-spec-wasm2c.py +++ b/test/run-spec-wasm2c.py @@ -18,9 +18,9 @@ from __future__ import print_function import argparse try: - from cStringIO import StringIO + from cStringIO import StringIO except ImportError: - from io import StringIO + from io import StringIO import json import os import re @@ -36,390 +36,390 @@ WASM2C_DIR = os.path.join(find_exe.REPO_ROOT_DIR, 'wasm2c') def ReinterpretF32(f32_bits): - return struct.unpack('<f', struct.pack('<I', f32_bits))[0] + return struct.unpack('<f', struct.pack('<I', f32_bits))[0] def F32ToC(f32_bits): - F32_SIGN_BIT = 0x80000000 - F32_INF = 0x7f800000 - F32_SIG_MASK = 0x7fffff - - if (f32_bits & F32_INF) == F32_INF: - sign = '-' if (f32_bits & F32_SIGN_BIT) == F32_SIGN_BIT else '' - # NaN or infinity - if f32_bits & F32_SIG_MASK: - # NaN - return '%smake_nan_f32(0x%06x)' % (sign, f32_bits & F32_SIG_MASK) + F32_SIGN_BIT = 0x80000000 + F32_INF = 0x7f800000 + F32_SIG_MASK = 0x7fffff + + if (f32_bits & F32_INF) == F32_INF: + sign = '-' if (f32_bits & F32_SIGN_BIT) == F32_SIGN_BIT else '' + # NaN or infinity + if f32_bits & F32_SIG_MASK: + # NaN + return '%smake_nan_f32(0x%06x)' % (sign, f32_bits & F32_SIG_MASK) + else: + return '%sINFINITY' % sign + elif f32_bits == F32_SIGN_BIT: + return '-0.f' else: - return '%sINFINITY' % sign - elif f32_bits == F32_SIGN_BIT: - return '-0.f' - else: - s = '%.9g' % ReinterpretF32(f32_bits) - if '.' not in s: - s += '.' - return s + 'f' + s = '%.9g' % ReinterpretF32(f32_bits) + if '.' not in s: + s += '.' + return s + 'f' def ReinterpretF64(f64_bits): - return struct.unpack('<d', struct.pack('<Q', f64_bits))[0] + return struct.unpack('<d', struct.pack('<Q', f64_bits))[0] def F64ToC(f64_bits): - F64_SIGN_BIT = 0x8000000000000000 - F64_INF = 0x7ff0000000000000 - F64_SIG_MASK = 0xfffffffffffff - - if (f64_bits & F64_INF) == F64_INF: - sign = '-' if (f64_bits & F64_SIGN_BIT) == F64_SIGN_BIT else '' - # NaN or infinity - if f64_bits & F64_SIG_MASK: - # NaN - return '%smake_nan_f64(0x%06x)' % (sign, f64_bits & F64_SIG_MASK) + F64_SIGN_BIT = 0x8000000000000000 + F64_INF = 0x7ff0000000000000 + F64_SIG_MASK = 0xfffffffffffff + + if (f64_bits & F64_INF) == F64_INF: + sign = '-' if (f64_bits & F64_SIGN_BIT) == F64_SIGN_BIT else '' + # NaN or infinity + if f64_bits & F64_SIG_MASK: + # NaN + return '%smake_nan_f64(0x%06x)' % (sign, f64_bits & F64_SIG_MASK) + else: + return '%sINFINITY' % sign + elif f64_bits == F64_SIGN_BIT: + return '-0.0' else: - return '%sINFINITY' % sign - elif f64_bits == F64_SIGN_BIT: - return '-0.0' - else: - return '%.17g' % ReinterpretF64(f64_bits) + return '%.17g' % ReinterpretF64(f64_bits) def MangleType(t): - return {'i32': 'i', 'i64': 'j', 'f32': 'f', 'f64': 'd'}[t] + return {'i32': 'i', 'i64': 'j', 'f32': 'f', 'f64': 'd'}[t] def MangleTypes(types): - if not types: - return 'v' - return ''.join(MangleType(t) for t in types) + if not types: + return 'v' + return ''.join(MangleType(t) for t in types) def MangleName(s): - result = 'Z_' - for c in s.encode('utf-8'): - # NOTE(binji): Z is not allowed. - if c in '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY0123456789': - result += c - else: - result += 'Z%02X' % ord(c) - return result + result = 'Z_' + for c in s.encode('utf-8'): + # NOTE(binji): Z is not allowed. + if c in '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY0123456789': + result += c + else: + result += 'Z%02X' % ord(c) + return result def IsModuleCommand(command): - return (command['type'] == 'module' or - command['type'] == 'assert_uninstantiable') + return (command['type'] == 'module' or + command['type'] == 'assert_uninstantiable') class CWriter(object): - def __init__(self, spec_json, prefix, out_file, out_dir): - self.source_filename = os.path.basename(spec_json['source_filename']) - self.commands = spec_json['commands'] - self.out_file = out_file - self.out_dir = out_dir - self.prefix = prefix - self.module_idx = 0 - self.module_name_to_idx = {} - self.module_prefix_map = {} - - def Write(self): - self._MaybeWriteDummyModule() - self._CacheModulePrefixes() - self._WriteIncludes() - self.out_file.write(self.prefix) - self.out_file.write("\nvoid run_spec_tests(void) {\n\n") - for command in self.commands: - self._WriteCommand(command) - self.out_file.write("\n}\n") - - def GetModuleFilenames(self): - return [c['filename'] for c in self.commands if IsModuleCommand(c)] - - def GetModulePrefix(self, idx_or_name=None): - if idx_or_name is not None: - return self.module_prefix_map[idx_or_name] - return self.module_prefix_map[self.module_idx - 1] - - def _CacheModulePrefixes(self): - idx = 0 - for command in self.commands: - if IsModuleCommand(command): - name = os.path.basename(command['filename']) - name = os.path.splitext(name)[0] - name = re.sub(r'[^a-zA-Z0-9_]', '_', name) - name = MangleName(name) - - self.module_prefix_map[idx] = name - - if 'name' in command: - self.module_name_to_idx[command['name']] = idx - self.module_prefix_map[command['name']] = name - - idx += 1 - elif command['type'] == 'register': - name = MangleName(command['as']) - if 'name' in command: - self.module_prefix_map[command['name']] = name - name_idx = self.module_name_to_idx[command['name']] + def __init__(self, spec_json, prefix, out_file, out_dir): + self.source_filename = os.path.basename(spec_json['source_filename']) + self.commands = spec_json['commands'] + self.out_file = out_file + self.out_dir = out_dir + self.prefix = prefix + self.module_idx = 0 + self.module_name_to_idx = {} + self.module_prefix_map = {} + + def Write(self): + self._MaybeWriteDummyModule() + self._CacheModulePrefixes() + self._WriteIncludes() + self.out_file.write(self.prefix) + self.out_file.write("\nvoid run_spec_tests(void) {\n\n") + for command in self.commands: + self._WriteCommand(command) + self.out_file.write("\n}\n") + + def GetModuleFilenames(self): + return [c['filename'] for c in self.commands if IsModuleCommand(c)] + + def GetModulePrefix(self, idx_or_name=None): + if idx_or_name is not None: + return self.module_prefix_map[idx_or_name] + return self.module_prefix_map[self.module_idx - 1] + + def _CacheModulePrefixes(self): + idx = 0 + for command in self.commands: + if IsModuleCommand(command): + name = os.path.basename(command['filename']) + name = os.path.splitext(name)[0] + name = re.sub(r'[^a-zA-Z0-9_]', '_', name) + name = MangleName(name) + + self.module_prefix_map[idx] = name + + if 'name' in command: + self.module_name_to_idx[command['name']] = idx + self.module_prefix_map[command['name']] = name + + idx += 1 + elif command['type'] == 'register': + name = MangleName(command['as']) + if 'name' in command: + self.module_prefix_map[command['name']] = name + name_idx = self.module_name_to_idx[command['name']] + else: + name_idx = idx - 1 + + self.module_prefix_map[name_idx] = name + + def _MaybeWriteDummyModule(self): + if len(self.GetModuleFilenames()) == 0: + # This test doesn't have any valid modules, so just use a dummy instead. + filename = utils.ChangeExt(self.source_filename, '-dummy.wasm') + with open(os.path.join(self.out_dir, filename), 'wb') as wasm_file: + wasm_file.write(b'\x00\x61\x73\x6d\x01\x00\x00\x00') + + dummy_command = {'type': 'module', 'line': 0, 'filename': filename} + self.commands.insert(0, dummy_command) + + def _WriteFileAndLine(self, command): + self.out_file.write('// %s:%d\n' % (self.source_filename, command['line'])) + + def _WriteIncludes(self): + idx = 0 + for filename in self.GetModuleFilenames(): + header = os.path.splitext(filename)[0] + '.h' + self.out_file.write( + '#define WASM_RT_MODULE_PREFIX %s\n' % self.GetModulePrefix(idx)) + self.out_file.write("#include \"%s\"\n" % header) + self.out_file.write('#undef WASM_RT_MODULE_PREFIX\n\n') + idx += 1 + + def _WriteCommand(self, command): + command_funcs = { + 'module': self._WriteModuleCommand, + 'assert_uninstantiable': self._WriteAssertUninstantiableCommand, + 'action': self._WriteActionCommand, + 'assert_return': self._WriteAssertReturnCommand, + 'assert_return_canonical_nan': self._WriteAssertReturnNanCommand, + 'assert_return_arithmetic_nan': self._WriteAssertReturnNanCommand, + 'assert_trap': self._WriteAssertActionCommand, + 'assert_exhaustion': self._WriteAssertActionCommand, + } + + func = command_funcs.get(command['type']) + if func is not None: + self._WriteFileAndLine(command) + func(command) + self.out_file.write('\n') + + def _WriteModuleCommand(self, command): + self.module_idx += 1 + self.out_file.write('%sinit();\n' % self.GetModulePrefix()) + + def _WriteAssertUninstantiableCommand(self, command): + self.module_idx += 1 + self.out_file.write('ASSERT_TRAP(%sinit());\n' % self.GetModulePrefix()) + + def _WriteActionCommand(self, command): + self.out_file.write('%s;\n' % self._Action(command)) + + def _WriteAssertReturnCommand(self, command): + expected = command['expected'] + if len(expected) == 1: + assert_map = { + 'i32': 'ASSERT_RETURN_I32', + 'f32': 'ASSERT_RETURN_F32', + 'i64': 'ASSERT_RETURN_I64', + 'f64': 'ASSERT_RETURN_F64', + } + + type_ = expected[0]['type'] + assert_macro = assert_map[type_] + self.out_file.write('%s(%s, %s);\n' % + (assert_macro, + self._Action(command), + self._ConstantList(expected))) + elif len(expected) == 0: + self._WriteAssertActionCommand(command) else: - name_idx = idx - 1 - - self.module_prefix_map[name_idx] = name - - def _MaybeWriteDummyModule(self): - if len(self.GetModuleFilenames()) == 0: - # This test doesn't have any valid modules, so just use a dummy instead. - filename = utils.ChangeExt(self.source_filename, '-dummy.wasm') - with open(os.path.join(self.out_dir, filename), 'wb') as wasm_file: - wasm_file.write(b'\x00\x61\x73\x6d\x01\x00\x00\x00') - - dummy_command = {'type': 'module', 'line': 0, 'filename': filename} - self.commands.insert(0, dummy_command) - - def _WriteFileAndLine(self, command): - self.out_file.write('// %s:%d\n' % (self.source_filename, command['line'])) - - def _WriteIncludes(self): - idx = 0 - for filename in self.GetModuleFilenames(): - header = os.path.splitext(filename)[0] + '.h' - self.out_file.write( - '#define WASM_RT_MODULE_PREFIX %s\n' % self.GetModulePrefix(idx)) - self.out_file.write("#include \"%s\"\n" % header) - self.out_file.write('#undef WASM_RT_MODULE_PREFIX\n\n') - idx += 1 - - def _WriteCommand(self, command): - command_funcs = { - 'module': self._WriteModuleCommand, - 'assert_uninstantiable': self._WriteAssertUninstantiableCommand, - 'action': self._WriteActionCommand, - 'assert_return': self._WriteAssertReturnCommand, - 'assert_return_canonical_nan': self._WriteAssertReturnNanCommand, - 'assert_return_arithmetic_nan': self._WriteAssertReturnNanCommand, - 'assert_trap': self._WriteAssertActionCommand, - 'assert_exhaustion': self._WriteAssertActionCommand, - } - - func = command_funcs.get(command['type']) - if func is not None: - self._WriteFileAndLine(command) - func(command) - self.out_file.write('\n') - - def _WriteModuleCommand(self, command): - self.module_idx += 1 - self.out_file.write('%sinit();\n' % self.GetModulePrefix()) - - def _WriteAssertUninstantiableCommand(self, command): - self.module_idx += 1 - self.out_file.write('ASSERT_TRAP(%sinit());\n' % self.GetModulePrefix()) - - def _WriteActionCommand(self, command): - self.out_file.write('%s;\n' % self._Action(command)) - - def _WriteAssertReturnCommand(self, command): - expected = command['expected'] - if len(expected) == 1: - assert_map = { - 'i32': 'ASSERT_RETURN_I32', - 'f32': 'ASSERT_RETURN_F32', - 'i64': 'ASSERT_RETURN_I64', - 'f64': 'ASSERT_RETURN_F64', - } - - type_ = expected[0]['type'] - assert_macro = assert_map[type_] - self.out_file.write('%s(%s, %s);\n' % - (assert_macro, - self._Action(command), - self._ConstantList(expected))) - elif len(expected) == 0: - self._WriteAssertActionCommand(command) - else: - raise Error('Unexpected result with multiple values: %s' % expected) - - def _WriteAssertReturnNanCommand(self, command): - assert_map = { - ('assert_return_canonical_nan', 'f32'): 'ASSERT_RETURN_CANONICAL_NAN_F32', - ('assert_return_canonical_nan', 'f64'): 'ASSERT_RETURN_CANONICAL_NAN_F64', - ('assert_return_arithmetic_nan', 'f32'): 'ASSERT_RETURN_ARITHMETIC_NAN_F32', - ('assert_return_arithmetic_nan', 'f64'): 'ASSERT_RETURN_ARITHMETIC_NAN_F64', - } - - expected = command['expected'] - type_ = expected[0]['type'] - assert_macro = assert_map[(command['type'], type_)] - - self.out_file.write('%s(%s);\n' % (assert_macro, self._Action(command))) - - def _WriteAssertActionCommand(self, command): - assert_map = { - 'assert_exhaustion': 'ASSERT_EXHAUSTION', - 'assert_return': 'ASSERT_RETURN', - 'assert_trap': 'ASSERT_TRAP', - } - - assert_macro = assert_map[command['type']] - self.out_file.write('%s(%s);\n' % (assert_macro, self._Action(command))) - - def _Constant(self, const): - type_ = const['type'] - value = int(const['value']) - if type_ == 'i32': - return '%su' % value - elif type_ == 'i64': - return '%sull' % value - elif type_ == 'f32': - return F32ToC(value) - elif type_ == 'f64': - return F64ToC(value) - else: - assert False - - def _ConstantList(self, consts): - return ', '.join(self._Constant(const) for const in consts) - - def _ActionSig(self, action, expected): - type_ = action['type'] - result_types = [result['type'] for result in expected] - arg_types = [arg['type'] for arg in action.get('args', [])] - if type_ == 'invoke': - return MangleTypes(result_types) + MangleTypes(arg_types) - elif type_ == 'get': - return MangleType(result_types[0]) - else: - raise Error('Unexpected action type: %s' % type_) - - def _Action(self, command): - action = command['action'] - expected = command['expected'] - type_ = action['type'] - mangled_module_name = self.GetModulePrefix(action.get('module')) - field = (mangled_module_name + MangleName(action['field']) + - MangleName(self._ActionSig(action, expected))) - if type_ == 'invoke': - return '%s(%s)' % (field, self._ConstantList(action.get('args', []))) - elif type_ == 'get': - return '*%s' % field - else: - raise Error('Unexpected action type: %s' % type_) + raise Error('Unexpected result with multiple values: %s' % expected) + + def _WriteAssertReturnNanCommand(self, command): + assert_map = { + ('assert_return_canonical_nan', 'f32'): 'ASSERT_RETURN_CANONICAL_NAN_F32', + ('assert_return_canonical_nan', 'f64'): 'ASSERT_RETURN_CANONICAL_NAN_F64', + ('assert_return_arithmetic_nan', 'f32'): 'ASSERT_RETURN_ARITHMETIC_NAN_F32', + ('assert_return_arithmetic_nan', 'f64'): 'ASSERT_RETURN_ARITHMETIC_NAN_F64', + } + + expected = command['expected'] + type_ = expected[0]['type'] + assert_macro = assert_map[(command['type'], type_)] + + self.out_file.write('%s(%s);\n' % (assert_macro, self._Action(command))) + + def _WriteAssertActionCommand(self, command): + assert_map = { + 'assert_exhaustion': 'ASSERT_EXHAUSTION', + 'assert_return': 'ASSERT_RETURN', + 'assert_trap': 'ASSERT_TRAP', + } + + assert_macro = assert_map[command['type']] + self.out_file.write('%s(%s);\n' % (assert_macro, self._Action(command))) + + def _Constant(self, const): + type_ = const['type'] + value = int(const['value']) + if type_ == 'i32': + return '%su' % value + elif type_ == 'i64': + return '%sull' % value + elif type_ == 'f32': + return F32ToC(value) + elif type_ == 'f64': + return F64ToC(value) + else: + assert False + + def _ConstantList(self, consts): + return ', '.join(self._Constant(const) for const in consts) + + def _ActionSig(self, action, expected): + type_ = action['type'] + result_types = [result['type'] for result in expected] + arg_types = [arg['type'] for arg in action.get('args', [])] + if type_ == 'invoke': + return MangleTypes(result_types) + MangleTypes(arg_types) + elif type_ == 'get': + return MangleType(result_types[0]) + else: + raise Error('Unexpected action type: %s' % type_) + + def _Action(self, command): + action = command['action'] + expected = command['expected'] + type_ = action['type'] + mangled_module_name = self.GetModulePrefix(action.get('module')) + field = (mangled_module_name + MangleName(action['field']) + + MangleName(self._ActionSig(action, expected))) + if type_ == 'invoke': + return '%s(%s)' % (field, self._ConstantList(action.get('args', []))) + elif type_ == 'get': + return '*%s' % field + else: + raise Error('Unexpected action type: %s' % type_) def Compile(cc, c_filename, out_dir, *args): - out_dir = os.path.abspath(out_dir) - o_filename = utils.ChangeDir(utils.ChangeExt(c_filename, '.o'), out_dir) - cc.RunWithArgs('-c', '-o', o_filename, c_filename, *args, cwd=out_dir) - return o_filename + out_dir = os.path.abspath(out_dir) + o_filename = utils.ChangeDir(utils.ChangeExt(c_filename, '.o'), out_dir) + cc.RunWithArgs('-c', '-o', o_filename, c_filename, *args, cwd=out_dir) + return o_filename def Link(cc, o_filenames, main_exe, out_dir, *args): - args = ['-o', main_exe] + o_filenames + list(args) - cc.RunWithArgs(*args, cwd=out_dir) + args = ['-o', main_exe] + o_filenames + list(args) + cc.RunWithArgs(*args, cwd=out_dir) def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('-o', '--out-dir', metavar='PATH', - help='output directory for files.') - parser.add_argument('-P', '--prefix', metavar='PATH', help='prefix file.', - default=os.path.join(SCRIPT_DIR, 'spec-wasm2c-prefix.c')) - parser.add_argument('--bindir', metavar='PATH', - default=find_exe.GetDefaultPath(), - help='directory to search for all executables.') - parser.add_argument('--wasmrt-dir', metavar='PATH', - help='directory with wasm-rt files', default=WASM2C_DIR) - parser.add_argument('--cc', metavar='PATH', - help='the path to the C compiler', default='cc') - parser.add_argument('--cflags', metavar='FLAGS', - help='additional flags for C compiler.', - action='append', default=[]) - parser.add_argument('--compile', help='compile the C code (default)', - dest='compile', action='store_true') - parser.add_argument('--no-compile', help='don\'t compile the C code', - dest='compile', action='store_false') - parser.set_defaults(compile=True) - parser.add_argument('--no-run', help='don\'t run the compiled executable', - dest='run', action='store_false') - parser.add_argument('-v', '--verbose', help='print more diagnotic messages.', - action='store_true') - parser.add_argument('--no-error-cmdline', - help='don\'t display the subprocess\'s commandline when ' - 'an error occurs', dest='error_cmdline', - action='store_false') - parser.add_argument('-p', '--print-cmd', - help='print the commands that are run.', - action='store_true') - parser.add_argument('file', help='wast file.') - options = parser.parse_args(args) - - with utils.TempDirectory(options.out_dir, 'run-spec-wasm2c-') as out_dir: - # Parse JSON file and generate main .c file with calls to test functions. - wast2json = utils.Executable( - find_exe.GetWast2JsonExecutable(options.bindir), - error_cmdline=options.error_cmdline) - wast2json.AppendOptionalArgs({'-v': options.verbose}) - - json_file_path = utils.ChangeDir( - utils.ChangeExt(options.file, '.json'), out_dir) - wast2json.RunWithArgs(options.file, '-o', json_file_path) - - wasm2c = utils.Executable( - find_exe.GetWasm2CExecutable(options.bindir), - error_cmdline=options.error_cmdline) - - cc = utils.Executable(options.cc, *options.cflags) - - with open(json_file_path) as json_file: - spec_json = json.load(json_file) - - prefix = '' - if options.prefix: - with open(options.prefix) as prefix_file: - prefix = prefix_file.read() + '\n' - - output = StringIO() - cwriter = CWriter(spec_json, prefix, output, out_dir) - cwriter.Write() - - main_filename = utils.ChangeExt(json_file_path, '-main.c') - with open(main_filename, 'w') as out_main_file: - out_main_file.write(output.getvalue()) - - o_filenames = [] - includes = '-I%s' % options.wasmrt_dir - - # Compile wasm-rt-impl. - wasm_rt_impl_c = os.path.join(options.wasmrt_dir, 'wasm-rt-impl.c') - o_filenames.append(Compile(cc, wasm_rt_impl_c, out_dir, includes)) - - for i, wasm_filename in enumerate(cwriter.GetModuleFilenames()): - c_filename = utils.ChangeExt(wasm_filename, '.c') - wasm2c.RunWithArgs(wasm_filename, '-o', c_filename, cwd=out_dir) - if options.compile: - defines = '-DWASM_RT_MODULE_PREFIX=%s' % cwriter.GetModulePrefix(i) - o_filenames.append(Compile(cc, c_filename, out_dir, includes, defines)) - - if options.compile: - main_c = os.path.basename(main_filename) - o_filenames.append(Compile(cc, main_c, out_dir, includes, defines)) - main_exe = os.path.basename(utils.ChangeExt(json_file_path, '')) - Link(cc, o_filenames, main_exe, out_dir, '-lm') - - if options.compile and options.run: - utils.Executable(os.path.join(out_dir, main_exe), - forward_stdout=True).RunWithArgs() - - return 0 + parser = argparse.ArgumentParser() + parser.add_argument('-o', '--out-dir', metavar='PATH', + help='output directory for files.') + parser.add_argument('-P', '--prefix', metavar='PATH', help='prefix file.', + default=os.path.join(SCRIPT_DIR, 'spec-wasm2c-prefix.c')) + parser.add_argument('--bindir', metavar='PATH', + default=find_exe.GetDefaultPath(), + help='directory to search for all executables.') + parser.add_argument('--wasmrt-dir', metavar='PATH', + help='directory with wasm-rt files', default=WASM2C_DIR) + parser.add_argument('--cc', metavar='PATH', + help='the path to the C compiler', default='cc') + parser.add_argument('--cflags', metavar='FLAGS', + help='additional flags for C compiler.', + action='append', default=[]) + parser.add_argument('--compile', help='compile the C code (default)', + dest='compile', action='store_true') + parser.add_argument('--no-compile', help='don\'t compile the C code', + dest='compile', action='store_false') + parser.set_defaults(compile=True) + parser.add_argument('--no-run', help='don\'t run the compiled executable', + dest='run', action='store_false') + parser.add_argument('-v', '--verbose', help='print more diagnotic messages.', + action='store_true') + parser.add_argument('--no-error-cmdline', + help='don\'t display the subprocess\'s commandline when ' + 'an error occurs', dest='error_cmdline', + action='store_false') + parser.add_argument('-p', '--print-cmd', + help='print the commands that are run.', + action='store_true') + parser.add_argument('file', help='wast file.') + options = parser.parse_args(args) + + with utils.TempDirectory(options.out_dir, 'run-spec-wasm2c-') as out_dir: + # Parse JSON file and generate main .c file with calls to test functions. + wast2json = utils.Executable( + find_exe.GetWast2JsonExecutable(options.bindir), + error_cmdline=options.error_cmdline) + wast2json.AppendOptionalArgs({'-v': options.verbose}) + + json_file_path = utils.ChangeDir( + utils.ChangeExt(options.file, '.json'), out_dir) + wast2json.RunWithArgs(options.file, '-o', json_file_path) + + wasm2c = utils.Executable( + find_exe.GetWasm2CExecutable(options.bindir), + error_cmdline=options.error_cmdline) + + cc = utils.Executable(options.cc, *options.cflags) + + with open(json_file_path) as json_file: + spec_json = json.load(json_file) + + prefix = '' + if options.prefix: + with open(options.prefix) as prefix_file: + prefix = prefix_file.read() + '\n' + + output = StringIO() + cwriter = CWriter(spec_json, prefix, output, out_dir) + cwriter.Write() + + main_filename = utils.ChangeExt(json_file_path, '-main.c') + with open(main_filename, 'w') as out_main_file: + out_main_file.write(output.getvalue()) + + o_filenames = [] + includes = '-I%s' % options.wasmrt_dir + + # Compile wasm-rt-impl. + wasm_rt_impl_c = os.path.join(options.wasmrt_dir, 'wasm-rt-impl.c') + o_filenames.append(Compile(cc, wasm_rt_impl_c, out_dir, includes)) + + for i, wasm_filename in enumerate(cwriter.GetModuleFilenames()): + c_filename = utils.ChangeExt(wasm_filename, '.c') + wasm2c.RunWithArgs(wasm_filename, '-o', c_filename, cwd=out_dir) + if options.compile: + defines = '-DWASM_RT_MODULE_PREFIX=%s' % cwriter.GetModulePrefix(i) + o_filenames.append(Compile(cc, c_filename, out_dir, includes, defines)) + + if options.compile: + main_c = os.path.basename(main_filename) + o_filenames.append(Compile(cc, main_c, out_dir, includes, defines)) + main_exe = os.path.basename(utils.ChangeExt(json_file_path, '')) + Link(cc, o_filenames, main_exe, out_dir, '-lm') + + if options.compile and options.run: + utils.Executable(os.path.join(out_dir, main_exe), + forward_stdout=True).RunWithArgs() + + return 0 if __name__ == '__main__': - try: - sys.exit(main(sys.argv[1:])) - except Error as e: - # TODO(binji): gcc will output unicode quotes in errors since the terminal - # environment allows it, but python2 stderr will always attempt to convert - # to ascii first, which fails. This will replace the invalid characters - # instead, which is ugly, but works. - sys.stderr.write(u'{0}\n'.format(e).encode('ascii', 'replace')) - sys.exit(1) + try: + sys.exit(main(sys.argv[1:])) + except Error as e: + # TODO(binji): gcc will output unicode quotes in errors since the terminal + # environment allows it, but python2 stderr will always attempt to convert + # to ascii first, which fails. This will replace the invalid characters + # instead, which is ugly, but works. + sys.stderr.write(u'{0}\n'.format(e).encode('ascii', 'replace')) + sys.exit(1) diff --git a/test/run-tests.py b/test/run-tests.py index 815ec905..021391c1 100755 --- a/test/run-tests.py +++ b/test/run-tests.py @@ -36,7 +36,7 @@ IS_WINDOWS = sys.platform == 'win32' TEST_DIR = os.path.dirname(os.path.abspath(__file__)) REPO_ROOT_DIR = os.path.dirname(TEST_DIR) OUT_DIR = os.path.join(REPO_ROOT_DIR, 'out') -DEFAULT_TIMEOUT = 10 # seconds +DEFAULT_TIMEOUT = 10 # seconds SLOW_TIMEOUT_MULTIPLIER = 3 # default configurations for tests @@ -74,13 +74,13 @@ TOOLS = { 'run-roundtrip': [ ('RUN', 'test/run-roundtrip.py'), ('ARGS', [ - '%(in_file)s', - '-v', - '--bindir=%(bindir)s', - '--no-error-cmdline', - '-o', - '%(out_dir)s', - ]), + '%(in_file)s', + '-v', + '--bindir=%(bindir)s', + '--no-error-cmdline', + '-o', + '%(out_dir)s', + ]), ('VERBOSE-ARGS', ['--print-cmd', '-v']), ], 'run-interp': [ @@ -131,824 +131,825 @@ TOOLS = { 'run-spec-wasm2c': [ ('RUN', 'test/run-spec-wasm2c.py'), ('ARGS', [ - '%(in_file)s', - '--bindir=%(bindir)s', - '--no-error-cmdline', - '-o', - '%(out_dir)s', - ]), + '%(in_file)s', + '--bindir=%(bindir)s', + '--no-error-cmdline', + '-o', + '%(out_dir)s', + ]), ('VERBOSE-ARGS', ['--print-cmd', '-v']), ] } # TODO(binji): Add Windows support for compiling using run-spec-wasm2c.py if IS_WINDOWS: - TOOLS['run-spec-wasm2c'].append(('SKIP', '')) + TOOLS['run-spec-wasm2c'].append(('SKIP', '')) ROUNDTRIP_TOOLS = ('wat2wasm',) class NoRoundtripError(Error): - pass + pass def Indent(s, spaces): - return ''.join(' ' * spaces + l for l in s.splitlines(1)) + return ''.join(' ' * spaces + l for l in s.splitlines(1)) def DiffLines(expected, actual): - expected_lines = [line for line in expected.splitlines() if line] - actual_lines = [line for line in actual.splitlines() if line] - return list( - difflib.unified_diff(expected_lines, actual_lines, fromfile='expected', - tofile='actual', lineterm='')) + expected_lines = [line for line in expected.splitlines() if line] + actual_lines = [line for line in actual.splitlines() if line] + return list( + difflib.unified_diff(expected_lines, actual_lines, fromfile='expected', + tofile='actual', lineterm='')) class Cell(object): - def __init__(self, value): - self.value = [value] + def __init__(self, value): + self.value = [value] - def Set(self, value): - self.value[0] = value + def Set(self, value): + self.value[0] = value - def Get(self): - return self.value[0] + def Get(self): + return self.value[0] def SplitArgs(value): - if isinstance(value, list): - return value - return shlex.split(value) + if isinstance(value, list): + return value + return shlex.split(value) def FixPythonExecutable(args): - """Given an argument list beginning with a `*.py` file, return one with that - uses sys.executable as arg[0]. - """ - exe, rest = args[0], args[1:] - if os.path.splitext(exe)[1] == '.py': - return [sys.executable, os.path.join(REPO_ROOT_DIR, exe)] + rest - return args + """Given an argument list beginning with a `*.py` file, return one with that + uses sys.executable as arg[0]. + """ + exe, rest = args[0], args[1:] + if os.path.splitext(exe)[1] == '.py': + return [sys.executable, os.path.join(REPO_ROOT_DIR, exe)] + rest + return args class CommandTemplate(object): - def __init__(self, exe): - self.args = SplitArgs(exe) - self.verbose_args = [] - self.expected_returncode = 0 + def __init__(self, exe): + self.args = SplitArgs(exe) + self.verbose_args = [] + self.expected_returncode = 0 - def AppendArgs(self, args): - self.args += SplitArgs(args) + def AppendArgs(self, args): + self.args += SplitArgs(args) - def SetExpectedReturncode(self, returncode): - self.expected_returncode = returncode + def SetExpectedReturncode(self, returncode): + self.expected_returncode = returncode - def AppendVerboseArgs(self, args_list): - for level, level_args in enumerate(args_list): - while level >= len(self.verbose_args): - self.verbose_args.append([]) - self.verbose_args[level] += SplitArgs(level_args) + def AppendVerboseArgs(self, args_list): + for level, level_args in enumerate(args_list): + while level >= len(self.verbose_args): + self.verbose_args.append([]) + self.verbose_args[level] += SplitArgs(level_args) - def _Format(self, cmd, variables): - return [arg % variables for arg in cmd] + def _Format(self, cmd, variables): + return [arg % variables for arg in cmd] - def GetCommand(self, variables, extra_args=None, verbose_level=0): - args = self.args[:] - vl = 0 - while vl < verbose_level and vl < len(self.verbose_args): - args += self.verbose_args[vl] - vl += 1 - if extra_args: - args += extra_args - args = self._Format(args, variables) - return Command(self, FixPythonExecutable(args)) + def GetCommand(self, variables, extra_args=None, verbose_level=0): + args = self.args[:] + vl = 0 + while vl < verbose_level and vl < len(self.verbose_args): + args += self.verbose_args[vl] + vl += 1 + if extra_args: + args += extra_args + args = self._Format(args, variables) + return Command(self, FixPythonExecutable(args)) class Command(object): - def __init__(self, template, args): - self.template = template - self.args = args + def __init__(self, template, args): + self.template = template + self.args = args - def GetExpectedReturncode(self): - return self.template.expected_returncode + def GetExpectedReturncode(self): + return self.template.expected_returncode - def Run(self, cwd, timeout, console_out=False, env=None): - process = None - is_timeout = Cell(False) - - def KillProcess(timeout=True): - if process: - try: - if IS_WINDOWS: - # http://stackoverflow.com/a/10830753: deleting child processes in - # Windows - subprocess.call(['taskkill', '/F', '/T', '/PID', str(process.pid)]) - else: - os.killpg(os.getpgid(process.pid), 15) - except OSError: - pass - is_timeout.Set(timeout) - - try: - start_time = time.time() - kwargs = {} - if not IS_WINDOWS: - kwargs['preexec_fn'] = os.setsid - - # http://stackoverflow.com/a/10012262: subprocess with a timeout - # http://stackoverflow.com/a/22582602: kill subprocess and children - process = subprocess.Popen(self.args, cwd=cwd, env=env, - stdout=None if console_out else subprocess.PIPE, - stderr=None if console_out else subprocess.PIPE, - universal_newlines=True, **kwargs) - timer = threading.Timer(timeout, KillProcess) - try: - timer.start() - stdout, stderr = process.communicate() - finally: - returncode = process.returncode + def Run(self, cwd, timeout, console_out=False, env=None): process = None - timer.cancel() - if is_timeout.Get(): - raise Error('TIMEOUT\nSTDOUT:\n%s\nSTDERR:\n%s\n' % (stdout, stderr)) - duration = time.time() - start_time - except OSError as e: - raise Error(str(e)) - finally: - KillProcess(False) - - return RunResult(self, stdout, stderr, returncode, duration) + is_timeout = Cell(False) + + def KillProcess(timeout=True): + if process: + try: + if IS_WINDOWS: + # http://stackoverflow.com/a/10830753: deleting child processes in + # Windows + subprocess.call(['taskkill', '/F', '/T', '/PID', str(process.pid)]) + else: + os.killpg(os.getpgid(process.pid), 15) + except OSError: + pass + is_timeout.Set(timeout) - def __str__(self): - return ' '.join(self.args) + try: + start_time = time.time() + kwargs = {} + if not IS_WINDOWS: + kwargs['preexec_fn'] = os.setsid + + # http://stackoverflow.com/a/10012262: subprocess with a timeout + # http://stackoverflow.com/a/22582602: kill subprocess and children + process = subprocess.Popen(self.args, cwd=cwd, env=env, + stdout=None if console_out else subprocess.PIPE, + stderr=None if console_out else subprocess.PIPE, + universal_newlines=True, **kwargs) + timer = threading.Timer(timeout, KillProcess) + try: + timer.start() + stdout, stderr = process.communicate() + finally: + returncode = process.returncode + process = None + timer.cancel() + if is_timeout.Get(): + raise Error('TIMEOUT\nSTDOUT:\n%s\nSTDERR:\n%s\n' % (stdout, stderr)) + duration = time.time() - start_time + except OSError as e: + raise Error(str(e)) + finally: + KillProcess(False) + + return RunResult(self, stdout, stderr, returncode, duration) + + def __str__(self): + return ' '.join(self.args) class RunResult(object): - def __init__(self, cmd=None, stdout='', stderr='', returncode=0, duration=0): - self.cmd = cmd - self.stdout = stdout - self.stderr = stderr - self.returncode = returncode - self.duration = duration + def __init__(self, cmd=None, stdout='', stderr='', returncode=0, duration=0): + self.cmd = cmd + self.stdout = stdout + self.stderr = stderr + self.returncode = returncode + self.duration = duration - def GetExpectedReturncode(self): - return self.cmd.GetExpectedReturncode() + def GetExpectedReturncode(self): + return self.cmd.GetExpectedReturncode() - def Failed(self): - return self.returncode != self.GetExpectedReturncode() + def Failed(self): + return self.returncode != self.GetExpectedReturncode() - def __repr__(self): - return 'RunResult(%s, %s, %s, %s, %s)' % ( - self.cmd, self.stdout, self.stderr, self.returncode, self.duration) + def __repr__(self): + return 'RunResult(%s, %s, %s, %s, %s)' % ( + self.cmd, self.stdout, self.stderr, self.returncode, self.duration) class TestResult(object): - def __init__(self): - self.results = [] - self.stdout = '' - self.stderr = '' - self.duration = 0 + def __init__(self): + self.results = [] + self.stdout = '' + self.stderr = '' + self.duration = 0 - def GetLastCommand(self): - return self.results[-1].cmd + def GetLastCommand(self): + return self.results[-1].cmd - def GetLastFailure(self): - return [r for r in self.results if r.Failed()][-1] + def GetLastFailure(self): + return [r for r in self.results if r.Failed()][-1] - def Failed(self): - return any(r.Failed() for r in self.results) + def Failed(self): + return any(r.Failed() for r in self.results) - def Append(self, result): - self.results.append(result) + def Append(self, result): + self.results.append(result) - if result.stdout is not None: - self.stdout += result.stdout + if result.stdout is not None: + self.stdout += result.stdout - if result.stderr is not None: - self.stderr += result.stderr + if result.stderr is not None: + self.stderr += result.stderr - self.duration += result.duration + self.duration += result.duration class TestInfo(object): - def __init__(self): - self.filename = '' - self.header = [] - self.input_filename = '' - self.input_ = '' - self.expected_stdout = '' - self.expected_stderr = '' - self.tool = None - self.cmds = [] - self.env = {} - self.slow = False - self.skip = False - self.is_roundtrip = False - - def CreateRoundtripInfo(self, fold_exprs): - if self.tool not in ROUNDTRIP_TOOLS: - raise NoRoundtripError() - - if len(self.cmds) != 1: - raise NoRoundtripError() - - result = TestInfo() - result.SetTool('run-roundtrip') - result.filename = self.filename - result.header = self.header - result.input_filename = self.input_filename - result.input_ = self.input_ - result.expected_stdout = '' - result.expected_stderr = '' - - # TODO(binji): It's kind of cheesy to keep the enable flag based on prefix. - # Maybe it would be nicer to add a new directive ENABLE instead. - old_cmd = self.cmds[0] - new_cmd = result.cmds[0] - new_cmd.AppendArgs([f for f in old_cmd.args if f.startswith('--enable')]) - if fold_exprs: - new_cmd.AppendArgs('--fold-exprs') - - result.env = self.env - result.slow = self.slow - result.skip = self.skip - result.is_roundtrip = True - result.fold_exprs = fold_exprs - return result - - def GetName(self): - name = self.filename - if self.is_roundtrip: - if self.fold_exprs: - name += ' (roundtrip fold-exprs)' - else: - name += ' (roundtrip)' - return name - - def GetGeneratedInputFilename(self): - # All tests should be generated in their own directories, even if they - # share the same input filename. We want the input filename to be correct, - # though, so we use the directory of the test (.txt) file, but the basename - # of the input file (likely a .wast). - - path = OUT_DIR - if self.input_filename: - basename = os.path.basename(self.input_filename) - else: - basename = os.path.basename(self.filename) + def __init__(self): + self.filename = '' + self.header = [] + self.input_filename = '' + self.input_ = '' + self.expected_stdout = '' + self.expected_stderr = '' + self.tool = None + self.cmds = [] + self.env = {} + self.slow = False + self.skip = False + self.is_roundtrip = False + + def CreateRoundtripInfo(self, fold_exprs): + if self.tool not in ROUNDTRIP_TOOLS: + raise NoRoundtripError() + + if len(self.cmds) != 1: + raise NoRoundtripError() + + result = TestInfo() + result.SetTool('run-roundtrip') + result.filename = self.filename + result.header = self.header + result.input_filename = self.input_filename + result.input_ = self.input_ + result.expected_stdout = '' + result.expected_stderr = '' + + # TODO(binji): It's kind of cheesy to keep the enable flag based on prefix. + # Maybe it would be nicer to add a new directive ENABLE instead. + old_cmd = self.cmds[0] + new_cmd = result.cmds[0] + new_cmd.AppendArgs([f for f in old_cmd.args if f.startswith('--enable')]) + if fold_exprs: + new_cmd.AppendArgs('--fold-exprs') + + result.env = self.env + result.slow = self.slow + result.skip = self.skip + result.is_roundtrip = True + result.fold_exprs = fold_exprs + return result + + def GetName(self): + name = self.filename + if self.is_roundtrip: + if self.fold_exprs: + name += ' (roundtrip fold-exprs)' + else: + name += ' (roundtrip)' + return name + + def GetGeneratedInputFilename(self): + # All tests should be generated in their own directories, even if they + # share the same input filename. We want the input filename to be correct, + # though, so we use the directory of the test (.txt) file, but the basename + # of the input file (likely a .wast). + + path = OUT_DIR + if self.input_filename: + basename = os.path.basename(self.input_filename) + else: + basename = os.path.basename(self.filename) - path = os.path.join(path, os.path.dirname(self.filename), basename) + path = os.path.join(path, os.path.dirname(self.filename), basename) - if self.is_roundtrip: - dirname = os.path.dirname(path) - basename = os.path.basename(path) - if self.fold_exprs: - path = os.path.join(dirname, 'roundtrip_folded', basename) - else: - path = os.path.join(dirname, 'roundtrip', basename) + if self.is_roundtrip: + dirname = os.path.dirname(path) + basename = os.path.basename(path) + if self.fold_exprs: + path = os.path.join(dirname, 'roundtrip_folded', basename) + else: + path = os.path.join(dirname, 'roundtrip', basename) - return path + return path - def SetTool(self, tool): - if tool not in TOOLS: - raise Error('Unknown tool: %s' % tool) - self.tool = tool - for tool_key, tool_value in TOOLS[tool]: - self.ParseDirective(tool_key, tool_value) + def SetTool(self, tool): + if tool not in TOOLS: + raise Error('Unknown tool: %s' % tool) + self.tool = tool + for tool_key, tool_value in TOOLS[tool]: + self.ParseDirective(tool_key, tool_value) - def GetCommand(self, index): - try: - return self.cmds[index] - except IndexError: - raise Error('Invalid command index: %s' % index) - - def GetLastCommand(self): - return self.GetCommand(len(self.cmds) - 1) - - def ApplyToCommandBySuffix(self, suffix, fn): - if suffix == '': - fn(self.GetLastCommand()) - elif re.match(r'^\d+$', suffix): - fn(self.GetCommand(int(suffix))) - elif suffix == '*': - for cmd in self.cmds: - fn(cmd) - else: - raise Error('Invalid directive suffix: %s' % suffix) - - def ParseDirective(self, key, value): - if key == 'RUN': - self.cmds.append(CommandTemplate(value)) - elif key == 'STDIN_FILE': - self.input_filename = value - elif key.startswith('ARGS'): - suffix = key[len('ARGS'):] - self.ApplyToCommandBySuffix(suffix, lambda cmd: cmd.AppendArgs(value)) - elif key.startswith('ERROR'): - suffix = key[len('ERROR'):] - self.ApplyToCommandBySuffix( - suffix, lambda cmd: cmd.SetExpectedReturncode(int(value))) - elif key == 'SLOW': - self.slow = True - elif key == 'SKIP': - self.skip = True - elif key == 'VERBOSE-ARGS': - self.GetLastCommand().AppendVerboseArgs(value) - elif key in ['TODO', 'NOTE']: - pass - elif key == 'TOOL': - self.SetTool(value) - elif key == 'ENV': - # Pattern: FOO=1 BAR=stuff - self.env = dict(x.split('=') for x in value.split()) - else: - raise Error('Unknown directive: %s' % key) - - def Parse(self, filename): - self.filename = filename - - test_path = os.path.join(REPO_ROOT_DIR, filename) - with open(test_path) as f: - state = 'header' - empty = True - header_lines = [] - input_lines = [] - stdout_lines = [] - stderr_lines = [] - for line in f.readlines(): - empty = False - m = re.match(r'\s*\(;; (STDOUT|STDERR) ;;;$', line) - if m: - directive = m.group(1) - if directive == 'STDERR': - state = 'stderr' - continue - elif directive == 'STDOUT': - state = 'stdout' - continue + def GetCommand(self, index): + try: + return self.cmds[index] + except IndexError: + raise Error('Invalid command index: %s' % index) + + def GetLastCommand(self): + return self.GetCommand(len(self.cmds) - 1) + + def ApplyToCommandBySuffix(self, suffix, fn): + if suffix == '': + fn(self.GetLastCommand()) + elif re.match(r'^\d+$', suffix): + fn(self.GetCommand(int(suffix))) + elif suffix == '*': + for cmd in self.cmds: + fn(cmd) else: - m = re.match(r'\s*;;;(.*)$', line) - if m: - directive = m.group(1).strip() - if state == 'header': - key, value = directive.split(':', 1) - key = key.strip() - value = value.strip() - self.ParseDirective(key, value) - elif state in ('stdout', 'stderr'): - if not re.match(r'%s ;;\)$' % state.upper(), directive): - raise Error('Bad directive in %s block: %s' % (state, - directive)) - state = 'none' + raise Error('Invalid directive suffix: %s' % suffix) + + def ParseDirective(self, key, value): + if key == 'RUN': + self.cmds.append(CommandTemplate(value)) + elif key == 'STDIN_FILE': + self.input_filename = value + elif key.startswith('ARGS'): + suffix = key[len('ARGS'):] + self.ApplyToCommandBySuffix(suffix, lambda cmd: cmd.AppendArgs(value)) + elif key.startswith('ERROR'): + suffix = key[len('ERROR'):] + self.ApplyToCommandBySuffix( + suffix, lambda cmd: cmd.SetExpectedReturncode(int(value))) + elif key == 'SLOW': + self.slow = True + elif key == 'SKIP': + self.skip = True + elif key == 'VERBOSE-ARGS': + self.GetLastCommand().AppendVerboseArgs(value) + elif key in ['TODO', 'NOTE']: + pass + elif key == 'TOOL': + self.SetTool(value) + elif key == 'ENV': + # Pattern: FOO=1 BAR=stuff + self.env = dict(x.split('=') for x in value.split()) + else: + raise Error('Unknown directive: %s' % key) + + def Parse(self, filename): + self.filename = filename + + test_path = os.path.join(REPO_ROOT_DIR, filename) + with open(test_path) as f: + state = 'header' + empty = True + header_lines = [] + input_lines = [] + stdout_lines = [] + stderr_lines = [] + for line in f.readlines(): + empty = False + m = re.match(r'\s*\(;; (STDOUT|STDERR) ;;;$', line) + if m: + directive = m.group(1) + if directive == 'STDERR': + state = 'stderr' + continue + elif directive == 'STDOUT': + state = 'stdout' + continue + else: + m = re.match(r'\s*;;;(.*)$', line) + if m: + directive = m.group(1).strip() + if state == 'header': + key, value = directive.split(':', 1) + key = key.strip() + value = value.strip() + self.ParseDirective(key, value) + elif state in ('stdout', 'stderr'): + if not re.match(r'%s ;;\)$' % state.upper(), directive): + raise Error('Bad directive in %s block: %s' % (state, + directive)) + state = 'none' + else: + raise Error('Unexpected directive: %s' % directive) + elif state == 'header': + state = 'input' + + if state == 'header': + header_lines.append(line) + if state == 'input': + if self.input_filename: + raise Error('Can\'t have STDIN_FILE and input') + input_lines.append(line) + elif state == 'stderr': + stderr_lines.append(line) + elif state == 'stdout': + stdout_lines.append(line) + if empty: + raise Error('empty test file') + + self.header = ''.join(header_lines) + self.input_ = ''.join(input_lines) + self.expected_stdout = ''.join(stdout_lines) + self.expected_stderr = ''.join(stderr_lines) + + if not self.cmds: + raise Error('test has no commands') + + def CreateInputFile(self): + gen_input_path = self.GetGeneratedInputFilename() + gen_input_dir = os.path.dirname(gen_input_path) + try: + os.makedirs(gen_input_dir) + except OSError: + if not os.path.isdir(gen_input_dir): + raise + + # Read/write as binary because spec tests may have invalid UTF-8 codes. + with open(gen_input_path, 'wb') as gen_input_file: + if self.input_filename: + input_path = os.path.join(REPO_ROOT_DIR, self.input_filename) + with open(input_path, 'rb') as input_file: + gen_input_file.write(input_file.read()) else: - raise Error('Unexpected directive: %s' % directive) - elif state == 'header': - state = 'input' - - if state == 'header': - header_lines.append(line) - if state == 'input': - if self.input_filename: - raise Error('Can\'t have STDIN_FILE and input') - input_lines.append(line) - elif state == 'stderr': - stderr_lines.append(line) - elif state == 'stdout': - stdout_lines.append(line) - if empty: - raise Error('empty test file') - - self.header = ''.join(header_lines) - self.input_ = ''.join(input_lines) - self.expected_stdout = ''.join(stdout_lines) - self.expected_stderr = ''.join(stderr_lines) - - if not self.cmds: - raise Error('test has no commands') - - def CreateInputFile(self): - gen_input_path = self.GetGeneratedInputFilename() - gen_input_dir = os.path.dirname(gen_input_path) - try: - os.makedirs(gen_input_dir) - except OSError: - if not os.path.isdir(gen_input_dir): - raise - - # Read/write as binary because spec tests may have invalid UTF-8 codes. - with open(gen_input_path, 'wb') as gen_input_file: - if self.input_filename: - input_path = os.path.join(REPO_ROOT_DIR, self.input_filename) - with open(input_path, 'rb') as input_file: - gen_input_file.write(input_file.read()) - else: - # add an empty line for each header line so the line numbers match - gen_input_file.write(('\n' * self.header.count('\n')).encode('ascii')) - gen_input_file.write(self.input_.encode('ascii')) - gen_input_file.flush() - return gen_input_file.name - - def Rebase(self, stdout, stderr): - test_path = os.path.join(REPO_ROOT_DIR, self.filename) - with open(test_path, 'wb') as f: - f.write(self.header) - f.write(self.input_) - if stderr: - f.write('(;; STDERR ;;;\n') - f.write(stderr) - f.write(';;; STDERR ;;)\n') - if stdout: - f.write('(;; STDOUT ;;;\n') - f.write(stdout) - f.write(';;; STDOUT ;;)\n') - - def Diff(self, stdout, stderr): - msg = '' - if self.expected_stderr != stderr: - diff_lines = DiffLines(self.expected_stderr, stderr) - if len(diff_lines) > 0: - msg += 'STDERR MISMATCH:\n' + '\n'.join(diff_lines) + '\n' - - if self.expected_stdout != stdout: - diff_lines = DiffLines(self.expected_stdout, stdout) - if len(diff_lines) > 0: - msg += 'STDOUT MISMATCH:\n' + '\n'.join(diff_lines) + '\n' - - if msg: - raise Error(msg) + # add an empty line for each header line so the line numbers match + gen_input_file.write(('\n' * self.header.count('\n')).encode('ascii')) + gen_input_file.write(self.input_.encode('ascii')) + gen_input_file.flush() + return gen_input_file.name + + def Rebase(self, stdout, stderr): + test_path = os.path.join(REPO_ROOT_DIR, self.filename) + with open(test_path, 'wb') as f: + f.write(self.header) + f.write(self.input_) + if stderr: + f.write('(;; STDERR ;;;\n') + f.write(stderr) + f.write(';;; STDERR ;;)\n') + if stdout: + f.write('(;; STDOUT ;;;\n') + f.write(stdout) + f.write(';;; STDOUT ;;)\n') + + def Diff(self, stdout, stderr): + msg = '' + if self.expected_stderr != stderr: + diff_lines = DiffLines(self.expected_stderr, stderr) + if len(diff_lines) > 0: + msg += 'STDERR MISMATCH:\n' + '\n'.join(diff_lines) + '\n' + + if self.expected_stdout != stdout: + diff_lines = DiffLines(self.expected_stdout, stdout) + if len(diff_lines) > 0: + msg += 'STDOUT MISMATCH:\n' + '\n'.join(diff_lines) + '\n' + + if msg: + raise Error(msg) class Status(object): - def __init__(self, isatty): - self.isatty = isatty - self.start_time = None - self.last_length = 0 - self.last_finished = None - self.skipped = 0 - self.passed = 0 - self.failed = 0 - self.total = 0 - self.failed_tests = [] - - def Start(self, total): - self.total = total - self.start_time = time.time() - - def Passed(self, info, duration): - self.passed += 1 - if self.isatty: - self._Clear() - self._PrintShortStatus(info) - else: - sys.stderr.write('+ %s (%.3fs)\n' % (info.GetName(), duration)) - - def Failed(self, info, error_msg, result=None): - self.failed += 1 - self.failed_tests.append((info, result)) - if self.isatty: - self._Clear() - sys.stderr.write('- %s\n%s\n' % (info.GetName(), Indent(error_msg, 2))) - - def Skipped(self, info): - self.skipped += 1 - if not self.isatty: - sys.stderr.write('. %s (skipped)\n' % info.GetName()) - - def Done(self): - if self.isatty: - sys.stderr.write('\n') - - def _PrintShortStatus(self, info): - assert(self.isatty) - total_duration = time.time() - self.start_time - name = info.GetName() if info else '' - if (self.total - self.skipped): - percent = 100 * (self.passed + self.failed) / (self.total - self.skipped) - else: - percent = 100 - status = '[+%d|-%d|%%%d] (%.2fs) %s' % (self.passed, self.failed, - percent, total_duration, name) - self.last_length = len(status) - self.last_finished = info - sys.stderr.write(status) - sys.stderr.flush() + def __init__(self, isatty): + self.isatty = isatty + self.start_time = None + self.last_length = 0 + self.last_finished = None + self.skipped = 0 + self.passed = 0 + self.failed = 0 + self.total = 0 + self.failed_tests = [] + + def Start(self, total): + self.total = total + self.start_time = time.time() + + def Passed(self, info, duration): + self.passed += 1 + if self.isatty: + self._Clear() + self._PrintShortStatus(info) + else: + sys.stderr.write('+ %s (%.3fs)\n' % (info.GetName(), duration)) + + def Failed(self, info, error_msg, result=None): + self.failed += 1 + self.failed_tests.append((info, result)) + if self.isatty: + self._Clear() + sys.stderr.write('- %s\n%s\n' % (info.GetName(), Indent(error_msg, 2))) + + def Skipped(self, info): + self.skipped += 1 + if not self.isatty: + sys.stderr.write('. %s (skipped)\n' % info.GetName()) + + def Done(self): + if self.isatty: + sys.stderr.write('\n') + + def _PrintShortStatus(self, info): + assert(self.isatty) + total_duration = time.time() - self.start_time + name = info.GetName() if info else '' + if (self.total - self.skipped): + percent = 100 * (self.passed + self.failed) / (self.total - self.skipped) + else: + percent = 100 + status = '[+%d|-%d|%%%d] (%.2fs) %s' % (self.passed, self.failed, + percent, total_duration, name) + self.last_length = len(status) + self.last_finished = info + sys.stderr.write(status) + sys.stderr.flush() - def _Clear(self): - assert(self.isatty) - sys.stderr.write('\r%s\r' % (' ' * self.last_length)) + def _Clear(self): + assert(self.isatty) + sys.stderr.write('\r%s\r' % (' ' * self.last_length)) def FindTestFiles(ext, filter_pattern_re): - tests = [] - for root, dirs, files in os.walk(TEST_DIR): - for f in files: - path = os.path.join(root, f) - if os.path.splitext(f)[1] == ext: - tests.append(os.path.relpath(path, REPO_ROOT_DIR)) - tests.sort() - return [test for test in tests if re.match(filter_pattern_re, test)] + tests = [] + for root, dirs, files in os.walk(TEST_DIR): + for f in files: + path = os.path.join(root, f) + if os.path.splitext(f)[1] == ext: + tests.append(os.path.relpath(path, REPO_ROOT_DIR)) + tests.sort() + return [test for test in tests if re.match(filter_pattern_re, test)] def GetAllTestInfo(test_names, status): - infos = [] - for test_name in test_names: - info = TestInfo() - try: - info.Parse(test_name) - infos.append(info) - except Error as e: - status.Failed(info, str(e)) - return infos + infos = [] + for test_name in test_names: + info = TestInfo() + try: + info.Parse(test_name) + infos.append(info) + except Error as e: + status.Failed(info, str(e)) + return infos def RunTest(info, options, variables, verbose_level=0): - timeout = options.timeout - if info.slow: - timeout *= SLOW_TIMEOUT_MULTIPLIER - - # Clone variables dict so it can be safely modified. - variables = dict(variables) - - cwd = REPO_ROOT_DIR - env = dict(os.environ) - env.update(info.env) - gen_input_path = info.CreateInputFile() - rel_gen_input_path = ( - os.path.relpath(gen_input_path, cwd).replace(os.path.sep, '/')) - variables['in_file'] = rel_gen_input_path - - # Each test runs with a unique output directory which is removed before - # we run the test. - out_dir = os.path.splitext(rel_gen_input_path)[0] - if os.path.isdir(out_dir): - shutil.rmtree(out_dir) - os.makedirs(out_dir) - variables['out_dir'] = out_dir - - # Temporary files typically are placed in `out_dir` and use the same test's - # basename. This name does include an extension. - input_basename = os.path.basename(rel_gen_input_path) - variables['temp_file'] = os.path.join( - out_dir, os.path.splitext(input_basename)[0]) - - test_result = TestResult() - - for cmd_template in info.cmds: - cmd = cmd_template.GetCommand(variables, options.arg, verbose_level) - if options.print_cmd: - print(cmd) + timeout = options.timeout + if info.slow: + timeout *= SLOW_TIMEOUT_MULTIPLIER + + # Clone variables dict so it can be safely modified. + variables = dict(variables) + + cwd = REPO_ROOT_DIR + env = dict(os.environ) + env.update(info.env) + gen_input_path = info.CreateInputFile() + rel_gen_input_path = ( + os.path.relpath(gen_input_path, cwd).replace(os.path.sep, '/')) + variables['in_file'] = rel_gen_input_path + + # Each test runs with a unique output directory which is removed before + # we run the test. + out_dir = os.path.splitext(rel_gen_input_path)[0] + if os.path.isdir(out_dir): + shutil.rmtree(out_dir) + os.makedirs(out_dir) + variables['out_dir'] = out_dir + + # Temporary files typically are placed in `out_dir` and use the same test's + # basename. This name does include an extension. + input_basename = os.path.basename(rel_gen_input_path) + variables['temp_file'] = os.path.join(out_dir, + os.path.splitext(input_basename)[0]) + + test_result = TestResult() + + for cmd_template in info.cmds: + cmd = cmd_template.GetCommand(variables, options.arg, verbose_level) + if options.print_cmd: + print(cmd) - try: - result = cmd.Run(cwd, timeout, verbose_level > 0, env) - except (Error, KeyboardInterrupt) as e: - return e + try: + result = cmd.Run(cwd, timeout, verbose_level > 0, env) + except (Error, KeyboardInterrupt) as e: + return e - test_result.Append(result) - if result.Failed(): - break + test_result.Append(result) + if result.Failed(): + break - return test_result + return test_result def HandleTestResult(status, info, result, rebase=False): - try: - if isinstance(result, (Error, KeyboardInterrupt)): - raise result - - if info.is_roundtrip: - if result.Failed(): - if result.GetLastFailure().returncode == 2: - # run-roundtrip.py returns 2 if the file couldn't be parsed. - # it's likely a "bad-*" file. - status.Skipped(info) - else: - raise Error(result.stderr) - else: - status.Passed(info, result.duration) - else: - if result.Failed(): - # This test has already failed, but diff it anyway. - last_failure = result.GetLastFailure() - msg = 'expected error code %d, got %d.' % ( - last_failure.GetExpectedReturncode(), last_failure.returncode) - try: - info.Diff(result.stdout, result.stderr) - except Error as e: - msg += '\n' + str(e) - raise Error(msg) - else: - if rebase: - info.Rebase(result.stdout, result.stderr) + try: + if isinstance(result, (Error, KeyboardInterrupt)): + raise result + + if info.is_roundtrip: + if result.Failed(): + if result.GetLastFailure().returncode == 2: + # run-roundtrip.py returns 2 if the file couldn't be parsed. + # it's likely a "bad-*" file. + status.Skipped(info) + else: + raise Error(result.stderr) + else: + status.Passed(info, result.duration) else: - info.Diff(result.stdout, result.stderr) - status.Passed(info, result.duration) - except Error as e: - status.Failed(info, str(e), result) + if result.Failed(): + # This test has already failed, but diff it anyway. + last_failure = result.GetLastFailure() + msg = 'expected error code %d, got %d.' % ( + last_failure.GetExpectedReturncode(), + last_failure.returncode) + try: + info.Diff(result.stdout, result.stderr) + except Error as e: + msg += '\n' + str(e) + raise Error(msg) + else: + if rebase: + info.Rebase(result.stdout, result.stderr) + else: + info.Diff(result.stdout, result.stderr) + status.Passed(info, result.duration) + except Error as e: + status.Failed(info, str(e), result) # Source : http://stackoverflow.com/questions/3041986/python-command-line-yes-no-input def YesNoPrompt(question, default='yes'): - """Ask a yes/no question via raw_input() and return their answer. - - "question" is a string that is presented to the user. - "default" is the presumed answer if the user just hits <Enter>. - It must be "yes" (the default), "no" or None (meaning - an answer is required of the user). - - The "answer" return value is True for "yes" or False for "no". - """ - valid = {'yes': True, 'y': True, 'ye': True, 'no': False, 'n': False} - if default is None: - prompt = ' [y/n] ' - elif default == 'yes': - prompt = ' [Y/n] ' - elif default == 'no': - prompt = ' [y/N] ' - else: - raise ValueError('invalid default answer: \'%s\'' % default) - - while True: - sys.stdout.write(question + prompt) - choice = raw_input().lower() - if default is not None and choice == '': - return valid[default] - elif choice in valid: - return valid[choice] + """Ask a yes/no question via raw_input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits <Enter>. + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is True for "yes" or False for "no". + """ + valid = {'yes': True, 'y': True, 'ye': True, 'no': False, 'n': False} + if default is None: + prompt = ' [y/n] ' + elif default == 'yes': + prompt = ' [Y/n] ' + elif default == 'no': + prompt = ' [y/N] ' else: - sys.stdout.write('Please respond with \'yes\' or \'no\' ' - '(or \'y\' or \'n\').\n') + raise ValueError('invalid default answer: \'%s\'' % default) + + while True: + sys.stdout.write(question + prompt) + choice = raw_input().lower() + if default is not None and choice == '': + return valid[default] + elif choice in valid: + return valid[choice] + else: + sys.stdout.write('Please respond with \'yes\' or \'no\' ' + '(or \'y\' or \'n\').\n') def RunMultiThreaded(infos_to_run, status, options, variables): - pool = multiprocessing.Pool(options.jobs) - try: - results = [(info, pool.apply_async(RunTest, (info, options, variables))) - for info in infos_to_run] - while results: - new_results = [] - for info, result in results: - if result.ready(): - HandleTestResult(status, info, result.get(0), options.rebase) - else: - new_results.append((info, result)) - time.sleep(0.01) - results = new_results - pool.close() - finally: - pool.terminate() - pool.join() + pool = multiprocessing.Pool(options.jobs) + try: + results = [(info, pool.apply_async(RunTest, (info, options, variables))) + for info in infos_to_run] + while results: + new_results = [] + for info, result in results: + if result.ready(): + HandleTestResult(status, info, result.get(0), options.rebase) + else: + new_results.append((info, result)) + time.sleep(0.01) + results = new_results + pool.close() + finally: + pool.terminate() + pool.join() def RunSingleThreaded(infos_to_run, status, options, variables): - continued_errors = 0 - - for info in infos_to_run: - result = RunTest(info, options, variables) - HandleTestResult(status, info, result, options.rebase) - if status.failed > continued_errors: - if options.fail_fast: - break - elif options.stop_interactive: - rerun_verbose = YesNoPrompt(question='Rerun with verbose option?', - default='no') - if rerun_verbose: - RunTest(info, options, variables, verbose_level=2) - should_continue = YesNoPrompt(question='Continue testing?', - default='yes') - if not should_continue: - break - elif options.verbose: - RunTest(info, options, variables, verbose_level=1) - continued_errors += 1 + continued_errors = 0 + + for info in infos_to_run: + result = RunTest(info, options, variables) + HandleTestResult(status, info, result, options.rebase) + if status.failed > continued_errors: + if options.fail_fast: + break + elif options.stop_interactive: + rerun_verbose = YesNoPrompt(question='Rerun with verbose option?', + default='no') + if rerun_verbose: + RunTest(info, options, variables, verbose_level=2) + should_continue = YesNoPrompt(question='Continue testing?', + default='yes') + if not should_continue: + break + elif options.verbose: + RunTest(info, options, variables, verbose_level=1) + continued_errors += 1 def GetDefaultJobCount(): - cpu_count = multiprocessing.cpu_count() - if cpu_count <= 1: - return 1 - else: - return cpu_count // 2 + cpu_count = multiprocessing.cpu_count() + if cpu_count <= 1: + return 1 + else: + return cpu_count // 2 def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('-a', '--arg', - help='additional args to pass to executable', - action='append') - parser.add_argument('--bindir', metavar='PATH', - default=find_exe.GetDefaultPath(), - help='directory to search for all executables.') - parser.add_argument('-v', '--verbose', help='print more diagnotic messages.', - action='store_true') - parser.add_argument('-f', '--fail-fast', help='Exit on first failure. ' - 'Extra options with \'--jobs 1\'', action='store_true') - parser.add_argument('--stop-interactive', - help='Enter interactive mode on errors. ' - 'Extra options with \'--jobs 1\'', action='store_true') - parser.add_argument('-l', '--list', help='list all tests.', - action='store_true') - parser.add_argument('-r', '--rebase', - help='rebase a test to its current output.', - action='store_true') - parser.add_argument('-j', '--jobs', - help='number of jobs to use to run tests', type=int, - default=GetDefaultJobCount()) - parser.add_argument('-t', '--timeout', type=float, default=DEFAULT_TIMEOUT, - help='per test timeout in seconds') - parser.add_argument('--no-roundtrip', - help='don\'t run roundtrip.py on all tests', - action='store_false', default=True, dest='roundtrip') - parser.add_argument('-p', '--print-cmd', - help='print the commands that are run.', - action='store_true') - parser.add_argument('patterns', metavar='pattern', nargs='*', - help='test patterns.') - options = parser.parse_args(args) - - if options.jobs != 1: - if options.fail_fast: - parser.error('--fail-fast only works with -j1') - if options.stop_interactive: - parser.error('--stop-interactive only works with -j1') - - if options.patterns: - pattern_re = '|'.join( - fnmatch.translate('*%s*' % p) for p in options.patterns) - else: - pattern_re = '.*' - - test_names = FindTestFiles('.txt', pattern_re) - - if options.list: - for test_name in test_names: - print(test_name) - return 0 - if not test_names: - print('no tests match that filter') - return 1 - - variables = {} - variables['test_dir'] = os.path.abspath(TEST_DIR) - variables['bindir'] = options.bindir - variables['gen_wasm_py'] = find_exe.GEN_WASM_PY - variables['gen_spec_js_py'] = find_exe.GEN_SPEC_JS_PY - for exe_basename in find_exe.EXECUTABLES: - exe_override = os.path.join(options.bindir, exe_basename) - variables[exe_basename] = find_exe.FindExecutable(exe_basename, - exe_override) - - status = Status(sys.stderr.isatty() and not options.verbose) - infos = GetAllTestInfo(test_names, status) - infos_to_run = [] - for info in infos: - if info.skip: - status.Skipped(info) - continue - infos_to_run.append(info) - - if options.roundtrip: - for fold_exprs in False, True: - try: - infos_to_run.append(info.CreateRoundtripInfo(fold_exprs=fold_exprs)) - except NoRoundtripError: - pass + parser = argparse.ArgumentParser() + parser.add_argument('-a', '--arg', + help='additional args to pass to executable', + action='append') + parser.add_argument('--bindir', metavar='PATH', + default=find_exe.GetDefaultPath(), + help='directory to search for all executables.') + parser.add_argument('-v', '--verbose', help='print more diagnotic messages.', + action='store_true') + parser.add_argument('-f', '--fail-fast', help='Exit on first failure. ' + 'Extra options with \'--jobs 1\'', action='store_true') + parser.add_argument('--stop-interactive', + help='Enter interactive mode on errors. ' + 'Extra options with \'--jobs 1\'', action='store_true') + parser.add_argument('-l', '--list', help='list all tests.', + action='store_true') + parser.add_argument('-r', '--rebase', + help='rebase a test to its current output.', + action='store_true') + parser.add_argument('-j', '--jobs', + help='number of jobs to use to run tests', type=int, + default=GetDefaultJobCount()) + parser.add_argument('-t', '--timeout', type=float, default=DEFAULT_TIMEOUT, + help='per test timeout in seconds') + parser.add_argument('--no-roundtrip', + help='don\'t run roundtrip.py on all tests', + action='store_false', default=True, dest='roundtrip') + parser.add_argument('-p', '--print-cmd', + help='print the commands that are run.', + action='store_true') + parser.add_argument('patterns', metavar='pattern', nargs='*', + help='test patterns.') + options = parser.parse_args(args) + + if options.jobs != 1: + if options.fail_fast: + parser.error('--fail-fast only works with -j1') + if options.stop_interactive: + parser.error('--stop-interactive only works with -j1') + + if options.patterns: + pattern_re = '|'.join( + fnmatch.translate('*%s*' % p) for p in options.patterns) + else: + pattern_re = '.*' + + test_names = FindTestFiles('.txt', pattern_re) + + if options.list: + for test_name in test_names: + print(test_name) + return 0 + if not test_names: + print('no tests match that filter') + return 1 + + variables = {} + variables['test_dir'] = os.path.abspath(TEST_DIR) + variables['bindir'] = options.bindir + variables['gen_wasm_py'] = find_exe.GEN_WASM_PY + variables['gen_spec_js_py'] = find_exe.GEN_SPEC_JS_PY + for exe_basename in find_exe.EXECUTABLES: + exe_override = os.path.join(options.bindir, exe_basename) + variables[exe_basename] = find_exe.FindExecutable(exe_basename, + exe_override) + + status = Status(sys.stderr.isatty() and not options.verbose) + infos = GetAllTestInfo(test_names, status) + infos_to_run = [] + for info in infos: + if info.skip: + status.Skipped(info) + continue + infos_to_run.append(info) - if not os.path.exists(OUT_DIR): - os.makedirs(OUT_DIR) + if options.roundtrip: + for fold_exprs in False, True: + try: + infos_to_run.append(info.CreateRoundtripInfo(fold_exprs=fold_exprs)) + except NoRoundtripError: + pass - status.Start(len(infos_to_run)) + if not os.path.exists(OUT_DIR): + os.makedirs(OUT_DIR) - try: - if options.jobs > 1: - RunMultiThreaded(infos_to_run, status, options, variables) - else: - RunSingleThreaded(infos_to_run, status, options, variables) - except KeyboardInterrupt: - print('\nInterrupted testing\n') + status.Start(len(infos_to_run)) - status.Done() + try: + if options.jobs > 1: + RunMultiThreaded(infos_to_run, status, options, variables) + else: + RunSingleThreaded(infos_to_run, status, options, variables) + except KeyboardInterrupt: + print('\nInterrupted testing\n') - ret = 0 - if status.failed: - sys.stderr.write('**** FAILED %s\n' % ('*' * (80 - 14))) - for info, result in status.failed_tests: - last_cmd = result.GetLastCommand() if result is not None else '' - sys.stderr.write('- %s\n %s\n' % (info.GetName(), last_cmd)) - ret = 1 + status.Done() - return ret + ret = 0 + if status.failed: + sys.stderr.write('**** FAILED %s\n' % ('*' * (80 - 14))) + for info, result in status.failed_tests: + last_cmd = result.GetLastCommand() if result is not None else '' + sys.stderr.write('- %s\n %s\n' % (info.GetName(), last_cmd)) + ret = 1 + + return ret if __name__ == '__main__': - try: - sys.exit(main(sys.argv[1:])) - except Error as e: - sys.stderr.write(str(e) + '\n') - sys.exit(1) + try: + sys.exit(main(sys.argv[1:])) + except Error as e: + sys.stderr.write(str(e) + '\n') + sys.exit(1) diff --git a/test/update-spec-tests.py b/test/update-spec-tests.py index fee95842..fda8883b 100755 --- a/test/update-spec-tests.py +++ b/test/update-spec-tests.py @@ -31,70 +31,70 @@ options = None def GetFilesWithExtension(src_dir, want_ext): - result = set() - if os.path.exists(src_dir): - for filename in os.listdir(src_dir): - name, ext = os.path.splitext(filename) - if ext == want_ext: - result.add(name) - return result + result = set() + if os.path.exists(src_dir): + for filename in os.listdir(src_dir): + name, ext = os.path.splitext(filename) + if ext == want_ext: + result.add(name) + return result def ProcessDir(wabt_test_dir, testsuite_dir, tool, flags=None): - testsuite_tests = GetFilesWithExtension(testsuite_dir, '.wast') - wabt_tests = GetFilesWithExtension(wabt_test_dir, '.txt') - - for removed_test_name in wabt_tests - testsuite_tests: - test_filename = os.path.join(wabt_test_dir, removed_test_name + '.txt') - if options.verbose: - print('Removing %s' % test_filename) - os.remove(test_filename) - - for added_test_name in testsuite_tests - wabt_tests: - wast_filename = os.path.join( - os.path.relpath(testsuite_dir, REPO_ROOT_DIR), - added_test_name + '.wast') - test_filename = os.path.join(wabt_test_dir, added_test_name + '.txt') - if options.verbose: - print('Adding %s' % test_filename) - - test_dirname = os.path.dirname(test_filename) - if not os.path.exists(test_dirname): - os.makedirs(test_dirname) - - with open(test_filename, 'w') as f: - f.write(';;; TOOL: %s\n' % tool) - f.write(';;; STDIN_FILE: %s\n' % wast_filename) - if flags: - f.write(';;; ARGS*: %s\n' % flags) + testsuite_tests = GetFilesWithExtension(testsuite_dir, '.wast') + wabt_tests = GetFilesWithExtension(wabt_test_dir, '.txt') + + for removed_test_name in wabt_tests - testsuite_tests: + test_filename = os.path.join(wabt_test_dir, removed_test_name + '.txt') + if options.verbose: + print('Removing %s' % test_filename) + os.remove(test_filename) + + for added_test_name in testsuite_tests - wabt_tests: + wast_filename = os.path.join( + os.path.relpath(testsuite_dir, REPO_ROOT_DIR), + added_test_name + '.wast') + test_filename = os.path.join(wabt_test_dir, added_test_name + '.txt') + if options.verbose: + print('Adding %s' % test_filename) + + test_dirname = os.path.dirname(test_filename) + if not os.path.exists(test_dirname): + os.makedirs(test_dirname) + + with open(test_filename, 'w') as f: + f.write(';;; TOOL: %s\n' % tool) + f.write(';;; STDIN_FILE: %s\n' % wast_filename) + if flags: + f.write(';;; ARGS*: %s\n' % flags) def ProcessProposalDir(name, flags=None): - ProcessDir(os.path.join(SPEC_TEST_DIR, name), - os.path.join(TESTSUITE_DIR, 'proposals', name), - 'run-interp-spec', - flags) + ProcessDir(os.path.join(SPEC_TEST_DIR, name), + os.path.join(TESTSUITE_DIR, 'proposals', name), + 'run-interp-spec', + flags) def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('-v', '--verbose', help='print more diagnotic messages.', - action='store_true') - global options - options = parser.parse_args(args) + parser = argparse.ArgumentParser() + parser.add_argument('-v', '--verbose', help='print more diagnotic messages.', + action='store_true') + global options + options = parser.parse_args(args) - ProcessDir(SPEC_TEST_DIR, TESTSUITE_DIR, 'run-interp-spec') - ProcessDir(WASM2C_SPEC_TEST_DIR, TESTSUITE_DIR, 'run-spec-wasm2c') + ProcessDir(SPEC_TEST_DIR, TESTSUITE_DIR, 'run-interp-spec') + ProcessDir(WASM2C_SPEC_TEST_DIR, TESTSUITE_DIR, 'run-spec-wasm2c') - ProcessProposalDir('multi-value', '--enable-multi-value') - ProcessProposalDir('mutable-global') # Already enabled by default. - ProcessProposalDir('nontrapping-float-to-int-conversions', - '--enable-saturating-float-to-int') - ProcessProposalDir('sign-extension-ops', '--enable-sign-extension') - ProcessProposalDir('bulk-memory-operations', '--enable-bulk-memory') + ProcessProposalDir('multi-value', '--enable-multi-value') + ProcessProposalDir('mutable-global') # Already enabled by default. + ProcessProposalDir('nontrapping-float-to-int-conversions', + '--enable-saturating-float-to-int') + ProcessProposalDir('sign-extension-ops', '--enable-sign-extension') + ProcessProposalDir('bulk-memory-operations', '--enable-bulk-memory') - return 0 + return 0 if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + sys.exit(main(sys.argv[1:])) diff --git a/test/utils.py b/test/utils.py index 00ece562..bdb36e6e 100644 --- a/test/utils.py +++ b/test/utils.py @@ -32,154 +32,154 @@ SIGNAMES = dict((k, v) for v, k in reversed(sorted(signal.__dict__.items())) class Error(Exception): - pass + pass class Executable(object): - def __init__(self, exe, *before_args, **kwargs): - self.exe = exe - self.before_args = list(before_args) - self.after_args = [] - self.basename = kwargs.get('basename', - os.path.basename(exe)).replace('.exe', '') - self.error_cmdline = kwargs.get('error_cmdline', True) - self.clean_stdout = kwargs.get('clean_stdout') - self.clean_stderr = kwargs.get('clean_stderr') - self.stdout_handle = self._ForwardHandle(kwargs.get('forward_stdout')) - self.stderr_handle = self._ForwardHandle(kwargs.get('forward_stderr')) - self.verbose = False - - def _ForwardHandle(self, forward): - return None if forward else subprocess.PIPE - - def _RunWithArgsInternal(self, *args, **kwargs): - cmd = [self.exe] + self.before_args + list(args) + self.after_args - cmd_str = ' '.join(cmd) - if self.verbose: - print(cmd_str) - - if self.error_cmdline: - err_cmd_str = cmd_str.replace('.exe', '') - else: - err_cmd_str = self.basename - - stdout = '' - stderr = '' - error = None - try: - process = subprocess.Popen(cmd, stdout=self.stdout_handle, - stderr=self.stderr_handle, **kwargs) - stdout, stderr = process.communicate() - if stdout: - stdout = stdout.decode('utf-8', 'ignore') - if stderr: - stderr = stderr.decode('utf-8', 'ignore') - if self.clean_stdout: - stdout = self.clean_stdout(stdout) - if self.clean_stderr: - stderr = self.clean_stderr(stderr) - if process.returncode < 0: - # Terminated by signal - signame = SIGNAMES.get(-process.returncode, '<unknown>') - error = Error('Signal raised running "%s": %s\n%s' % (err_cmd_str, - signame, stderr)) - elif process.returncode > 0: - error = Error('Error running "%s":\n%s' % (err_cmd_str, stderr)) - except OSError as e: - error = Error('Error running "%s": %s' % (err_cmd_str, str(e))) - return stdout, stderr, error - - def RunWithArgsForStdout(self, *args, **kwargs): - stdout, stderr, error = self._RunWithArgsInternal(*args, **kwargs) - if error: - raise error - return stdout - - def RunWithArgs(self, *args, **kwargs): - stdout, stderr, error = self._RunWithArgsInternal(*args, **kwargs) - if stdout: - sys.stdout.write(stdout) - if error: - raise error - - def AppendArg(self, arg): - self.after_args.append(arg) - - def AppendOptionalArgs(self, option_dict): - for option, value in option_dict.items(): - if value: - if value is True: - self.AppendArg(option) + def __init__(self, exe, *before_args, **kwargs): + self.exe = exe + self.before_args = list(before_args) + self.after_args = [] + self.basename = kwargs.get('basename', + os.path.basename(exe)).replace('.exe', '') + self.error_cmdline = kwargs.get('error_cmdline', True) + self.clean_stdout = kwargs.get('clean_stdout') + self.clean_stderr = kwargs.get('clean_stderr') + self.stdout_handle = self._ForwardHandle(kwargs.get('forward_stdout')) + self.stderr_handle = self._ForwardHandle(kwargs.get('forward_stderr')) + self.verbose = False + + def _ForwardHandle(self, forward): + return None if forward else subprocess.PIPE + + def _RunWithArgsInternal(self, *args, **kwargs): + cmd = [self.exe] + self.before_args + list(args) + self.after_args + cmd_str = ' '.join(cmd) + if self.verbose: + print(cmd_str) + + if self.error_cmdline: + err_cmd_str = cmd_str.replace('.exe', '') else: - self.AppendArg('%s=%s' % (option, value)) + err_cmd_str = self.basename + + stdout = '' + stderr = '' + error = None + try: + process = subprocess.Popen(cmd, stdout=self.stdout_handle, + stderr=self.stderr_handle, **kwargs) + stdout, stderr = process.communicate() + if stdout: + stdout = stdout.decode('utf-8', 'ignore') + if stderr: + stderr = stderr.decode('utf-8', 'ignore') + if self.clean_stdout: + stdout = self.clean_stdout(stdout) + if self.clean_stderr: + stderr = self.clean_stderr(stderr) + if process.returncode < 0: + # Terminated by signal + signame = SIGNAMES.get(-process.returncode, '<unknown>') + error = Error('Signal raised running "%s": %s\n%s' % (err_cmd_str, + signame, stderr)) + elif process.returncode > 0: + error = Error('Error running "%s":\n%s' % (err_cmd_str, stderr)) + except OSError as e: + error = Error('Error running "%s": %s' % (err_cmd_str, str(e))) + return stdout, stderr, error + + def RunWithArgsForStdout(self, *args, **kwargs): + stdout, stderr, error = self._RunWithArgsInternal(*args, **kwargs) + if error: + raise error + return stdout + + def RunWithArgs(self, *args, **kwargs): + stdout, stderr, error = self._RunWithArgsInternal(*args, **kwargs) + if stdout: + sys.stdout.write(stdout) + if error: + raise error + + def AppendArg(self, arg): + self.after_args.append(arg) + + def AppendOptionalArgs(self, option_dict): + for option, value in option_dict.items(): + if value: + if value is True: + self.AppendArg(option) + else: + self.AppendArg('%s=%s' % (option, value)) @contextlib.contextmanager def TempDirectory(out_dir, prefix=None): - if out_dir: - out_dir_is_temp = False - if not os.path.exists(out_dir): - os.makedirs(out_dir) - else: - out_dir = tempfile.mkdtemp(prefix=prefix) - out_dir_is_temp = True + if out_dir: + out_dir_is_temp = False + if not os.path.exists(out_dir): + os.makedirs(out_dir) + else: + out_dir = tempfile.mkdtemp(prefix=prefix) + out_dir_is_temp = True - try: - yield out_dir - finally: - if out_dir_is_temp: - shutil.rmtree(out_dir) + try: + yield out_dir + finally: + if out_dir_is_temp: + shutil.rmtree(out_dir) def ChangeExt(path, new_ext): - return os.path.splitext(path)[0] + new_ext + return os.path.splitext(path)[0] + new_ext def ChangeDir(path, new_dir): - return os.path.join(new_dir, os.path.basename(path)) + return os.path.join(new_dir, os.path.basename(path)) def Hexdump(data): - if type(data) is str: - data = bytearray(data, 'ascii') - - DUMP_OCTETS_PER_LINE = 16 - DUMP_OCTETS_PER_GROUP = 2 - - p = 0 - end = len(data) - lines = [] - while p < end: - line_start = p - line_end = p + DUMP_OCTETS_PER_LINE - line = '%07x: ' % p - while p < line_end: - for i in xrange(DUMP_OCTETS_PER_GROUP): - if p < end: - line += '%02x' % data[p] - else: - line += ' ' - p += 1 - line += ' ' - line += ' ' - p = line_start - for i in xrange(DUMP_OCTETS_PER_LINE): - if p >= end: - break - x = data[p] - if x >= 32 and x < 0x7f: - line += '%c' % x - else: - line += '.' - p += 1 - line += '\n' - lines.append(line) - - return lines + if type(data) is str: + data = bytearray(data, 'ascii') + + DUMP_OCTETS_PER_LINE = 16 + DUMP_OCTETS_PER_GROUP = 2 + + p = 0 + end = len(data) + lines = [] + while p < end: + line_start = p + line_end = p + DUMP_OCTETS_PER_LINE + line = '%07x: ' % p + while p < line_end: + for i in xrange(DUMP_OCTETS_PER_GROUP): + if p < end: + line += '%02x' % data[p] + else: + line += ' ' + p += 1 + line += ' ' + line += ' ' + p = line_start + for i in xrange(DUMP_OCTETS_PER_LINE): + if p >= end: + break + x = data[p] + if x >= 32 and x < 0x7f: + line += '%c' % x + else: + line += '.' + p += 1 + line += '\n' + lines.append(line) + + return lines def GetModuleFilenamesFromSpecJSON(json_filename): - with open(json_filename) as json_file: - json_data = json.load(json_file) - return [m['filename'] for m in json_data['commands'] if 'filename' in m] + with open(json_filename) as json_file: + json_data = json.load(json_file) + return [m['filename'] for m in json_data['commands'] if 'filename' in m] |