def test_type1(): """ Test canoncocal form of import and func and inline typedefs. """ # The canonical form CODE0 = dedent(""" (module (type $0 (func (param i32))) (type $1 (func (param i32 i32) (result i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $0))) (start $main) (func $add (type $1) (get_local 0) (get_local 1) (i32.add) ) (func $main (type $2) (i32.const 4) (i32.const 3) (call $add) (call $print) ) ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 if has_node(): assert run_wasm_in_node(m0, True) == '7' # Abbreviation: inline typedefs CODE1 = """ (module (import "js" "print_ln" (func $print (param i32))) (start $main) (func $add (param i32 i32) (result i32) (get_local 0) (get_local 1) (i32.add) ) (func $main (i32.const 4) (i32.const 3) (call $add) (call $print) ) ) """ m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0
def test_read_wat(): for text in (TEXT1, TEXT2): m = Module(text) m.to_string() b = m.to_bytes() assert isinstance(b, bytes)
def test_export1(): # The canonical form CODE0 = dedent(""" (module (type $sig (func)) (table $t1 2 anyfunc) (memory $m1 1) (global $g1 i32 (i32.const 7)) (export "bar_table1" (table $t1)) (export "bar_mem1" (memory $m1)) (export "bar_global" (global $g1)) (export "bar_func1" (func $f1)) (export "bar_func2" (func $f1)) (func $f1 (type $sig) ) ) """) # Test main code m0 = Module(CODE0) print(m0.to_string()) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 # Export abbreviations: definitions of func/memory/table/global # that are really exports. CODE1 = dedent(""" (module (type $sig (func)) (table $t1 (export "bar_table1") 2 anyfunc) (memory $m1 (export "bar_mem1") 1) (global $g1 (export "bar_global") i32 (i32.const 7)) (func $f1 (export "bar_func1") (export "bar_func2") (type $sig) ) ) """) m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0
def test_import1(): # The canonical form CODE0 = dedent(""" (module (type $bar_func (func)) (import "foo" "bar_func" (func $bar_func (type $bar_func))) (import "foo" "bar_table1" (table $t1 2 funcref)) (import "foo" "bar_mem1" (memory $m1 1)) (import "foo" "bar_mem2" (memory $m2 1 2)) (import "foo" "bar_global" (global $bar_global i32)) ) """) # (import "foo" "bar_table" (table $bar_table (type $bar_func))) # Test main code m0 = Module(CODE0) print(m0.to_string()) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 # Import abbreviations: definitions of func/memory/table/global # that are really imports. CODE1 = dedent(""" (module (type $bar_func (func)) (func $bar_func (import "foo" "bar_func") (type $bar_func)) (table $t1 (import "foo" "bar_table1") 2 funcref) (memory $m1 (import "foo" "bar_mem1") 1) (memory $m2 (import "foo" "bar_mem2") 1 2) (global $bar_global (import "foo" "bar_global") i32) ) """) m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0
def parse_module(self, s_expr): if 'binary' in s_expr: # We have (module binary "") # Iterate: elems = iter(s_expr) # Skip to binary tag: while next(elems) != 'binary': pass # fetch data from last tuple elements: parts = [] for elem in elems: data = datastring2bytes(elem) parts.append(data) data = reduce(add, parts) # Load module from binary data: m1 = Module(data) # Go back data2 = m1.to_bytes() # Wont always be the same, e.g. some tests use non-minimal LEB ints # assert data == data2 data3 = Module(data2).to_bytes() # Check that reading it in result in the same module ... assert data2 == data3 else: # Load module from tuples: m1 = Module(s_expr) # # Convert module to text form and parse again # # This should yield the same binary form: # m2 = Module(m1.to_string()) # assert m1.to_bytes() == m2.to_bytes() # NOTE: going to string format and back does not # guarantee that parsing was correct. self.reporter.dump_wasm(m1) self.logger.debug('loaded wasm module %s (id=%s)', m1, m1.id) return m1
def test_global1(): CODE0 = dedent(r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (global $foo i32 i32.const 7) (start $main) (func $main (type $2) global.get $foo call $print) ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 if has_node(): assert run_wasm_in_node(m0, True) == '7'
def do_func(fname): """ Test parsing on a single test file. Its great to call this at the botton during dev! """ print('Testing {} - "{}"'.format(fname, os.path.join(get_spec_suite_dir(), fname))) for text in get_test_script_parts(fname): sexpr = parse_sexpr(text) # Assert that the toplevel expression makes sense assert sexpr[0] in ( 'module', 'invoke', 'register', 'assert_return', 'assert_invalid', 'assert_trap', 'assert_malformed', 'assert_exhaustion', 'assert_unlinkable', 'assert_return_canonical_nan', 'assert_return_arithmetic_nan', 'func', 'memory', # inline-module.wast ), '{}: unexpected expression in'.format(fname) # But in this script we only do modules if sexpr[0] != 'module': continue if 'binary' in sexpr: continue # todo: skipping a few here, for now if fname in ( 'names.wast', 'comments.wast', # because sending Unicode over Pipes seems to go wrong ): continue wasm_bin0 = wabt.wat2wasm(text) m1 = Module(wasm_bin0) m2 = Module(text) m3 = Module(sexpr) wasm_bin1 = m1.to_bytes() wasm_bin2 = m2.to_bytes() wasm_bin3 = m3.to_bytes() assert wasm_bin0 == wasm_bin1, '{}: our binary parsing is broken'.format( fname) assert wasm_bin2 == wasm_bin3, '{}: our text/tuple paring differs'.format( fname) assert wasm_bin0 == wasm_bin2, '{}: our text parsing is broken'.format( fname) if False: # debug helpers print(len(wasm_bin1), len(wasm_bin2)) print(len(m1.definitions), len(m2.definitions)) hexdump(wasm_bin1) print() hexdump(wasm_bin2)
def test_table1(): # The canonical form CODE0 = dedent(r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (table $0 2 2 funcref) (start $main) (elem i32.const 0 $f1 $f2) (func $f1 (type $2) i32.const 101 call $print) (func $f2 (type $2) i32.const 102 call $print) (func $main (type $2) i32.const 0 call_indirect (type $2) i32.const 1 call_indirect (type $2)) ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 html_report = 'table_and_element_compilation_report.html' with open(html_report, 'w') as f, HtmlReportGenerator(f) as reporter: printed_numbers = [] def print_ln(x: int) -> None: printed_numbers.append(x) imports = { 'js': { 'print_ln': print_ln, }, } instantiate(m0, imports, target='python', reporter=reporter) assert [101, 102] == printed_numbers if is_platform_supported(): printed_numbers = [] def print_ln(x: int) -> None: printed_numbers.append(x) imports = { 'js': { 'print_ln': print_ln, }, } instantiate(m0, imports, target='native', reporter=reporter) assert [101, 102] == printed_numbers if has_node(): assert run_wasm_in_node(m0, True) == '101\n102' # Abbreviation: imported table m3 = Module('(module (table $t1 (import "foo" "bar_table1") funcref) )') assert m3.to_string() == dedent(""" (module (import "foo" "bar_table1" (table $t1 funcref)) ) """) m3 = Module('(module (table (import "foo" "bar_table1") 2 3 funcref) )') assert m3.to_string() == dedent(""" (module (import "foo" "bar_table1" (table 2 3 funcref)) ) """) # Abbeviation: inline data and unspecified (default) alignment CODE1 = r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (table funcref (elem $f1 $f2)) (start $main) (func $f1 (type $2) (i32.const 101) (call $print) ) (func $f2 (type $2) (i32.const 102) (call $print) ) (func $main (type $2) (i32.const 0) (call_indirect (type $2)) (i32.const 1) (call_indirect (type $2)) ) ) """ m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0
def test_func1(): # The canonical form CODE0 = dedent(""" (module (type $0 (func (param i32))) (type $1 (func (param i32 i32) (result i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $0))) (start $main) (func $add (type $1) (get_local 0) (get_local 1) (i32.add) ) (func $main (type $2) (local $foo i32) (i32.const 4) (i32.const 3) (call $add) (set_local $foo) (get_local $foo) (call $print) ) ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 # TODO: figure out what is wrong below: if False: printed_numbers = [] def print_ln(x: int) -> None: printed_numbers.append(x) imports = { 'js': { 'print_ln': print_ln, }, } instantiate(m0, imports, target='python') assert [7] == printed_numbers if has_node(): assert run_wasm_in_node(m0, True) == '7' # Abbreviation: inline typedefs CODE1 = """ (module (import "js" "print_ln" (func $print (param i32))) (start $main) (func $add (param i32 i32) (result i32) (get_local 0) (get_local 1) (i32.add) ) (func $main (local $foo i32) (set_local $foo (call $add (i32.const 4) (i32.const 3) ) ) (call $print (get_local $foo) ) ) ) """ m1 = Module(CODE1) assert m1.to_string() == CODE0 # look at the indentation! assert m1.to_bytes() == b0
def test_instructions1(): """ Test canoncocal form of import and func and inline typedefs. """ # The canonical form CODE0 = dedent(""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (start $main) (func $main (type $2) (i32.const 1) (if) (i32.const 4) (i32.const 3) (i32.add) (call $print) (else) (i32.const 5) (call $print) (end) ) ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 if has_node(): assert run_wasm_in_node(m0, True) == '7' # Variant 1 - no inentation, nested test for if CODE1 = dedent(""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (start $main) (func $main (type $2) (if (i32.const 1)) (i32.const 4) (i32.const 3) (i32.add) (call $print) (else) (i32.const 5) (call $print) (end) ) ) """) m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0 # Variant 2 - nesting all the way CODE2 = dedent(""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (start $main) (func $main (type $2) (if (i32.const 1) (i32.const 4) (i32.const 3) (i32.add) (call $print) (else) (i32.const 5) (call $print) ) ) ) """) m2 = Module(CODE2) assert m2.to_string() == CODE0 assert m2.to_bytes() == b0 # Variant 3 - leave out the else clause # This is described as an "abbreviation", but it seems that we don't # have to always output an else clause in binary form either. CODE3 = dedent(""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (start $main) (func $main (type $2) (if (i32.const 1) (i32.const 4) (i32.const 3) (i32.add) (call $print) ) ) ) """) m3 = Module(CODE3) assert m3.to_string() != CODE0 assert m3.to_bytes() != b0 if has_node(): assert run_wasm_in_node(m3, True) == '7'
def test_type2(): """ Test inline typedefs with various number of args and results. """ # Canonical form CODE0 = dedent(""" (module (type $0 (func (param i32))) (type $1 (func (param i32) (result i32))) (type $2 (func (result i32))) (type $3 (func)) (import "js" "print_ln" (func $print (type $0))) (start $main) (func $test_11 (type $1) (i32.const 111) (call $print) (i32.const 0) ) (func $test_10 (type $0) (i32.const 110) (call $print) ) (func $test_01 (type $2) (i32.const 101) (call $print) (i32.const 0) ) (func $test_00 (type $3) (i32.const 100) (call $print) ) (func $main (type $3) (i32.const 0) (call $test_11) (drop) (i32.const 0) (call $test_10) (call $test_01) (drop) (call $test_00) ) ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() if has_node(): assert run_wasm_in_node(m0, True) == '111\n110\n101\n100' # Abbreviated CODE1 = """ (module (import "js" "print_ln" (func $print (param i32))) (start $main) (func $test_11 (param i32) (result i32) (i32.const 111) (call $print) (i32.const 0) ) (func $test_10 (param i32) (i32.const 110) (call $print) ) (func $test_01 (result i32) (i32.const 101) (call $print) (i32.const 0) ) (func $test_00 (i32.const 100) (call $print) ) (func $main (i32.const 0) (call $test_11) (drop) (i32.const 0) (call $test_10) (call $test_01) (drop) (call $test_00) ) ) """ m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0
def test_memory1(): # The canonical form CODE0 = dedent(r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (memory $0 1 1) (start $main) (func $main (type $2) i32.const 0 i32.load8_u call $print i32.const 1 i32.load8_u call $print) (data i32.const 0 "\04\03\02") ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 printed_numbers = [] def print_ln(x: int) -> None: printed_numbers.append(x) imports = { 'js': { 'print_ln': print_ln, } } instantiate(m0, imports=imports, target='python') assert [4, 3] == printed_numbers if has_node(): assert run_wasm_in_node(m0, True) == '4\n3' # Abbreviation: imported memory m3 = Module('(module (memory $m1 (import "foo" "bar_mem1") 1) )') assert m3.to_string() == dedent(""" (module (import "foo" "bar_mem1" (memory $m1 1)) ) """) m3 = Module('(module (memory (import "foo" "bar_mem1") 2 3) )') assert m3.to_string() == dedent(""" (module (import "foo" "bar_mem1" (memory 2 3)) ) """) # Abbeviation: inline data and unspecified (default) alignment CODE1 = r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (memory (data "\04\03\02")) (start $main) (func $main (type $2) i32.const 0 i32.load8_u call $print i32.const 1 i32.load8_u call $print ) ) """ m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0
def load_module(self, s_expr): if 'binary' in s_expr: # We have (module binary "") # Iterate: elems = iter(s_expr) # Skip to binary tag: while next(elems) != 'binary': pass # fetch data from last tuple elements: parts = [] for elem in elems: data = datastring2bytes(elem) parts.append(data) data = reduce(add, parts) # Load module from binary data: m1 = Module(data) # Go back data2 = m1.to_bytes() # Wont always be the same, e.g. some tests use non-minimal LEB ints # assert data == data2 data3 = Module(data2).to_bytes() # Check that reading it in result in the same module ... assert data2 == data3 else: # Load module from tuples: m1 = Module(s_expr) # # Convert module to text form and parse again # # This should yield the same binary form: # m2 = Module(m1.to_string()) # assert m1.to_bytes() == m2.to_bytes() # NOTE: going to string format and back does not # guarantee that parsing was correct. self.reporter.dump_wasm(m1) self.logger.debug('loaded wasm module %s', m1) # Next step: Instantiate: if self.target: def my_print() -> None: pass def print_i32(x: int) -> None: pass imports = { 'spectest': { 'print_i32': print_i32, 'print': my_print, } } self.mod_instance = instantiate( m1, imports, target=self.target, reporter=self.reporter) self.logger.debug('Instantiated wasm module %s', self.mod_instance) else: self.mod_instance = None