summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.flake85
-rwxr-xr-xscripts/gen-emscripten-exported-json.py126
-rwxr-xr-xscripts/sha256sum.py22
-rwxr-xr-xsrc/wasm2c_tmpl.py86
-rw-r--r--test/find_exe.py64
-rwxr-xr-xtest/gen-spec-js.py902
-rwxr-xr-xtest/gen-wasm.py308
-rwxr-xr-xtest/run-roundtrip.py303
-rwxr-xr-xtest/run-spec-wasm2c.py698
-rwxr-xr-xtest/run-tests.py1415
-rwxr-xr-xtest/update-spec-tests.py104
-rw-r--r--test/utils.py258
12 files changed, 2145 insertions, 2146 deletions
diff --git a/.flake8 b/.flake8
index 2f8c669f..76b60063 100644
--- a/.flake8
+++ b/.flake8
@@ -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]