summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/binary-writer-spec.c41
-rw-r--r--src/tools/wasm-interp.c46
-rw-r--r--test/gen-spec-empty-prefix.js1
-rwxr-xr-xtest/gen-spec-js.py606
-rw-r--r--test/gen-spec-js/action.txt17
-rw-r--r--test/gen-spec-js/assert_malformed.txt10
-rw-r--r--test/gen-spec-js/assert_return.txt48
-rw-r--r--test/gen-spec-js/assert_return_nan.txt16
-rw-r--r--test/gen-spec-js/assert_trap.txt18
-rw-r--r--test/gen-spec-js/assert_uninstantiable.txt12
-rw-r--r--test/gen-spec-js/assert_unlinkable.txt10
-rw-r--r--test/gen-spec-js/basic.txt12
-rw-r--r--test/gen-spec-js/many-modules.txt21
-rw-r--r--test/gen-spec-js/register.txt20
-rw-r--r--test/gen-spec-prefix.js93
-rwxr-xr-xtest/run-gen-spec-js.py99
-rwxr-xr-xtest/run-tests.py18
-rw-r--r--test/utils.py5
18 files changed, 934 insertions, 159 deletions
diff --git a/src/binary-writer-spec.c b/src/binary-writer-spec.c
index 35d52ab1..3a48ccf7 100644
--- a/src/binary-writer-spec.c
+++ b/src/binary-writer-spec.c
@@ -162,6 +162,13 @@ static void write_var(Context* ctx, const WasmVar* var) {
write_escaped_string_slice(ctx, var->name);
}
+static void write_type_object(Context* ctx, WasmType type) {
+ wasm_writef(&ctx->json_stream, "{");
+ write_key(ctx, "type");
+ write_string(ctx, wasm_get_type_name(type));
+ wasm_writef(&ctx->json_stream, "}");
+}
+
static void write_const(Context* ctx, const WasmConst* const_) {
wasm_writef(&ctx->json_stream, "{");
write_key(ctx, "type");
@@ -249,6 +256,36 @@ static void write_action(Context* ctx, const WasmAction* action) {
wasm_writef(&ctx->json_stream, "}");
}
+static void write_action_result_type(Context* ctx,
+ WasmScript* script,
+ const WasmAction* action) {
+ const WasmModule* module =
+ wasm_get_module_by_var(script, &action->module_var);
+ const WasmExport* export;
+ wasm_writef(&ctx->json_stream, "[");
+ switch (action->type) {
+ case WASM_ACTION_TYPE_INVOKE: {
+ export = wasm_get_export_by_name(module, &action->invoke.name);
+ assert(export->kind == WASM_EXTERNAL_KIND_FUNC);
+ WasmFunc* func = wasm_get_func_by_var(module, &export->var);
+ size_t num_results = wasm_get_num_results(func);
+ size_t i;
+ for (i = 0; i < num_results; ++i)
+ write_type_object(ctx, wasm_get_result_type(func, i));
+ break;
+ }
+
+ case WASM_ACTION_TYPE_GET: {
+ export = wasm_get_export_by_name(module, &action->get.name);
+ assert(export->kind == WASM_EXTERNAL_KIND_GLOBAL);
+ WasmGlobal* global = wasm_get_global_by_var(module, &export->var);
+ write_type_object(ctx, global->type);
+ break;
+ }
+ }
+ wasm_writef(&ctx->json_stream, "]");
+}
+
static void write_module(Context* ctx,
char* filename,
const WasmModule* module) {
@@ -398,6 +435,10 @@ static void write_commands(Context* ctx, WasmScript* script) {
write_location(ctx, &command->assert_return_nan.action.loc);
write_separator(ctx);
write_action(ctx, &command->assert_return_nan.action);
+ write_separator(ctx);
+ write_key(ctx, "expected");
+ write_action_result_type(ctx, script,
+ &command->assert_return_nan.action);
break;
case WASM_COMMAND_TYPE_ASSERT_TRAP:
diff --git a/src/tools/wasm-interp.c b/src/tools/wasm-interp.c
index 9956c614..2180762e 100644
--- a/src/tools/wasm-interp.c
+++ b/src/tools/wasm-interp.c
@@ -909,6 +909,46 @@ static WasmResult parse_line(Context* ctx) {
return WASM_OK;
}
+static WasmResult parse_type_object(Context* ctx, WasmType* out_type) {
+ WasmStringSlice type_str;
+ EXPECT("{");
+ PARSE_KEY_STRING_VALUE("type", &type_str);
+ EXPECT("}");
+
+ if (string_slice_equals_str(&type_str, "i32")) {
+ *out_type = WASM_TYPE_I32;
+ return WASM_OK;
+ } else if (string_slice_equals_str(&type_str, "f32")) {
+ *out_type = WASM_TYPE_F32;
+ return WASM_OK;
+ } else if (string_slice_equals_str(&type_str, "i64")) {
+ *out_type = WASM_TYPE_I64;
+ return WASM_OK;
+ } else if (string_slice_equals_str(&type_str, "f64")) {
+ *out_type = WASM_TYPE_F64;
+ return WASM_OK;
+ } else {
+ print_parse_error(ctx, "unknown type: \"" PRIstringslice "\"",
+ WASM_PRINTF_STRING_SLICE_ARG(type_str));
+ return WASM_ERROR;
+ }
+}
+
+static WasmResult parse_type_vector(Context* ctx, WasmTypeVector* out_types) {
+ WASM_ZERO_MEMORY(*out_types);
+ EXPECT("[");
+ WasmBool first = WASM_TRUE;
+ while (!match(ctx, "]")) {
+ if (!first)
+ EXPECT(",");
+ WasmType type;
+ CHECK_RESULT(parse_type_object(ctx, &type));
+ first = WASM_FALSE;
+ wasm_append_type_value(ctx->allocator, out_types, &type);
+ }
+ return WASM_OK;
+}
+
static WasmResult parse_const(Context* ctx,
WasmInterpreterTypedValue* out_value) {
WasmStringSlice type_str;
@@ -1457,13 +1497,19 @@ static WasmResult parse_command(Context* ctx) {
destroy_action(ctx->allocator, &action);
} else if (match(ctx, "\"assert_return_nan\"")) {
Action action;
+ WasmTypeVector expected;
WASM_ZERO_MEMORY(action);
EXPECT(",");
CHECK_RESULT(parse_line(ctx));
EXPECT(",");
CHECK_RESULT(parse_action(ctx, &action));
+ EXPECT(",");
+ /* Not needed for wasm-interp, but useful for other parsers. */
+ EXPECT_KEY("expected");
+ CHECK_RESULT(parse_type_vector(ctx, &expected));
on_assert_return_nan_command(ctx, &action);
+ wasm_destroy_type_vector(ctx->allocator, &expected);
destroy_action(ctx->allocator, &action);
} else if (match(ctx, "\"assert_trap\"")) {
Action action;
diff --git a/test/gen-spec-empty-prefix.js b/test/gen-spec-empty-prefix.js
new file mode 100644
index 00000000..6ed02d3a
--- /dev/null
+++ b/test/gen-spec-empty-prefix.js
@@ -0,0 +1 @@
+// A deliberately empty file for testing.
diff --git a/test/gen-spec-js.py b/test/gen-spec-js.py
index 45e72c3d..cd3f3d75 100755
--- a/test/gen-spec-js.py
+++ b/test/gen-spec-js.py
@@ -16,181 +16,473 @@
#
import argparse
+from collections import namedtuple
import cStringIO
import json
import os
+import re
+import struct
import sys
-from utils import Error
-
-JS_HEADER = """\
-var passed = 0;
-var failed = 0;
-var quiet = false;
-
-"""
-
-MODULE_RUN = """\
-testModule%(module_index)s();
-"""
-
-MODULE_RUN_FOOTER = """\
-end();
-
-"""
-
-MODULE_TEST_HEADER = """\
-function testModule%(module_index)s() {
-"""
-
-MODULE_DATA_HEADER = """\
- var module = createModule([
-"""
-
-MODULE_DATA_FOOTER = """\
- ]);
-
-"""
-
-INVOKE_COMMAND = """\
- invoke(module, '%(name)s');
-"""
-
-ASSERT_RETURN_COMMAND = """\
- assertReturn(module, '%(name)s', '%(file)s', %(line)s);
-"""
-
-ASSERT_TRAP_COMMAND = """\
- assertTrap(module, '%(name)s', '%(file)s', %(line)s);
-"""
-
-COMMANDS = {
- 'invoke': INVOKE_COMMAND,
- 'assert_return': ASSERT_RETURN_COMMAND,
- 'assert_return_nan': ASSERT_RETURN_COMMAND,
- 'assert_trap': ASSERT_TRAP_COMMAND
-}
-
-MODULE_TEST_FOOTER = """\
-}
-
-"""
-
-JS_FOOTER = """\
-function createModule(data) {
- var u8a = new Uint8Array(data);
- var ffi = {spectest: {print: print}};
- return Wasm.instantiateModule(u8a, ffi);
-}
-
-function assertReturn(module, name, file, line) {
- try {
- var result = module.exports[name]();
- } catch(e) {
- print(file + ":" + line + ": " + name + " unexpectedly threw: " + e);
- }
-
- if (result == 1) {
- passed++;
- } else {
- print(file + ":" + line + ": " + name + " failed.");
- failed++;
- }
-}
-
-function assertTrap(module, name, file, line) {
- var threw = false;
- try {
- module.exports[name]();
- } catch (e) {
- threw = true;
- }
-
- if (threw) {
- passed++;
- } else {
- print(file + ":" + line + ": " + name + " failed, didn't throw");
- failed++;
- }
-}
-
-function invoke(module, name) {
- try {
- var invokeResult = module.exports[name]();
- passed++;
- } catch(e) {
- print(name + " unexpectedly threw: " + e);
- failed++;
- }
-
- if (!quiet)
- print(name + " = " + invokeResult);
-}
-
-function end() {
- if ((failed > 0) || !quiet)
- print(passed + "/" + (passed + failed) + " tests passed.");
-}
-"""
-
-def ProcessJsonFile(json_file_path):
- json_file_dir = os.path.dirname(json_file_path)
- with open(json_file_path) as json_file:
- json_data = json.load(json_file)
-
- output = cStringIO.StringIO()
- output.write(JS_HEADER)
- for module_index in range(len(json_data['modules'])):
- output.write(MODULE_RUN % {'module_index': module_index})
- output.write(MODULE_RUN_FOOTER)
-
- for module_index, module in enumerate(json_data['modules']):
- module_filepath = os.path.join(json_file_dir, module['filename'])
- with open(module_filepath, 'rb') as wasm_file:
- wasm_data = wasm_file.read()
-
- output.write(MODULE_TEST_HEADER % {'module_index': module_index})
- output.write(MODULE_DATA_HEADER)
- WriteModuleBytes(output, wasm_data)
- output.write(MODULE_DATA_FOOTER)
-
- for command_index, command in enumerate(module['commands']):
- output.write(COMMANDS[command['type']] % command)
- output.write(MODULE_TEST_FOOTER)
- output.write(JS_FOOTER)
-
- return output.getvalue()
-
-
-def WriteModuleBytes(output, module_data):
- BYTES_PER_LINE = 16
- offset = 0
- while True:
- line_data = module_data[offset:offset+BYTES_PER_LINE]
- if not line_data:
- break
- output.write(' ')
- output.write(', '.join('%3d' % ord(byte) for byte in line_data))
- output.write(',\n')
- offset += BYTES_PER_LINE
+from find_exe import GetWast2WasmExecutable, GetWasm2WastExecutable
+from utils import ChangeDir, ChangeExt, Error, Executable
+import utils
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+F32_INF = 0x7f800000
+F32_NEG_INF = 0xff800000
+F32_NEG_ZERO = 0x80000000
+F32_SIGN_BIT = F32_NEG_ZERO
+F32_SIG_MASK = 0x7fffff
+F32_QUIET_NAN_TAG = 0x400000
+F64_INF = 0x7ff0000000000000L
+F64_NEG_INF = 0xfff0000000000000L
+F64_NEG_ZERO = 0x8000000000000000L
+F64_SIGN_BIT = F64_NEG_ZERO
+F64_SIG_MASK = 0xfffffffffffffL
+F64_QUIET_NAN_TAG = 0x8000000000000L
+
+def I32ToJS(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)
+
+def ReinterpretF32(f32_bits):
+ 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
+
+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))
+
+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)
+
+def IsNaNF64(f64_bits):
+ 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]
+
+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
+
+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))
+
+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))
+
+
+def UnescapeWasmString(s):
+ # Wast allows for more escape characters than this, but we assume that
+ # wasm2wast 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
+
+
+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(long(const['value']))
+
+def IsValidJSAction(action):
+ 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_ == 'assert_return_nan':
+ return IsValidJSAction(action)
+ elif type_ == 'assert_trap':
+ 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_nan',
+ 'assert_trap'):
+ 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, wast2wasm, wasm2wast, temp_dir):
+ self.wast2wasm = wast2wasm
+ self.wasm2wast = wasm2wast
+ self.temp_dir = temp_dir
+ self.lines = []
+ self.exports = {}
+
+ def Extend(self, wasm_path, commands):
+ wast_path = self._RunWasm2Wast(wasm_path)
+ with open(wast_path) as wast_file:
+ wast = wast_file.read()
+
+ self.lines = []
+ self.exports = self._GetExports(wast)
+ for i, command in enumerate(commands):
+ self._Command(i, command)
+
+ wast = wast[:wast.rindex(')')] + '\n\n'
+ wast += '\n'.join(self.lines) + ')'
+ # print wast
+ with open(wast_path, 'w') as wast_file:
+ wast_file.write(wast)
+ return self._RunWast2Wasm(wast_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._Compare(expected['type'])
+ self.lines.extend(['i32.eqz', 'br_if 0'])
+ self.lines.extend(['return', 'end', 'unreachable', ')'])
+ elif command_type == 'assert_return_nan':
+ type_ = command['expected'][0]['type']
+ self.lines.append('(func (export "%s")' % new_field)
+ self.lines.append('(local %s)' % type_)
+ self.lines.append('block')
+ self._Action(command['action'])
+ self.lines.append('tee_local 0')
+ self._Reinterpret(type_)
+ self.lines.append('get_local 0')
+ self._Reinterpret(type_)
+ self._Compare(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'
+ elif command_type == 'assert_trap':
+ 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 _GetExports(self, wast):
+ result = {}
+ pattern = r'^\s*\(export \"(.*?)\"\s*\((\w+) (\d+)'
+ for name, type_, index in re.findall(pattern, wast, 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 _Compare(self, type_):
+ self.lines.append({'i32': 'i32.eq',
+ 'i64': 'i64.eq',
+ 'f32': 'i32.eq',
+ 'f64': 'i64.eq'}[type_])
+
+ 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(long(const['value']))
+ self.lines.append(inst)
+
+ def _RunWasm2Wast(self, wasm_path):
+ wast_path = ChangeDir(ChangeExt(wasm_path, '.wast'), self.temp_dir)
+ self.wasm2wast.RunWithArgs(wasm_path, '-o', wast_path)
+ return wast_path
+
+ def _RunWast2Wasm(self, wast_path):
+ wasm_path = ChangeDir(ChangeExt(wast_path, '.wasm'), self.temp_dir)
+ self.wast2wasm.RunWithArgs(wast_path, '-o', wasm_path)
+ return wasm_path
+
+
+class JSWriter(object):
+ def __init__(self, base_dir, commands, out_file):
+ self.base_dir = base_dir
+ self.commands = commands
+ self.out_file = out_file
+
+ def Write(self):
+ for command in self.commands:
+ self._WriteCommand(command)
+
+ 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_nan': self._WriteAssertActionCommand,
+ 'assert_trap': self._WriteAssertActionCommand,
+ }
+
+ func = command_funcs.get(command['type'])
+ if func is None:
+ raise Error('Unexpected type: %s' % command['type'])
+ func(command)
+
+ def _WriteModuleCommand(self, command):
+ if 'name' in command:
+ self.out_file.write('let %s = ' % command['name'])
+ self.out_file.write('$$ = instance("%s");\n' %
+ self._Module(command['filename']))
+
+ 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', '$$')))
+
+ def _WriteAssertModuleCommand(self, command):
+ filename = command.get('filename')
+ if filename:
+ self.out_file.write('%s("%s");\n' % (command['type'],
+ self._Module(filename)))
+ else:
+ # TODO(binji): this is only needed because assert_invalid doesn't write a
+ # module file currently.
+ self.out_file.write('// No filename for command: %s\n' % command)
+
+ 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' % ord(c) for c in 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']
+ if type_ not in ('invoke', 'get'):
+ raise Error('Unexpected action type: %s' % type_)
+
+ args = ''
+ if type_ == 'invoke':
+ args = '(%s)' % self._ConstantList(action.get('args', []))
+
+ return '%s.exports["%s"]%s' % (action.get('module', '$$'),
+ EscapeJSString(action['field']),
+ args)
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('--wast2wasm', metavar='PATH',
+ help='set the wast2wasm executable to use.')
+ parser.add_argument('--wasm2wast', metavar='PATH',
+ help='set the wasm2wast executable to use.')
+ parser.add_argument('--temp-dir', metavar='PATH',
+ help='set the directory that temporary wasm/wast'
+ ' 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)
- output_data = ProcessJsonFile(options.file)
+ wast2wasm = Executable(GetWast2WasmExecutable(options.wast2wasm),
+ error_cmdline=options.error_cmdline)
+ wasm2wast = Executable(GetWasm2WastExecutable(options.wasm2wast),
+ error_cmdline=options.error_cmdline)
+
+ wast2wasm.verbose = options.print_cmd
+ wasm2wast.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(wast2wasm, wasm2wast, 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 = cStringIO.StringIO()
+ if options.prefix:
+ with open(options.prefix) as prefix_file:
+ output.write(prefix_file.read())
+ output.write('\n')
+
+ JSWriter(json_dir, all_commands, output).Write()
+
if options.output:
- output_file = open(options.output, 'w')
+ out_file = open(options.output, 'w')
else:
- output_file = sys.stdout
+ out_file = sys.stdout
try:
- output_file.write(output_data)
+ out_file.write(output.getvalue())
finally:
- output_file.close()
+ out_file.close()
return 0
diff --git a/test/gen-spec-js/action.txt b/test/gen-spec-js/action.txt
new file mode 100644
index 00000000..ace9edac
--- /dev/null
+++ b/test/gen-spec-js/action.txt
@@ -0,0 +1,17 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(module
+ (import "spectest" "print" (func (param i32)))
+ (func (export "print_i32") (param i32) get_local 0 call 0)
+
+ (global (export "global") i32 (i32.const 14)))
+
+(invoke "print_i32" (i32.const 1))
+(get "global")
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+$$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x05\x01\x60\x01\x7f\x00\x02\x12\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x05\x70\x72\x69\x6e\x74\x00\x00\x03\x02\x01\x00\x06\x06\x01\x7f\x00\x41\x0e\x0b\x07\x16\x02\x09\x70\x72\x69\x6e\x74\x5f\x69\x33\x32\x00\x01\x06\x67\x6c\x6f\x62\x61\x6c\x03\x00\x0a\x08\x01\x06\x00\x20\x00\x10\x00\x0b");
+$$.exports["print_i32"](1);
+$$.exports["global"];
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/assert_malformed.txt b/test/gen-spec-js/assert_malformed.txt
new file mode 100644
index 00000000..c97bb2e9
--- /dev/null
+++ b/test/gen-spec-js/assert_malformed.txt
@@ -0,0 +1,10 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(assert_malformed
+ (module "\00asm\bc\0a\00\00")
+ "unknown binary version")
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+assert_malformed("\x00\x61\x73\x6d\xbc\x0a\x00\x00");
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/assert_return.txt b/test/gen-spec-js/assert_return.txt
new file mode 100644
index 00000000..02bea616
--- /dev/null
+++ b/test/gen-spec-js/assert_return.txt
@@ -0,0 +1,48 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(module
+ (func (export "no_result"))
+ (func (export "42") (result i32) i32.const 42)
+ (func (export "i32.add") (param i32 i32) (result i32)
+ get_local 0
+ get_local 1
+ i32.add)
+ (func (export "i64.add") (param i64 i64) (result i64)
+ get_local 0
+ get_local 1
+ i64.add)
+ (func (export "f32.add") (param f32 f32) (result f32)
+ get_local 0
+ get_local 1
+ f32.add)
+ (func (export "f64.add") (param f64 f64) (result f64)
+ get_local 0
+ get_local 1
+ f64.add)
+ (func (export "nan") (result f32) f32.const nan:0x2))
+
+(assert_return (invoke "no_result"))
+(assert_return (invoke "42") (i32.const 42))
+
+(assert_return (invoke "i32.add" (i32.const 1) (i32.const 2)) (i32.const 3))
+;; Rewritten to avoid passing i64 values as parameters.
+(assert_return (invoke "i64.add" (i64.const 1) (i64.const 2)) (i64.const 3))
+;; Normal floats are not rewritten.
+(assert_return (invoke "f32.add" (f32.const 1) (f32.const 2)) (f32.const 3))
+(assert_return (invoke "f64.add" (f64.const 1) (f64.const 2)) (f64.const 3))
+
+;; Rewritten to avoid passing nan as a parameter.
+(assert_return (invoke "nan") (f32.const nan:0x2))
+
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+$$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x24\x07\x60\x00\x00\x60\x00\x01\x7f\x60\x02\x7f\x7f\x01\x7f\x60\x02\x7e\x7e\x01\x7e\x60\x02\x7d\x7d\x01\x7d\x60\x02\x7c\x7c\x01\x7c\x60\x00\x01\x7d\x03\x0a\x09\x00\x01\x02\x03\x04\x05\x06\x00\x00\x07\x56\x09\x09\x6e\x6f\x5f\x72\x65\x73\x75\x6c\x74\x00\x00\x02\x34\x32\x00\x01\x07\x69\x33\x32\x2e\x61\x64\x64\x00\x02\x07\x69\x36\x34\x2e\x61\x64\x64\x00\x03\x07\x66\x33\x32\x2e\x61\x64\x64\x00\x04\x07\x66\x36\x34\x2e\x61\x64\x64\x00\x05\x03\x6e\x61\x6e\x00\x06\x08\x61\x73\x73\x65\x72\x74\x5f\x30\x00\x07\x08\x61\x73\x73\x65\x72\x74\x5f\x31\x00\x08\x0a\x5a\x09\x02\x00\x0b\x04\x00\x41\x2a\x0b\x07\x00\x20\x00\x20\x01\x6a\x0b\x07\x00\x20\x00\x20\x01\x7c\x0b\x07\x00\x20\x00\x20\x01\x92\x0b\x07\x00\x20\x00\x20\x01\xa0\x0b\x07\x00\x43\x02\x00\x80\x7f\x0b\x13\x00\x02\x40\x42\x01\x42\x02\x10\x03\x42\x03\x51\x45\x0d\x00\x0f\x0b\x00\x0b\x14\x00\x02\x40\x10\x06\xbc\x43\x02\x00\x80\x7f\xbc\x46\x45\x0d\x00\x0f\x0b\x00\x0b");
+assert_return(() => $$.exports["no_result"]());
+assert_return(() => $$.exports["42"](), 42);
+assert_return(() => $$.exports["i32.add"](1, 2), 3);
+assert_return(() => $$.exports["assert_0"]());
+assert_return(() => $$.exports["f32.add"](f32(1.0), f32(2.0)), f32(3.0));
+assert_return(() => $$.exports["f64.add"](1.0, 2.0), 3.0);
+assert_return(() => $$.exports["assert_1"]());
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/assert_return_nan.txt b/test/gen-spec-js/assert_return_nan.txt
new file mode 100644
index 00000000..0fdd2334
--- /dev/null
+++ b/test/gen-spec-js/assert_return_nan.txt
@@ -0,0 +1,16 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(module
+ (func (export "nan") (result f32) f32.const nan)
+ (func (export "passthru") (param f32) (result f32) get_local 0))
+
+(assert_return_nan (invoke "nan"))
+;; Rewritten to avoid passing nan as a parameter.
+(assert_return_nan (invoke "passthru" (f32.const -nan)))
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+$$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x0d\x03\x60\x00\x01\x7d\x60\x01\x7d\x01\x7d\x60\x00\x00\x03\x04\x03\x00\x01\x02\x07\x1d\x03\x03\x6e\x61\x6e\x00\x00\x08\x70\x61\x73\x73\x74\x68\x72\x75\x00\x01\x08\x61\x73\x73\x65\x72\x74\x5f\x30\x00\x02\x0a\x29\x03\x07\x00\x43\x00\x00\xc0\x7f\x0b\x04\x00\x20\x00\x0b\x1a\x01\x01\x7d\x02\x40\x43\x00\x00\xc0\xff\x10\x01\x22\x00\xbc\x20\x00\xbc\x46\x45\x0d\x00\x0f\x0b\x00\x0b");
+assert_return_nan(() => $$.exports["nan"]());
+assert_return(() => $$.exports["assert_0"]());
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/assert_trap.txt b/test/gen-spec-js/assert_trap.txt
new file mode 100644
index 00000000..80a7f632
--- /dev/null
+++ b/test/gen-spec-js/assert_trap.txt
@@ -0,0 +1,18 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(module
+ (func (export "unreachable") unreachable)
+ (func (export "i32.trunc_s") (param f32) (result i32)
+ get_local 0
+ i32.trunc_s/f32))
+
+(assert_trap (invoke "unreachable") "unreachable")
+;; Rewritten to avoid passing nan as a parameter.
+(assert_trap (invoke "i32.trunc_s" (f32.const -nan)) "invalid conversion")
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+$$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x09\x02\x60\x00\x00\x60\x01\x7d\x01\x7f\x03\x04\x03\x00\x01\x00\x07\x28\x03\x0b\x75\x6e\x72\x65\x61\x63\x68\x61\x62\x6c\x65\x00\x00\x0b\x69\x33\x32\x2e\x74\x72\x75\x6e\x63\x5f\x73\x00\x01\x08\x61\x73\x73\x65\x72\x74\x5f\x30\x00\x02\x0a\x17\x03\x03\x00\x00\x0b\x05\x00\x20\x00\xa8\x0b\x0b\x00\x43\x00\x00\xc0\xff\x10\x01\x0c\x00\x0b");
+assert_trap(() => $$.exports["unreachable"]());
+assert_trap(() => $$.exports["assert_0"]());
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/assert_uninstantiable.txt b/test/gen-spec-js/assert_uninstantiable.txt
new file mode 100644
index 00000000..81cd8bba
--- /dev/null
+++ b/test/gen-spec-js/assert_uninstantiable.txt
@@ -0,0 +1,12 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(assert_trap
+ (module
+ (func unreachable)
+ (start 0))
+ "trap in start function")
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+assert_uninstantiable("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x04\x01\x60\x00\x00\x03\x02\x01\x00\x08\x01\x00\x0a\x05\x01\x03\x00\x00\x0b");
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/assert_unlinkable.txt b/test/gen-spec-js/assert_unlinkable.txt
new file mode 100644
index 00000000..7ec3ce04
--- /dev/null
+++ b/test/gen-spec-js/assert_unlinkable.txt
@@ -0,0 +1,10 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(assert_unlinkable
+ (module (import "foo" "bar" (func)))
+ "module not linkable")
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+assert_unlinkable("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x04\x01\x60\x00\x00\x02\x0b\x01\x03\x66\x6f\x6f\x03\x62\x61\x72\x00\x00");
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/basic.txt b/test/gen-spec-js/basic.txt
new file mode 100644
index 00000000..85d705ca
--- /dev/null
+++ b/test/gen-spec-js/basic.txt
@@ -0,0 +1,12 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(module
+ (func (export "42") (result i32) i32.const 42))
+
+(assert_return (invoke "42") (i32.const 42))
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+$$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x05\x01\x60\x00\x01\x7f\x03\x02\x01\x00\x07\x06\x01\x02\x34\x32\x00\x00\x0a\x06\x01\x04\x00\x41\x2a\x0b");
+assert_return(() => $$.exports["42"](), 42);
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/many-modules.txt b/test/gen-spec-js/many-modules.txt
new file mode 100644
index 00000000..79c357c5
--- /dev/null
+++ b/test/gen-spec-js/many-modules.txt
@@ -0,0 +1,21 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(module $A (func (export "f") (result i32) i32.const 1))
+(module $B (func (export "f") (result i32) i32.const 2))
+(module $C (func (export "f") (result i32) i32.const 3))
+
+(assert_return (invoke "f") (i32.const 3))
+(assert_return (invoke $A "f") (i32.const 1))
+(assert_return (invoke $B "f") (i32.const 2))
+(assert_return (invoke $C "f") (i32.const 3))
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+let $A = $$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x05\x01\x60\x00\x01\x7f\x03\x02\x01\x00\x07\x05\x01\x01\x66\x00\x00\x0a\x06\x01\x04\x00\x41\x01\x0b");
+let $B = $$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x05\x01\x60\x00\x01\x7f\x03\x02\x01\x00\x07\x05\x01\x01\x66\x00\x00\x0a\x06\x01\x04\x00\x41\x02\x0b");
+let $C = $$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x05\x01\x60\x00\x01\x7f\x03\x02\x01\x00\x07\x05\x01\x01\x66\x00\x00\x0a\x06\x01\x04\x00\x41\x03\x0b");
+assert_return(() => $$.exports["f"](), 3);
+assert_return(() => $A.exports["f"](), 1);
+assert_return(() => $B.exports["f"](), 2);
+assert_return(() => $C.exports["f"](), 3);
+;;; STDOUT ;;)
diff --git a/test/gen-spec-js/register.txt b/test/gen-spec-js/register.txt
new file mode 100644
index 00000000..18ffa688
--- /dev/null
+++ b/test/gen-spec-js/register.txt
@@ -0,0 +1,20 @@
+;;; TOOL: run-gen-spec-js
+;;; FLAGS: --prefix=%(test_dir)s/gen-spec-empty-prefix.js
+(module
+ (func (export "f") (result i32) i32.const 1))
+
+(register "A")
+
+(module
+ (import "A" "f" (func (result i32)))
+ (func (export "g") (result i32) call 0))
+
+(assert_return (invoke "g") (i32.const 1))
+(;; STDOUT ;;;
+// A deliberately empty file for testing.
+
+$$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x05\x01\x60\x00\x01\x7f\x03\x02\x01\x00\x07\x05\x01\x01\x66\x00\x00\x0a\x06\x01\x04\x00\x41\x01\x0b");
+register("A", $$)
+$$ = instance("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x05\x01\x60\x00\x01\x7f\x02\x07\x01\x01\x41\x01\x66\x00\x00\x03\x02\x01\x00\x07\x05\x01\x01\x67\x00\x01\x0a\x06\x01\x04\x00\x10\x00\x0b");
+assert_return(() => $$.exports["g"](), 1);
+;;; STDOUT ;;)
diff --git a/test/gen-spec-prefix.js b/test/gen-spec-prefix.js
new file mode 100644
index 00000000..99cfab7a
--- /dev/null
+++ b/test/gen-spec-prefix.js
@@ -0,0 +1,93 @@
+/* Copied from *
+ * https://github.com/WebAssembly/spec/blob/master/interpreter/host/js.ml */
+'use strict';
+
+let soft_validate = true;
+
+let spectest = {
+ print: print || ((...xs) => console.log(...xs)),
+ global: 666,
+ table: new WebAssembly.Table({initial: 10, maximum: 20, element: 'anyfunc'}),
+ memory: new WebAssembly.Memory({initial: 1, maximum: 2}),};
+
+let registry = {spectest};
+let $$;
+
+function register(name, instance) {
+ registry[name] = instance.exports;
+}
+
+function module(bytes) {
+ let buffer = new ArrayBuffer(bytes.length);
+ let view = new Uint8Array(buffer);
+ for (let i = 0; i < bytes.length; ++i) {
+ view[i] = bytes.charCodeAt(i);
+ }
+ return new WebAssembly.Module(buffer);
+}
+
+function instance(bytes, imports = registry) {
+ return new WebAssembly.Instance(module(bytes), imports);
+}
+
+function assert_malformed(bytes) {
+ try { module(bytes) } catch (e) {
+ if (e instanceof WebAssembly.CompileError) return;
+ }
+ throw new Error("Wasm decoding failure expected");
+}
+
+function assert_invalid(bytes) {
+ try { module(bytes) } catch (e) {
+ if (e instanceof WebAssembly.CompileError) return;
+ }
+ throw new Error("Wasm validation failure expected");
+}
+
+function assert_soft_invalid(bytes) {
+ try { module(bytes) } catch (e) {
+ if (e instanceof WebAssembly.CompileError) return;
+ throw new Error("Wasm validation failure expected");
+ }
+ if (soft_validate)
+ throw new Error("Wasm validation failure expected");
+}
+
+function assert_unlinkable(bytes) {
+ let mod = module(bytes);
+ try { new WebAssembly.Instance(mod, registry) } catch (e) {
+ if (e instanceof TypeError) return;
+ }
+ throw new Error("Wasm linking failure expected");
+}
+
+function assert_uninstantiable(bytes) {
+ let mod = module(bytes);
+ try { new WebAssembly.Instance(mod, registry) } catch (e) {
+ if (e instanceof WebAssembly.RuntimeError) return;
+ }
+ throw new Error("Wasm trap expected");
+}
+
+function assert_trap(action) {
+ try { action() } catch (e) {
+ if (e instanceof WebAssembly.RuntimeError) return;
+ }
+ throw new Error("Wasm trap expected");
+}
+
+function assert_return(action, expected) {
+ let actual = action();
+ if (!Object.is(actual, expected)) {
+ throw new Error("Wasm return value " + expected + " expected, got " + actual);
+ };
+}
+
+function assert_return_nan(action) {
+ let actual = action();
+ if (!Number.isNaN(actual)) {
+ throw new Error("Wasm return value NaN expected, got " + actual);
+ };
+}
+
+let f32 = Math.fround;
diff --git a/test/run-gen-spec-js.py b/test/run-gen-spec-js.py
new file mode 100755
index 00000000..73daee12
--- /dev/null
+++ b/test/run-gen-spec-js.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 WebAssembly Community Group participants
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import argparse
+import os
+import subprocess
+import sys
+
+import find_exe
+import utils
+from utils import Error
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+GEN_SPEC_JS_PY = os.path.join(SCRIPT_DIR, 'gen-spec-js.py')
+
+
+def main(args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-o', '--out-dir', metavar='PATH',
+ help='output directory for files.')
+ parser.add_argument('--wast2wasm', metavar='PATH',
+ help='override wast2wasm executable.')
+ parser.add_argument('--wasm2wast', metavar='PATH',
+ help='override wasm2wast executable.')
+ parser.add_argument('--js-engine', metavar='PATH',
+ help='the path to the JavaScript engine with which to run'
+ ' the generated JavaScript. If not specified, JavaScript'
+ ' output will be written to stdout.')
+ parser.add_argument('--js-engine-flags', metavar='FLAGS',
+ help='additional flags for JavaScript engine.',
+ action='append', default=[])
+ parser.add_argument('--prefix-js',
+ help='Prefix JavaScript file to pass to gen-spec-js')
+ 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('--use-libc-allocator', action='store_true')
+ parser.add_argument('file', help='wast file.')
+ options = parser.parse_args(args)
+
+ with utils.TempDirectory(options.out_dir, 'run-gen-spec-js-') as out_dir:
+ wast2wasm = utils.Executable(
+ find_exe.GetWast2WasmExecutable(options.wast2wasm),
+ '--spec',
+ '--no-check-assert-invalid',
+ error_cmdline=options.error_cmdline)
+ wast2wasm.AppendOptionalArgs({
+ '-v': options.verbose,
+ '--use-libc-allocator': options.use_libc_allocator
+ })
+
+ gen_spec_js = utils.Executable(
+ sys.executable, GEN_SPEC_JS_PY,
+ '--temp-dir', out_dir,
+ error_cmdline=options.error_cmdline)
+ gen_spec_js.AppendOptionalArgs({
+ '--wast2wasm': options.wast2wasm,
+ '--wasm2wast': options.wasm2wast,
+ '--prefix': options.prefix_js,
+ })
+ gen_spec_js.verbose = options.print_cmd
+
+ json_file = utils.ChangeDir(utils.ChangeExt(options.file, '.json'), out_dir)
+ js_file = utils.ChangeExt(json_file, '.js')
+ wast2wasm.RunWithArgs(options.file, '-o', json_file)
+
+ if options.js_engine:
+ gen_spec_js.RunWithArgs(json_file, '-o', js_file)
+ js = utils.Executable(options.js_engine, *options.js_engine_flags)
+ js.RunWithArgs(js_file)
+ else:
+ # Write JavaScript output to stdout
+ gen_spec_js.RunWithArgs(json_file)
+
+if __name__ == '__main__':
+ 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-tests.py b/test/run-tests.py
index ba544d98..e0f53d6e 100755
--- a/test/run-tests.py
+++ b/test/run-tests.py
@@ -157,7 +157,22 @@ TOOLS = {
]),
'-v'
]
- }
+ },
+ 'run-gen-spec-js': {
+ 'EXE': 'test/run-gen-spec-js.py',
+ 'FLAGS': ' '.join([
+ '--wast2wasm=%(wast2wasm)s',
+ '--wasm2wast=%(wasm2wast)s',
+ '--no-error-cmdline',
+ '-o', '%(out_dir)s',
+ ]),
+ 'VERBOSE-FLAGS': [
+ ' '.join([
+ '--print-cmd',
+ ]),
+ '-v'
+ ]
+ },
}
ROUNDTRIP_TOOLS = ('wast2wasm',)
@@ -743,6 +758,7 @@ def main(args):
return 1
variables = {}
+ variables['test_dir'] = os.path.abspath(SCRIPT_DIR)
for exe_basename in find_exe.EXECUTABLES:
attr_name = exe_basename.replace('-', '_')
diff --git a/test/utils.py b/test/utils.py
index 254257cd..49fcdeaf 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -99,7 +99,10 @@ class Executable(object):
def AppendOptionalArgs(self, option_dict):
for option, value in option_dict.items():
if value:
- self.AppendArg(option)
+ if value is True:
+ self.AppendArg(option)
+ else:
+ self.AppendArg('%s=%s' % (option, value))
@contextlib.contextmanager