diff options
Diffstat (limited to 'test/gen-spec-js.py')
-rwxr-xr-x | test/gen-spec-js.py | 902 |
1 files changed, 451 insertions, 451 deletions
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) |