├── .gitignore ├── README.md ├── images ├── ch02 │ └── ch02.png ├── ch03 │ └── ch03.png ├── ch05 │ └── ch05_param.png ├── ch06 │ └── ch06.png ├── ch07 │ └── ch07.png ├── ch08 │ └── ch08.png ├── ch09 │ ├── ch09_测试HelloWorld程序.png │ └── ch09_测试calc程序.png ├── ch10 │ └── ch10.png └── ch11 │ └── ch11.png ├── js ├── ch01_hw.html ├── ch01_hw.wasm └── ch01_hw.wasm.objdump ├── spec └── test │ └── core │ ├── .gitignore │ ├── README.md │ ├── address.wast │ ├── align.wast │ ├── binary-leb128.wast │ ├── binary.wast │ ├── block.wast │ ├── br.wast │ ├── br_if.wast │ ├── br_table.wast │ ├── call.wast │ ├── call_indirect.wast │ ├── comments.wast │ ├── const.wast │ ├── conversions.wast │ ├── custom.wast │ ├── data.wast │ ├── elem.wast │ ├── endianness.wast │ ├── exports.wast │ ├── f32.wast │ ├── f32_bitwise.wast │ ├── f32_cmp.wast │ ├── f64.wast │ ├── f64_bitwise.wast │ ├── f64_cmp.wast │ ├── fac.wast │ ├── float_exprs.wast │ ├── float_literals.wast │ ├── float_memory.wast │ ├── float_misc.wast │ ├── forward.wast │ ├── func.wast │ ├── func_ptrs.wast │ ├── global.wast │ ├── i32.wast │ ├── i64.wast │ ├── if.wast │ ├── imports.wast │ ├── inline-module.wast │ ├── int_exprs.wast │ ├── int_literals.wast │ ├── labels.wast │ ├── left-to-right.wast │ ├── linking.wast │ ├── load.wast │ ├── local_get.wast │ ├── local_set.wast │ ├── local_tee.wast │ ├── loop.wast │ ├── memory.wast │ ├── memory_grow.wast │ ├── memory_redundancy.wast │ ├── memory_size.wast │ ├── memory_trap.wast │ ├── names.wast │ ├── nop.wast │ ├── return.wast │ ├── select.wast │ ├── skip-stack-guard-page.wast │ ├── stack.wast │ ├── start.wast │ ├── store.wast │ ├── switch.wast │ ├── table.wast │ ├── token.wast │ ├── traps.wast │ ├── type.wast │ ├── unreachable.wast │ ├── unreached-invalid.wast │ ├── unwind.wast │ ├── utf8-custom-section-id.wast │ ├── utf8-import-field.wast │ ├── utf8-import-module.wast │ └── utf8-invalid-encoding.wast ├── src ├── ch02 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── reader.py │ │ └── types.py │ └── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ └── main.py ├── ch03 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── opcodes.py │ │ ├── opnames.py │ │ ├── reader.py │ │ └── types.py │ └── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ └── main.py ├── ch05 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── opcodes.py │ │ ├── opnames.py │ │ ├── reader.py │ │ └── types.py │ ├── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ └── main.py │ └── interpreter │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instr_control.py │ │ ├── instr_numeric.py │ │ ├── instr_parametric.py │ │ ├── instructions.py │ │ ├── vm.py │ │ └── vm_stack_operand.py ├── ch06 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── opcodes.py │ │ ├── opnames.py │ │ ├── reader.py │ │ └── types.py │ ├── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ └── main.py │ └── interpreter │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instr_control.py │ │ ├── instr_memory.py │ │ ├── instr_numeric.py │ │ ├── instr_parametric.py │ │ ├── instructions.py │ │ ├── vm.py │ │ ├── vm_memory.py │ │ └── vm_stack_operand.py ├── ch07 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── opcodes.py │ │ ├── opnames.py │ │ ├── reader.py │ │ └── types.py │ ├── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ └── main.py │ └── interpreter │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instr_control.py │ │ ├── instr_memory.py │ │ ├── instr_numeric.py │ │ ├── instr_parametric.py │ │ ├── instr_variable.py │ │ ├── instructions.py │ │ ├── vm.py │ │ ├── vm_global.py │ │ ├── vm_memory.py │ │ ├── vm_stack_control.py │ │ └── vm_stack_operand.py ├── ch08 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── opcodes.py │ │ ├── opnames.py │ │ ├── reader.py │ │ └── types.py │ ├── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ └── main.py │ └── interpreter │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instr_control.py │ │ ├── instr_memory.py │ │ ├── instr_numeric.py │ │ ├── instr_parametric.py │ │ ├── instr_variable.py │ │ ├── instructions.py │ │ ├── vm.py │ │ ├── vm_global.py │ │ ├── vm_memory.py │ │ ├── vm_stack_control.py │ │ └── vm_stack_operand.py ├── ch09 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── opcodes.py │ │ ├── opnames.py │ │ ├── reader.py │ │ └── types.py │ ├── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ └── main.py │ └── interpreter │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instr_control.py │ │ ├── instr_memory.py │ │ ├── instr_numeric.py │ │ ├── instr_parametric.py │ │ ├── instr_variable.py │ │ ├── instructions.py │ │ ├── native.py │ │ ├── val.py │ │ ├── vm.py │ │ ├── vm_func.py │ │ ├── vm_global.py │ │ ├── vm_memory.py │ │ ├── vm_stack_control.py │ │ ├── vm_stack_operand.py │ │ └── vm_table.py ├── ch10 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── opcodes.py │ │ ├── opnames.py │ │ ├── reader.py │ │ └── types.py │ ├── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ ├── main.py │ │ └── native.py │ ├── instance │ │ ├── __init__.py │ │ ├── module.py │ │ ├── native_function.py │ │ ├── native_module.py │ │ └── sig_parser.py │ └── interpreter │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instr_control.py │ │ ├── instr_memory.py │ │ ├── instr_numeric.py │ │ ├── instr_parametric.py │ │ ├── instr_variable.py │ │ ├── instructions.py │ │ ├── val.py │ │ ├── vm.py │ │ ├── vm_func.py │ │ ├── vm_global.py │ │ ├── vm_memory.py │ │ ├── vm_stack_control.py │ │ ├── vm_stack_operand.py │ │ └── vm_table.py ├── ch11 │ ├── binary │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instruction.py │ │ ├── leb128.py │ │ ├── module.py │ │ ├── opcodes.py │ │ ├── opnames.py │ │ ├── reader.py │ │ └── types.py │ ├── cmd │ │ ├── __init__.py │ │ ├── dumper.py │ │ ├── main.py │ │ └── native.py │ ├── instance │ │ ├── __init__.py │ │ ├── module.py │ │ ├── native_function.py │ │ ├── native_module.py │ │ └── sig_parser.py │ ├── interpreter │ │ ├── __init__.py │ │ ├── errors.py │ │ ├── instr_control.py │ │ ├── instr_memory.py │ │ ├── instr_numeric.py │ │ ├── instr_parametric.py │ │ ├── instr_variable.py │ │ ├── instructions.py │ │ ├── val.py │ │ ├── vm.py │ │ ├── vm_func.py │ │ ├── vm_global.py │ │ ├── vm_memory.py │ │ ├── vm_stack_control.py │ │ ├── vm_stack_operand.py │ │ └── vm_table.py │ └── validator │ │ ├── __init__.py │ │ ├── code_validator.py │ │ └── module_validator.py └── develop_code │ ├── binary │ ├── __init__.py │ ├── errors.py │ ├── instruction.py │ ├── leb128.py │ ├── module.py │ ├── opcodes.py │ ├── opnames.py │ ├── reader.py │ └── types.py │ ├── cmd │ ├── __init__.py │ ├── dumper.py │ ├── main.py │ └── test_env.py │ ├── instance │ ├── __init__.py │ ├── module.py │ ├── native_function.py │ ├── native_module.py │ └── sig_parser.py │ ├── interpreter │ ├── __init__.py │ ├── errors.py │ ├── instr_control.py │ ├── instr_memory.py │ ├── instr_numeric.py │ ├── instr_parametric.py │ ├── instr_variable.py │ ├── instructions.py │ ├── val.py │ ├── vm.py │ ├── vm_func.py │ ├── vm_global.py │ ├── vm_memory.py │ ├── vm_stack_control.py │ ├── vm_stack_operand.py │ └── vm_table.py │ ├── spectest │ ├── __init__.py │ ├── native.py │ ├── wasm_impl.py │ └── wast_tester.py │ ├── test │ ├── __init__.py │ ├── binary │ │ ├── leb128_test.py │ │ ├── module_test.py │ │ ├── reader_test.py │ │ └── types_test.py │ ├── instance │ │ └── sig_parser_test.py │ ├── interpreter │ │ ├── __init__.py │ │ ├── instr_memory_test.py │ │ ├── instr_numeric_test.py │ │ ├── instr_variable_test.py │ │ └── vm_test.py │ ├── testdata │ │ ├── err_2mem.wat │ │ ├── err_2start.wat │ │ ├── err_2table.wat │ │ ├── err_br.wat │ │ ├── err_import.wat │ │ ├── err_name_redef.wat │ │ ├── err_name_undef.wat │ │ ├── err_type.wat │ │ ├── err_var.wat │ │ └── hw_rust.wasm │ └── text │ │ ├── compiler_test.py │ │ └── num_parser_test.py │ ├── text │ ├── __init__.py │ ├── builder_code.py │ ├── builder_instr.py │ ├── builder_module.py │ ├── builder_script.py │ ├── builder_symbols.py │ ├── compiler.py │ ├── error_listener.py │ ├── error_reporter.py │ ├── errors.py │ ├── grammar │ │ ├── WAST.g4 │ │ ├── WAT.g4 │ │ ├── pygrun │ │ └── pygrun.bat │ ├── num_parser.py │ ├── parser │ │ ├── WAST.interp │ │ ├── WAST.tokens │ │ ├── WASTLexer.interp │ │ ├── WASTLexer.py │ │ ├── WASTLexer.tokens │ │ ├── WASTParser.py │ │ ├── WASTVisitor.py │ │ └── __init__.py │ ├── str_escaper.py │ ├── visitor_utils.py │ ├── visitor_wast.py │ ├── visitor_wat.py │ ├── visitor_wat_names.py │ └── wast_script.py │ └── validator │ ├── __init__.py │ ├── code_validator.py │ └── module_validator.py └── wat ├── ch03_eg1_num.wat ├── ch03_eg2_var.wat ├── ch03_eg3_mem.wat ├── ch03_eg4_block.wasm ├── ch03_eg4_block.wat ├── ch03_eg5_br.wat ├── ch03_eg6_call.wat ├── ch05_cz.wat ├── ch05_num.wasm ├── ch05_num.wat ├── ch05_param.wasm ├── ch05_param.wat ├── ch06_mem.wasm ├── ch06_mem.wat ├── ch07_fib.wasm ├── ch07_fib.wat ├── ch07_global.wat ├── ch07_local.wat ├── ch07_max.wat ├── ch07_sum.wat ├── ch08_cmp.wat ├── ch08_eg1_labels.wat ├── ch08_eg2_nested_labels.wat ├── ch08_eg3_label_names.wat ├── ch08_eg4_add.wat ├── ch08_eg5_calc.wat ├── ch08_eg6_br.wat ├── ch08_eg7_br_if.wat ├── ch08_eg8_br_table.wat ├── ch08_eg9_return.wat ├── ch08_fac.wasm ├── ch08_fac.wat ├── ch08_sum.wat ├── ch08_test.wat ├── ch09_calc.wasm ├── ch09_calc.wat ├── ch10_assert.wat ├── ch13_eg1_param.wat ├── ch13_eg2_var.wat ├── ch13_eg3_mem.wat ├── ch13_eg4_num.wat ├── ch13_eg5_block.wat ├── ch13_eg6_loop.wat ├── ch13_eg7_if.wat ├── ch13_eg8_br_table.wat ├── ch13_eg9_call.wat ├── ch13_egA_call_indirect.wat ├── ch13_hw.wat ├── ch13_mv.wat ├── ch14_annot.wat ├── ch14_bulk_mem.wat ├── ch14_bulk_table.wat ├── ch14_ex.wat ├── ch14_multi_mem.wat ├── ch14_multi_table.wat ├── ch14_ref_type.wat ├── ch14_simd.wat ├── ch14_table_op.wat ├── ch14_tail_call.wat └── ch14_threads.wat /images/ch02/ch02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch02/ch02.png -------------------------------------------------------------------------------- /images/ch03/ch03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch03/ch03.png -------------------------------------------------------------------------------- /images/ch05/ch05_param.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch05/ch05_param.png -------------------------------------------------------------------------------- /images/ch06/ch06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch06/ch06.png -------------------------------------------------------------------------------- /images/ch07/ch07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch07/ch07.png -------------------------------------------------------------------------------- /images/ch08/ch08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch08/ch08.png -------------------------------------------------------------------------------- /images/ch09/ch09_测试HelloWorld程序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch09/ch09_测试HelloWorld程序.png -------------------------------------------------------------------------------- /images/ch09/ch09_测试calc程序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch09/ch09_测试calc程序.png -------------------------------------------------------------------------------- /images/ch10/ch10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch10/ch10.png -------------------------------------------------------------------------------- /images/ch11/ch11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/images/ch11/ch11.png -------------------------------------------------------------------------------- /js/ch01_hw.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello, World! 5 | 6 | 7 | 25 | 26 | -------------------------------------------------------------------------------- /js/ch01_hw.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/js/ch01_hw.wasm -------------------------------------------------------------------------------- /spec/test/core/.gitignore: -------------------------------------------------------------------------------- 1 | output -------------------------------------------------------------------------------- /spec/test/core/README.md: -------------------------------------------------------------------------------- 1 | This directory contains tests for the core WebAssembly semantics, as described in [Semantics.md](https://github.com/WebAssembly/design/blob/master/Semantics.md) and specified by the [spec interpreter](https://github.com/WebAssembly/spec/blob/master/interpreter). 2 | 3 | Tests are written in the [S-Expression script format](https://github.com/WebAssembly/spec/blob/master/interpreter/README.md#s-expression-syntax) defined by the interpreter. 4 | 5 | The test suite can be run with the spec interpreter as follows: 6 | ``` 7 | ./run.py --wasm 8 | ``` 9 | where the path points to the spec interpreter executable (or a tool that understands similar options). If the binary is in the working directory, this option can be omitted. 10 | 11 | In addition, the option `--js ` can be given to point to a stand-alone JavaScript interpreter supporting the WebAssembly API. If provided, all tests are also executed in JavaScript. 12 | -------------------------------------------------------------------------------- /spec/test/core/comments.wast: -------------------------------------------------------------------------------- 1 | ;; Test comment syntax 2 | 3 | ;;comment 4 | 5 | ;;;;;;;;;;; 6 | 7 | ;;comment 8 | 9 | ( ;;comment 10 | module;;comment 11 | );;comment 12 | 13 | ;;) 14 | ;;;) 15 | ;; ;) 16 | ;; (; 17 | 18 | (;;) 19 | 20 | (;comment;) 21 | 22 | (;;comment;) 23 | 24 | (;;;comment;) 25 | 26 | (;;;;;;;;;;;;;;) 27 | 28 | (;(((((((((( ;) 29 | 30 | (;)))))))))));) 31 | 32 | (;comment";) 33 | 34 | (;comment"";) 35 | 36 | (;comment""";) 37 | 38 | ;; ASCII 00-1F, 7F 39 | (; 40 | ;) 41 | 42 | (;Heiße Würstchen;) 43 | 44 | (;;) 45 | 46 | (;comment 47 | comment;) 48 | 49 | (;comment;) 50 | 51 | (;comment;)((;comment;) 52 | (;comment;)module(;comment;) 53 | (;comment;))(;comment;) 54 | 55 | (;comment(;nested;)comment;) 56 | 57 | (;comment 58 | (;nested 59 | ;)comment 60 | ;) 61 | 62 | (module 63 | (;comment(;nested(;further;)nested;)comment;) 64 | ) 65 | 66 | (;comment;;comment;) 67 | 68 | (;comment;;comment 69 | ;) 70 | 71 | (module 72 | (;comment;;comment(;nested;)comment;) 73 | ) -------------------------------------------------------------------------------- /spec/test/core/forward.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func $even (export "even") (param $n i32) (result i32) 3 | (if (result i32) (i32.eq (local.get $n) (i32.const 0)) 4 | (then (i32.const 1)) 5 | (else (call $odd (i32.sub (local.get $n) (i32.const 1)))) 6 | ) 7 | ) 8 | 9 | (func $odd (export "odd") (param $n i32) (result i32) 10 | (if (result i32) (i32.eq (local.get $n) (i32.const 0)) 11 | (then (i32.const 0)) 12 | (else (call $even (i32.sub (local.get $n) (i32.const 1)))) 13 | ) 14 | ) 15 | ) 16 | 17 | (assert_return (invoke "even" (i32.const 13)) (i32.const 0)) 18 | (assert_return (invoke "even" (i32.const 20)) (i32.const 1)) 19 | (assert_return (invoke "odd" (i32.const 13)) (i32.const 1)) 20 | (assert_return (invoke "odd" (i32.const 20)) (i32.const 0)) 21 | -------------------------------------------------------------------------------- /spec/test/core/inline-module.wast: -------------------------------------------------------------------------------- 1 | (func) (memory 0) (func (export "f")) 2 | -------------------------------------------------------------------------------- /spec/test/core/table.wast: -------------------------------------------------------------------------------- 1 | ;; Test table section structure 2 | 3 | (module (table 0 funcref)) 4 | (module (table 1 funcref)) 5 | (module (table 0 0 funcref)) 6 | (module (table 0 1 funcref)) 7 | (module (table 1 256 funcref)) 8 | (module (table 0 65536 funcref)) 9 | (module (table 0 0xffff_ffff funcref)) 10 | 11 | (assert_invalid (module (table 0 funcref) (table 0 funcref)) "multiple tables") 12 | (assert_invalid (module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) "multiple tables") 13 | 14 | (assert_invalid (module (elem (i32.const 0))) "unknown table") 15 | (assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table") 16 | 17 | 18 | (assert_invalid 19 | (module (table 1 0 funcref)) 20 | "size minimum must not be greater than maximum" 21 | ) 22 | (assert_invalid 23 | (module (table 0xffff_ffff 0 funcref)) 24 | "size minimum must not be greater than maximum" 25 | ) 26 | 27 | (assert_malformed 28 | (module quote "(table 0x1_0000_0000 funcref)") 29 | "i32 constant out of range" 30 | ) 31 | (assert_malformed 32 | (module quote "(table 0x1_0000_0000 0x1_0000_0000 funcref)") 33 | "i32 constant out of range" 34 | ) 35 | (assert_malformed 36 | (module quote "(table 0 0x1_0000_0000 funcref)") 37 | "i32 constant out of range" 38 | ) 39 | 40 | 41 | ;; Duplicate table identifiers 42 | 43 | (assert_malformed (module quote 44 | "(table $foo 1 funcref)" 45 | "(table $foo 1 funcref)") 46 | "duplicate table") 47 | (assert_malformed (module quote 48 | "(import \"\" \"\" (table $foo 1 funcref))" 49 | "(table $foo 1 funcref)") 50 | "duplicate table") 51 | (assert_malformed (module quote 52 | "(import \"\" \"\" (table $foo 1 funcref))" 53 | "(import \"\" \"\" (table $foo 1 funcref))") 54 | "duplicate table") 55 | -------------------------------------------------------------------------------- /spec/test/core/token.wast: -------------------------------------------------------------------------------- 1 | ;; Test tokenization 2 | 3 | (assert_malformed 4 | (module quote "(func (drop (i32.const0)))") 5 | "unknown operator" 6 | ) 7 | (assert_malformed 8 | (module quote "(func br 0drop)") 9 | "unknown operator" 10 | ) 11 | -------------------------------------------------------------------------------- /spec/test/core/type.wast: -------------------------------------------------------------------------------- 1 | ;; Test type definitions 2 | 3 | (module 4 | (type (func)) 5 | (type $t (func)) 6 | 7 | (type (func (param i32))) 8 | (type (func (param $x i32))) 9 | (type (func (result i32))) 10 | (type (func (param i32) (result i32))) 11 | (type (func (param $x i32) (result i32))) 12 | 13 | (type (func (param f32 f64))) 14 | (type (func (result i64 f32))) 15 | (type (func (param i32 i64) (result f32 f64))) 16 | 17 | (type (func (param f32) (param f64))) 18 | (type (func (param $x f32) (param f64))) 19 | (type (func (param f32) (param $y f64))) 20 | (type (func (param $x f32) (param $y f64))) 21 | (type (func (result i64) (result f32))) 22 | (type (func (param i32) (param i64) (result f32) (result f64))) 23 | (type (func (param $x i32) (param $y i64) (result f32) (result f64))) 24 | 25 | (type (func (param f32 f64) (param $x i32) (param f64 i32 i32))) 26 | (type (func (result i64 i64 f32) (result f32 i32))) 27 | (type 28 | (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)) 29 | ) 30 | 31 | (type (func (param) (param $x f32) (param) (param) (param f64 i32) (param))) 32 | (type 33 | (func (result) (result) (result i64 i64) (result) (result f32) (result)) 34 | ) 35 | (type 36 | (func 37 | (param i32 i32) (param i64 i32) (param) (param $x i32) (param) 38 | (result) (result f32 f64) (result f64 i32) (result) 39 | ) 40 | ) 41 | ) 42 | 43 | (assert_malformed 44 | (module quote "(type (func (result i32) (param i32)))") 45 | "result before parameter" 46 | ) 47 | (assert_malformed 48 | (module quote "(type (func (result $x i32)))") 49 | "unexpected token" 50 | ) 51 | -------------------------------------------------------------------------------- /src/ch02/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch02.binary import reader 11 | 12 | decode_file = reader.decode_file -------------------------------------------------------------------------------- /src/ch02/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch02/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | 12 | class Expr: 13 | pass 14 | -------------------------------------------------------------------------------- /src/ch02/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch02.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch02/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/ch02/cmd/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: Main.py 6 | @time: 2020/8/18 16:23 7 | @desc: 主函数 8 | """ 9 | import os 10 | from optparse import OptionParser 11 | 12 | from ch02 import binary 13 | from ch02.cmd.dumper import dump 14 | 15 | 16 | def main(input_args): 17 | # 设置传入参数 18 | parser = OptionParser(usage="usage:%prog [-d] filename") 19 | 20 | parser.add_option("-d", "--dump", action="store_true", default=False, dest="dump_flag", 21 | help="dump Wasm file.") 22 | # 解析参数 23 | (options, args) = parser.parse_args(input_args) 24 | module, err = binary.decode_file(args[0]) 25 | 26 | if options.dump_flag: 27 | dump(module) 28 | 29 | 30 | if __name__ == '__main__': 31 | # 打印帮助 32 | # fake_args = ['-h'] 33 | # main(fake_args) 34 | 35 | # 使用输入参数测试 36 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 37 | file_name = os.path.join(os.path.dirname(root_path), "..\\js", "ch01_hw.wasm") 38 | fake_args = ['-d', file_name] 39 | print("main.py", *fake_args, end='\n\n') 40 | main(fake_args) 41 | -------------------------------------------------------------------------------- /src/ch03/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch03.binary import reader 11 | 12 | decode_file = reader.decode_file 13 | -------------------------------------------------------------------------------- /src/ch03/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch03/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch03.binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self): 27 | # 操作码 28 | self.opcode = None 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | self.bt = None 48 | # 内嵌的指令序列 49 | self.instrs = [] 50 | 51 | 52 | class IfArgs: 53 | """if指令的参数""" 54 | 55 | def __init__(self): 56 | # block type 57 | self.bt = None 58 | self.instrs1 = [] 59 | self.instrs2 = [] 60 | 61 | 62 | class BrTableArgs: 63 | """br_table指令的参数""" 64 | 65 | def __init__(self, labels=None, default=None): 66 | # 跳转表 67 | if labels is None: 68 | labels = [] 69 | self.labels = labels 70 | # 默认跳转标签 71 | self.default = default 72 | 73 | 74 | class MemArg: 75 | """内存指令参数""" 76 | 77 | def __init__(self, align=0, offset=0): 78 | # 对齐提示 79 | self.align = align 80 | # 内存偏移量 81 | self.offset = offset 82 | -------------------------------------------------------------------------------- /src/ch03/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch03.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch03/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/ch03/cmd/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: Main.py 6 | @time: 2020/8/18 16:23 7 | @desc: 主函数 8 | """ 9 | import os 10 | from optparse import OptionParser 11 | 12 | from ch03 import binary 13 | from ch03.cmd.dumper import dump 14 | 15 | 16 | def main(input_args): 17 | # 设置传入参数 18 | parser = OptionParser(usage="usage:%prog [-d] filename") 19 | 20 | parser.add_option("-d", "--dump", action="store_true", default=False, dest="dump_flag", 21 | help="dump Wasm file.") 22 | # 解析参数 23 | (options, args) = parser.parse_args(input_args) 24 | module, err = binary.decode_file(args[0]) 25 | 26 | if options.dump_flag: 27 | dump(module) 28 | 29 | if err is not None: 30 | raise err 31 | 32 | 33 | if __name__ == '__main__': 34 | # 打印帮助 35 | # fake_args = ['-h'] 36 | # main(fake_args) 37 | 38 | # 使用输入参数测试 39 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 40 | file_name = os.path.join(os.path.dirname(root_path), "..\\wat", "ch03_eg4_block.wasm") 41 | fake_args = ['-d', file_name] 42 | print("main.py", *fake_args, end='\n\n') 43 | main(fake_args) 44 | -------------------------------------------------------------------------------- /src/ch05/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch05.binary import reader 11 | 12 | decode_file = reader.decode_file -------------------------------------------------------------------------------- /src/ch05/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch05/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch05.binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self): 27 | # 操作码 28 | self.opcode = None 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | self.bt = None 48 | # 内嵌的指令序列 49 | self.instrs = [] 50 | 51 | 52 | class IfArgs: 53 | """if指令的参数""" 54 | 55 | def __init__(self): 56 | # block type 57 | self.bt = None 58 | self.instrs1 = [] 59 | self.instrs2 = [] 60 | 61 | 62 | class BrTableArgs: 63 | """br_table指令的参数""" 64 | 65 | def __init__(self, labels=None, default=None): 66 | # 跳转表 67 | if labels is None: 68 | labels = [] 69 | self.labels = labels 70 | # 默认跳转标签 71 | self.default = default 72 | 73 | 74 | class MemArg: 75 | """内存指令参数""" 76 | 77 | def __init__(self, align=0, offset=0): 78 | # 对齐提示 79 | self.align = align 80 | # 内存偏移量 81 | self.offset = offset 82 | -------------------------------------------------------------------------------- /src/ch05/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch05.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch05/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/ch05/cmd/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: Main.py 6 | @time: 2020/8/18 16:23 7 | @desc: 主函数 8 | """ 9 | import os 10 | from optparse import OptionParser 11 | 12 | from ch05 import binary 13 | from ch05.cmd.dumper import dump 14 | from ch05.interpreter.vm import exec_main_func 15 | 16 | 17 | def main(input_args): 18 | # 设置传入参数 19 | parser = OptionParser(usage="usage:%prog [-d] filename") 20 | 21 | parser.add_option("-d", "--dump", action="store_true", default=False, dest="dump_flag", 22 | help="dump Wasm file.") 23 | # 解析参数 24 | (options, args) = parser.parse_args(input_args) 25 | module, err = binary.decode_file(args[0]) 26 | 27 | if err is not None: 28 | raise err 29 | 30 | if options.dump_flag: 31 | dump(module) 32 | else: 33 | exec_main_func(module) 34 | 35 | 36 | if __name__ == '__main__': 37 | # 打印帮助 38 | # fake_args = ['-h'] 39 | # main(fake_args) 40 | 41 | # 使用输入参数测试 42 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 43 | file_name = os.path.join(os.path.dirname(root_path), "..\\wat", "ch05_param.wasm") 44 | # file_name = os.path.join(os.path.dirname(root_path), "../wat", "ch05_num.wasm") 45 | fake_args = [file_name] 46 | print("main.py", *fake_args, end='\n\n') 47 | main(fake_args) 48 | -------------------------------------------------------------------------------- /src/ch05/interpreter/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/19 21:07 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import ctypes 12 | 13 | 14 | class int8(int): 15 | def __new__(cls, val): 16 | val = ctypes.c_int8(val).value 17 | return super().__new__(cls, val) 18 | 19 | 20 | class int16(int): 21 | def __new__(cls, val): 22 | val = ctypes.c_int16(val).value 23 | return super().__new__(cls, val) 24 | 25 | 26 | class int32(int): 27 | def __new__(cls, val): 28 | val = ctypes.c_int32(val).value 29 | return super().__new__(cls, val) 30 | 31 | 32 | class int64(int): 33 | def __new__(cls, val): 34 | val = ctypes.c_int64(val).value 35 | return super().__new__(cls, val) 36 | 37 | 38 | class uint32(int): 39 | def __new__(cls, val): 40 | val = ctypes.c_uint32(val).value 41 | return super().__new__(cls, val) 42 | 43 | 44 | class uint64(int): 45 | def __new__(cls, val): 46 | val = ctypes.c_uint64(val).value 47 | return super().__new__(cls, val) 48 | 49 | 50 | class float32(float): 51 | def __new__(cls, val): 52 | val = ctypes.c_float(val).value 53 | return super().__new__(cls, val) 54 | 55 | 56 | class float64(float): 57 | def __new__(cls, val): 58 | val = ctypes.c_double(val).value 59 | return super().__new__(cls, val) 60 | -------------------------------------------------------------------------------- /src/ch05/interpreter/instr_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_control.py 6 | @time: 2020/8/20 17:31 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | 12 | # hack! 13 | from ch05.interpreter import uint32 14 | 15 | 16 | def call(vm, args): 17 | idx = uint32(args) 18 | name = vm.module.import_sec[idx].name 19 | if name == 'assert_true': 20 | __assert_equal(vm.pop_bool(), True) 21 | elif name == 'assert_false': 22 | __assert_equal(vm.pop_bool(), False) 23 | elif name == 'assert_eq_i32': 24 | __assert_equal(vm.pop_u32(), vm.pop_u32()) 25 | elif name == 'assert_eq_i64': 26 | __assert_equal(vm.pop_u64(), vm.pop_u64()) 27 | elif name == 'assert_eq_f32': 28 | __assert_equal(vm.pop_f32(), vm.pop_f32()) 29 | elif name == 'assert_eq_f64': 30 | __assert_equal(vm.pop_f64(), vm.pop_f64()) 31 | else: 32 | raise Exception("TODO") 33 | 34 | 35 | def __assert_equal(a, b): 36 | if a != b: 37 | raise Exception("{} != {}".format(a, b)) 38 | -------------------------------------------------------------------------------- /src/ch05/interpreter/instr_parametric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_parametric.py 6 | @time: 2020/8/19 21:48 7 | @project: wasm-python-book 8 | @desc: 参数指令 9 | """ 10 | 11 | 12 | def drop(vm, _): 13 | """drop指令(操作码0x1A)从栈顶弹出一个操作数并把它扔掉""" 14 | vm.pop_u64() 15 | 16 | 17 | def _select(vm, _): 18 | """ 19 | select指令(操作码0x1B)从栈顶弹出3个操作数, 20 | 然后根据最先弹出的操作数从其他两个操作数中“选择”一个压栈 21 | """ 22 | v3 = vm.pop_bool() 23 | v2 = vm.pop_u64() 24 | v1 = vm.pop_u64() 25 | 26 | if v3: 27 | vm.push_u64(v1) 28 | else: 29 | vm.push_u64(v2) 30 | -------------------------------------------------------------------------------- /src/ch05/interpreter/vm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm.py 6 | @time: 2020/8/19 21:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch05.binary.module import Module 11 | from ch05.interpreter.instructions import instr_table 12 | from ch05.interpreter.vm_stack_operand import OperandStack 13 | 14 | 15 | class VM(OperandStack): 16 | def __init__(self, module=None): 17 | super().__init__() 18 | if module is None: 19 | module = Module() 20 | self.module = module 21 | 22 | def exec_code(self, idx): 23 | """一条一条执行函数指令""" 24 | code = self.module.code_sec[idx] 25 | for _, instr in enumerate(code.expr): 26 | self.exec_instr(instr) 27 | 28 | def exec_instr(self, instr): 29 | """指令分派逻辑:采用查表法""" 30 | instr_table[instr.opcode](self, instr.args) 31 | 32 | 33 | def exec_main_func(module): 34 | idx = int(module.start_sec) - len(module.import_sec) 35 | vm = VM(module) 36 | vm.exec_code(idx) 37 | -------------------------------------------------------------------------------- /src/ch06/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch06.binary import reader 11 | 12 | decode_file = reader.decode_file -------------------------------------------------------------------------------- /src/ch06/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch06/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch06.binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self): 27 | # 操作码 28 | self.opcode = None 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | self.bt = None 48 | # 内嵌的指令序列 49 | self.instrs = [] 50 | 51 | 52 | class IfArgs: 53 | """if指令的参数""" 54 | 55 | def __init__(self): 56 | # block type 57 | self.bt = None 58 | self.instrs1 = [] 59 | self.instrs2 = [] 60 | 61 | 62 | class BrTableArgs: 63 | """br_table指令的参数""" 64 | 65 | def __init__(self, labels=None, default=None): 66 | # 跳转表 67 | if labels is None: 68 | labels = [] 69 | self.labels = labels 70 | # 默认跳转标签 71 | self.default = default 72 | 73 | 74 | class MemArg: 75 | """内存指令参数""" 76 | 77 | def __init__(self, align=0, offset=0): 78 | # 对齐提示 79 | self.align = align 80 | # 内存偏移量 81 | self.offset = offset 82 | -------------------------------------------------------------------------------- /src/ch06/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch06.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch06/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/ch06/cmd/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: Main.py 6 | @time: 2020/8/18 16:23 7 | @desc: 主函数 8 | """ 9 | import os 10 | from optparse import OptionParser 11 | 12 | from ch06 import binary 13 | from ch06.cmd.dumper import dump 14 | from ch06.interpreter.vm import exec_main_func 15 | 16 | 17 | def main(input_args): 18 | # 设置传入参数 19 | parser = OptionParser(usage="usage:%prog [-d] filename") 20 | 21 | parser.add_option("-d", "--dump", action="store_true", default=False, dest="dump_flag", 22 | help="dump Wasm file.") 23 | # 解析参数 24 | (options, args) = parser.parse_args(input_args) 25 | module, err = binary.decode_file(args[0]) 26 | 27 | if err is not None: 28 | raise err 29 | 30 | if options.dump_flag: 31 | dump(module) 32 | else: 33 | exec_main_func(module) 34 | 35 | 36 | if __name__ == '__main__': 37 | # 打印帮助 38 | # fake_args = ['-h'] 39 | # main(fake_args) 40 | 41 | # 使用输入参数测试 42 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 43 | file_name = os.path.join(os.path.dirname(root_path), "..\\wat", "ch06_mem.wasm") 44 | fake_args = [file_name] 45 | print("main.py", *fake_args, end='\n\n') 46 | main(fake_args) 47 | -------------------------------------------------------------------------------- /src/ch06/interpreter/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/19 21:07 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import ctypes 12 | 13 | 14 | class int8(int): 15 | def __new__(cls, val): 16 | val = ctypes.c_int8(val).value 17 | return super().__new__(cls, val) 18 | 19 | 20 | class int16(int): 21 | def __new__(cls, val): 22 | val = ctypes.c_int16(val).value 23 | return super().__new__(cls, val) 24 | 25 | 26 | class int32(int): 27 | def __new__(cls, val): 28 | val = ctypes.c_int32(val).value 29 | return super().__new__(cls, val) 30 | 31 | 32 | class int64(int): 33 | def __new__(cls, val): 34 | val = ctypes.c_int64(val).value 35 | return super().__new__(cls, val) 36 | 37 | 38 | class uint16(int): 39 | def __new__(cls, val): 40 | val = ctypes.c_uint16(val).value 41 | return super().__new__(cls, val) 42 | 43 | 44 | class uint32(int): 45 | def __new__(cls, val): 46 | val = ctypes.c_uint32(val).value 47 | return super().__new__(cls, val) 48 | 49 | 50 | class uint64(int): 51 | def __new__(cls, val): 52 | val = ctypes.c_uint64(val).value 53 | return super().__new__(cls, val) 54 | 55 | 56 | class float32(float): 57 | def __new__(cls, val): 58 | val = ctypes.c_float(val).value 59 | return super().__new__(cls, val) 60 | 61 | 62 | class float64(float): 63 | def __new__(cls, val): 64 | val = ctypes.c_double(val).value 65 | return super().__new__(cls, val) 66 | -------------------------------------------------------------------------------- /src/ch06/interpreter/instr_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_control.py 6 | @time: 2020/8/20 17:31 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | 12 | # hack! 13 | from ch06.interpreter import uint32 14 | 15 | 16 | def call(vm, args): 17 | idx = uint32(args) 18 | name = vm.module.import_sec[idx].name 19 | if name == 'assert_true': 20 | __assert_equal(vm.pop_bool(), True) 21 | elif name == 'assert_false': 22 | __assert_equal(vm.pop_bool(), False) 23 | elif name == 'assert_eq_i32': 24 | __assert_equal(vm.pop_u32(), vm.pop_u32()) 25 | elif name == 'assert_eq_i64': 26 | __assert_equal(vm.pop_u64(), vm.pop_u64()) 27 | elif name == 'assert_eq_f32': 28 | __assert_equal(vm.pop_f32(), vm.pop_f32()) 29 | elif name == 'assert_eq_f64': 30 | __assert_equal(vm.pop_f64(), vm.pop_f64()) 31 | else: 32 | raise Exception("TODO") 33 | 34 | 35 | def __assert_equal(a, b): 36 | if a != b: 37 | raise Exception("{} != {}".format(a, b)) 38 | -------------------------------------------------------------------------------- /src/ch06/interpreter/instr_parametric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_parametric.py 6 | @time: 2020/8/19 21:48 7 | @project: wasm-python-book 8 | @desc: 参数指令 9 | """ 10 | 11 | 12 | def drop(vm, _): 13 | """drop指令(操作码0x1A)从栈顶弹出一个操作数并把它扔掉""" 14 | vm.pop_u64() 15 | 16 | 17 | def _select(vm, _): 18 | """ 19 | select指令(操作码0x1B)从栈顶弹出3个操作数, 20 | 然后根据最先弹出的操作数从其他两个操作数中“选择”一个压栈 21 | """ 22 | v3 = vm.pop_bool() 23 | v2 = vm.pop_u64() 24 | v1 = vm.pop_u64() 25 | 26 | if v3: 27 | vm.push_u64(v1) 28 | else: 29 | vm.push_u64(v2) 30 | -------------------------------------------------------------------------------- /src/ch06/interpreter/vm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm.py 6 | @time: 2020/8/19 21:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch06.binary.module import Module 11 | from ch06.interpreter.instructions import instr_table 12 | from ch06.interpreter.vm_memory import Memory 13 | from ch06.interpreter.vm_stack_operand import OperandStack 14 | 15 | 16 | class VM(OperandStack): 17 | def __init__(self, module=None, memory=None): 18 | super().__init__() 19 | if module is None: 20 | module = Module() 21 | self.module = module 22 | self.memory = memory 23 | 24 | def init_mem(self): 25 | """ 26 | 内存初始化 27 | Wasm模块可以导入或者定义一块内存,还有一个数据段专门用来存放内存初始化数据 28 | """ 29 | # 如果模块定义了内存,就先创建内存实例并分配必要的内存页 30 | if len(self.module.mem_sec) > 0: 31 | self.memory = Memory(self.module.mem_sec[0]) 32 | 33 | for data in self.module.data_sec: 34 | for instr in data.offset: 35 | self.exec_instr(instr) 36 | 37 | # 指令执行完毕后,留在操作数栈顶的就是内存起始地址 38 | self.memory.write(self.pop_u64(), data.init) 39 | 40 | def exec_code(self, idx): 41 | """一条一条执行函数指令""" 42 | code = self.module.code_sec[idx] 43 | for _, instr in enumerate(code.expr): 44 | self.exec_instr(instr) 45 | 46 | def exec_instr(self, instr): 47 | """指令分派逻辑:采用查表法""" 48 | instr_table[instr.opcode](self, instr.args) 49 | 50 | 51 | def exec_main_func(module): 52 | idx = int(module.start_sec) - len(module.import_sec) 53 | vm = VM(module) 54 | vm.init_mem() 55 | vm.exec_code(idx) 56 | -------------------------------------------------------------------------------- /src/ch06/interpreter/vm_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_memory.py 6 | @time: 2020/8/21 16:01 7 | @project: wasm-python-book 8 | @desc: 内存实现 9 | """ 10 | from ch06.binary.module import PageSize, MaxPageCount 11 | from ch06.interpreter.errors import ErrMemOutOfBounds 12 | 13 | 14 | class Memory: 15 | def __init__(self, mem_type=None): 16 | self.type = mem_type 17 | # 内存初始页分配 18 | self.data = [0x00] * mem_type.min * PageSize 19 | 20 | @property 21 | def size(self): 22 | return int(len(self.data) / PageSize) 23 | 24 | def grow(self, n): 25 | old_size = self.size 26 | if n == 0: 27 | return old_size 28 | 29 | # 检查页数,防止超出限制 30 | max = self.type.max 31 | max_page_count = max if max > 0 else MaxPageCount 32 | if old_size + n > max_page_count: 33 | return -1 34 | 35 | self.data.extend([0x00] * n * PageSize) 36 | return old_size 37 | 38 | def read(self, offset, buf): 39 | self.check_offset(offset, len(buf)) 40 | buf = self.data[offset:(offset + len(buf))] 41 | return buf 42 | 43 | def write(self, offset, data): 44 | self.check_offset(offset, len(data)) 45 | self.data[offset:(offset + len(data))] = data 46 | 47 | def check_offset(self, offset, length): 48 | """检查边界""" 49 | if len(self.data) - length < offset: 50 | raise ErrMemOutOfBounds 51 | -------------------------------------------------------------------------------- /src/ch07/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch07.binary import reader 11 | 12 | decode_file = reader.decode_file -------------------------------------------------------------------------------- /src/ch07/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch07/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch07.binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self): 27 | # 操作码 28 | self.opcode = None 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | self.bt = None 48 | # 内嵌的指令序列 49 | self.instrs = [] 50 | 51 | 52 | class IfArgs: 53 | """if指令的参数""" 54 | 55 | def __init__(self): 56 | # block type 57 | self.bt = None 58 | self.instrs1 = [] 59 | self.instrs2 = [] 60 | 61 | 62 | class BrTableArgs: 63 | """br_table指令的参数""" 64 | 65 | def __init__(self, labels=None, default=None): 66 | # 跳转表 67 | if labels is None: 68 | labels = [] 69 | self.labels = labels 70 | # 默认跳转标签 71 | self.default = default 72 | 73 | 74 | class MemArg: 75 | """内存指令参数""" 76 | 77 | def __init__(self, align=0, offset=0): 78 | # 对齐提示 79 | self.align = align 80 | # 内存偏移量 81 | self.offset = offset 82 | -------------------------------------------------------------------------------- /src/ch07/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch07.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch07/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/ch07/cmd/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: Main.py 6 | @time: 2020/8/18 16:23 7 | @desc: 主函数 8 | """ 9 | import os 10 | from optparse import OptionParser 11 | 12 | from ch07 import binary 13 | from ch07.cmd.dumper import dump 14 | from ch07.interpreter.vm import exec_main_func 15 | 16 | 17 | def main(input_args): 18 | # 设置传入参数 19 | parser = OptionParser(usage="usage:%prog [-d] filename") 20 | 21 | parser.add_option("-d", "--dump", action="store_true", default=False, dest="dump_flag", 22 | help="dump Wasm file.") 23 | # 解析参数 24 | (options, args) = parser.parse_args(input_args) 25 | module, err = binary.decode_file(args[0]) 26 | 27 | if err is not None: 28 | raise err 29 | 30 | if options.dump_flag: 31 | dump(module) 32 | else: 33 | exec_main_func(module) 34 | 35 | 36 | if __name__ == '__main__': 37 | # 打印帮助 38 | # fake_args = ['-h'] 39 | # main(fake_args) 40 | 41 | # 使用输入参数测试 42 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 43 | file_name = os.path.join(os.path.dirname(root_path), "..\\wat", "ch07_fib.wasm") 44 | fake_args = [file_name] 45 | print("main.py", *fake_args, end='\n\n') 46 | main(fake_args) 47 | -------------------------------------------------------------------------------- /src/ch07/interpreter/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/19 21:07 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import ctypes 12 | 13 | 14 | class int8(int): 15 | def __new__(cls, val): 16 | val = ctypes.c_int8(val).value 17 | return super().__new__(cls, val) 18 | 19 | 20 | class int16(int): 21 | def __new__(cls, val): 22 | val = ctypes.c_int16(val).value 23 | return super().__new__(cls, val) 24 | 25 | 26 | class int32(int): 27 | def __new__(cls, val): 28 | val = ctypes.c_int32(val).value 29 | return super().__new__(cls, val) 30 | 31 | 32 | class int64(int): 33 | def __new__(cls, val): 34 | val = ctypes.c_int64(val).value 35 | return super().__new__(cls, val) 36 | 37 | 38 | class uint16(int): 39 | def __new__(cls, val): 40 | val = ctypes.c_uint16(val).value 41 | return super().__new__(cls, val) 42 | 43 | 44 | class uint32(int): 45 | def __new__(cls, val): 46 | val = ctypes.c_uint32(val).value 47 | return super().__new__(cls, val) 48 | 49 | 50 | class uint64(int): 51 | def __new__(cls, val): 52 | val = ctypes.c_uint64(val).value 53 | return super().__new__(cls, val) 54 | 55 | 56 | class float32(float): 57 | def __new__(cls, val): 58 | val = ctypes.c_float(val).value 59 | return super().__new__(cls, val) 60 | 61 | 62 | class float64(float): 63 | def __new__(cls, val): 64 | val = ctypes.c_double(val).value 65 | return super().__new__(cls, val) 66 | -------------------------------------------------------------------------------- /src/ch07/interpreter/instr_parametric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_parametric.py 6 | @time: 2020/8/19 21:48 7 | @project: wasm-python-book 8 | @desc: 参数指令 9 | """ 10 | 11 | 12 | def drop(vm, _): 13 | """drop指令(操作码0x1A)从栈顶弹出一个操作数并把它扔掉""" 14 | vm.pop_u64() 15 | 16 | 17 | def _select(vm, _): 18 | """ 19 | select指令(操作码0x1B)从栈顶弹出3个操作数, 20 | 然后根据最先弹出的操作数从其他两个操作数中“选择”一个压栈 21 | """ 22 | v3 = vm.pop_bool() 23 | v2 = vm.pop_u64() 24 | v1 = vm.pop_u64() 25 | 26 | if v3: 27 | vm.push_u64(v1) 28 | else: 29 | vm.push_u64(v2) 30 | -------------------------------------------------------------------------------- /src/ch07/interpreter/instr_variable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_variable.py 6 | @time: 2020/8/23 11:35 7 | @project: wasm-python-book 8 | @desc: 局部/全局变量指令 9 | """ 10 | from ch07.interpreter import uint32 11 | 12 | 13 | def local_get(vm, args): 14 | """获取局部变量""" 15 | idx = uint32(args) 16 | val = vm.get_operand(vm.local_0_idx + idx) 17 | vm.push_u64(val) 18 | 19 | 20 | def local_set(vm, args): 21 | """设置局部变量的值""" 22 | idx = uint32(args) 23 | val = vm.pop_u64() 24 | vm.set_operand(vm.local_0_idx + idx, val) 25 | 26 | 27 | def local_tee(vm, args): 28 | """用重定向操作符>把某个命令的输出重定向到文件里""" 29 | idx = uint32(args) 30 | val = vm.pop_u64() 31 | vm.push_u64(val) 32 | vm.set_operand(vm.local_0_idx + idx, val) 33 | 34 | 35 | def global_get(vm, args): 36 | idx = uint32(args) 37 | val = vm.globals[idx].get_as_u64() 38 | vm.push_u64(val) 39 | 40 | 41 | def global_set(vm, args): 42 | idx = uint32(args) 43 | val = vm.pop_u64() 44 | vm.globals[idx].set_as_u64(val) 45 | -------------------------------------------------------------------------------- /src/ch07/interpreter/vm_global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_global.py 6 | @time: 2020/8/23 10:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch07.interpreter.errors import ErrImmutableGlobal 11 | 12 | 13 | class GlobalVar: 14 | def __init__(self, gt, val): 15 | self.type = gt 16 | self.val = val 17 | 18 | def get_as_u64(self): 19 | return self.val 20 | 21 | def set_as_u64(self, val): 22 | if self.type.mut != 1: 23 | raise ErrImmutableGlobal 24 | self.val = val 25 | -------------------------------------------------------------------------------- /src/ch07/interpreter/vm_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_memory.py 6 | @time: 2020/8/21 16:01 7 | @project: wasm-python-book 8 | @desc: 内存实现 9 | """ 10 | from ch07.binary.module import PageSize, MaxPageCount 11 | from ch07.interpreter.errors import ErrMemOutOfBounds 12 | 13 | 14 | class Memory: 15 | def __init__(self, mem_type=None): 16 | self.type = mem_type 17 | # 内存初始页分配 18 | self.data = [0x00] * mem_type.min * PageSize 19 | 20 | @property 21 | def size(self): 22 | return int(len(self.data) / PageSize) 23 | 24 | def grow(self, n): 25 | old_size = self.size 26 | if n == 0: 27 | return old_size 28 | 29 | # 检查页数,防止超出限制 30 | max = self.type.max 31 | max_page_count = max if max > 0 else MaxPageCount 32 | if old_size + n > max_page_count: 33 | return -1 34 | 35 | self.data.extend([0x00] * n * PageSize) 36 | return old_size 37 | 38 | def read(self, offset, buf): 39 | self.check_offset(offset, len(buf)) 40 | buf = self.data[offset:(offset + len(buf))] 41 | return buf 42 | 43 | def write(self, offset, data): 44 | self.check_offset(offset, len(data)) 45 | self.data[offset:(offset + len(data))] = data 46 | 47 | def check_offset(self, offset, length): 48 | """检查边界""" 49 | if len(self.data) - length < offset: 50 | raise ErrMemOutOfBounds 51 | -------------------------------------------------------------------------------- /src/ch07/interpreter/vm_stack_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_stack_control.py 6 | @time: 2020/8/23 10:41 7 | @project: wasm-python-book 8 | @desc: 控制帧 9 | """ 10 | from ch07.binary.opcodes import Call 11 | 12 | 13 | class ControlFrame: 14 | """控制帧""" 15 | 16 | def __init__(self, opcode, bt, instrs, bp): 17 | self.opcode = opcode 18 | self.bt = bt 19 | self.instrs = instrs 20 | self.bp = bp 21 | # 程序计数器,用于记录指令执行的位置 22 | self.pc = 0 23 | 24 | 25 | class ControlStack: 26 | def __init__(self): 27 | self.frames = [] 28 | 29 | @property 30 | def control_depth(self): 31 | return len(self.frames) 32 | 33 | @property 34 | def top_control_frame(self): 35 | return self.frames[-1] 36 | 37 | def top_call_frame(self): 38 | n = len(self.frames) - 1 39 | while n >= 0: 40 | cf = self.frames[n] 41 | if cf.opcode == Call: 42 | return cf, len(self.frames) - 1 - n 43 | n -= 1 44 | return None, -1 45 | 46 | def push_control_frame(self, cf): 47 | self.frames.append(cf) 48 | 49 | def pop_control_frame(self): 50 | return self.frames.pop() 51 | -------------------------------------------------------------------------------- /src/ch08/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch08.binary import reader 11 | 12 | decode_file = reader.decode_file 13 | -------------------------------------------------------------------------------- /src/ch08/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch08/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch08.binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self): 27 | # 操作码 28 | self.opcode = None 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | self.bt = None 48 | # 内嵌的指令序列 49 | self.instrs = [] 50 | 51 | 52 | class IfArgs: 53 | """if指令的参数""" 54 | 55 | def __init__(self): 56 | # block type 57 | self.bt = None 58 | self.instrs1 = [] 59 | self.instrs2 = [] 60 | 61 | 62 | class BrTableArgs: 63 | """br_table指令的参数""" 64 | 65 | def __init__(self, labels=None, default=None): 66 | # 跳转表 67 | if labels is None: 68 | labels = [] 69 | self.labels = labels 70 | # 默认跳转标签 71 | self.default = default 72 | 73 | 74 | class MemArg: 75 | """内存指令参数""" 76 | 77 | def __init__(self, align=0, offset=0): 78 | # 对齐提示 79 | self.align = align 80 | # 内存偏移量 81 | self.offset = offset 82 | -------------------------------------------------------------------------------- /src/ch08/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch08.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch08/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/ch08/cmd/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: Main.py 6 | @time: 2020/8/18 16:23 7 | @desc: 主函数 8 | """ 9 | import os 10 | from optparse import OptionParser 11 | 12 | from ch08 import binary 13 | from ch08.cmd.dumper import dump 14 | from ch08.interpreter.vm import exec_main_func 15 | 16 | 17 | def main(input_args): 18 | # 设置传入参数 19 | parser = OptionParser(usage="usage:%prog [-d] filename") 20 | 21 | parser.add_option("-d", "--dump", action="store_true", default=False, dest="dump_flag", 22 | help="dump Wasm file.") 23 | parser.add_option("--verbose", action="store_true", default=False, dest="verbose_flag", 24 | help="enable verbose output") 25 | # 解析参数 26 | (options, args) = parser.parse_args(input_args) 27 | module, err = binary.decode_file(args[0]) 28 | 29 | if err is not None: 30 | raise err 31 | 32 | if options.dump_flag: 33 | dump(module) 34 | else: 35 | exec_main_func(module, options.verbose_flag) 36 | 37 | 38 | if __name__ == '__main__': 39 | # 打印帮助 40 | # fake_args = ['-h'] 41 | # main(fake_args) 42 | 43 | # 使用输入参数测试 44 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 45 | file_name = os.path.join(os.path.dirname(root_path), "..\\wat", "ch08_fac.wasm") 46 | fake_args = ["--verbose", file_name] 47 | print("main.py", *fake_args, end='\n\n') 48 | main(fake_args) 49 | -------------------------------------------------------------------------------- /src/ch08/interpreter/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/19 21:07 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import ctypes 12 | 13 | 14 | class int8(int): 15 | def __new__(cls, val): 16 | val = ctypes.c_int8(val).value 17 | return super().__new__(cls, val) 18 | 19 | 20 | class int16(int): 21 | def __new__(cls, val): 22 | val = ctypes.c_int16(val).value 23 | return super().__new__(cls, val) 24 | 25 | 26 | class int32(int): 27 | def __new__(cls, val): 28 | val = ctypes.c_int32(val).value 29 | return super().__new__(cls, val) 30 | 31 | 32 | class int64(int): 33 | def __new__(cls, val): 34 | val = ctypes.c_int64(val).value 35 | return super().__new__(cls, val) 36 | 37 | 38 | class uint16(int): 39 | def __new__(cls, val): 40 | val = ctypes.c_uint16(val).value 41 | return super().__new__(cls, val) 42 | 43 | 44 | class uint32(int): 45 | def __new__(cls, val): 46 | val = ctypes.c_uint32(val).value 47 | return super().__new__(cls, val) 48 | 49 | 50 | class uint64(int): 51 | def __new__(cls, val): 52 | val = ctypes.c_uint64(val).value 53 | return super().__new__(cls, val) 54 | 55 | 56 | class float32(float): 57 | def __new__(cls, val): 58 | val = ctypes.c_float(val).value 59 | return super().__new__(cls, val) 60 | 61 | 62 | class float64(float): 63 | def __new__(cls, val): 64 | val = ctypes.c_double(val).value 65 | return super().__new__(cls, val) 66 | -------------------------------------------------------------------------------- /src/ch08/interpreter/instr_parametric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_parametric.py 6 | @time: 2020/8/19 21:48 7 | @project: wasm-python-book 8 | @desc: 参数指令 9 | """ 10 | 11 | 12 | def drop(vm, _): 13 | """drop指令(操作码0x1A)从栈顶弹出一个操作数并把它扔掉""" 14 | vm.pop_u64() 15 | 16 | 17 | def _select(vm, _): 18 | """ 19 | select指令(操作码0x1B)从栈顶弹出3个操作数, 20 | 然后根据最先弹出的操作数从其他两个操作数中“选择”一个压栈 21 | """ 22 | v3 = vm.pop_bool() 23 | v2 = vm.pop_u64() 24 | v1 = vm.pop_u64() 25 | 26 | if v3: 27 | vm.push_u64(v1) 28 | else: 29 | vm.push_u64(v2) 30 | -------------------------------------------------------------------------------- /src/ch08/interpreter/instr_variable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_variable.py 6 | @time: 2020/8/23 11:35 7 | @project: wasm-python-book 8 | @desc: 局部变量指令 9 | """ 10 | from ch08.interpreter import uint32 11 | 12 | 13 | def local_get(vm, args): 14 | """获取局部变量""" 15 | idx = uint32(args) 16 | val = vm.get_operand(vm.local_0_idx + idx) 17 | vm.push_u64(val) 18 | 19 | 20 | def local_set(vm, args): 21 | """设置局部变量的值""" 22 | idx = uint32(args) 23 | val = vm.pop_u64() 24 | vm.set_operand(vm.local_0_idx + idx, val) 25 | 26 | 27 | def local_tee(vm, args): 28 | """用重定向操作符>把某个命令的输出重定向到文件里""" 29 | idx = uint32(args) 30 | val = vm.pop_u64() 31 | vm.push_u64(val) 32 | vm.set_operand(vm.local_0_idx + idx, val) 33 | 34 | 35 | def global_get(vm, args): 36 | idx = uint32(args) 37 | val = vm.globals[idx].get_as_u64() 38 | vm.push_u64(val) 39 | 40 | 41 | def global_set(vm, args): 42 | idx = uint32(args) 43 | val = vm.pop_u64() 44 | vm.globals[idx].set_as_u64(val) 45 | -------------------------------------------------------------------------------- /src/ch08/interpreter/vm_global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_global.py 6 | @time: 2020/8/23 10:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch08.interpreter.errors import ErrImmutableGlobal 11 | 12 | 13 | class GlobalVar: 14 | def __init__(self, gt, val): 15 | self.type = gt 16 | self.val = val 17 | 18 | def get_as_u64(self): 19 | return self.val 20 | 21 | def set_as_u64(self, val): 22 | if self.type.mut != 1: 23 | raise ErrImmutableGlobal 24 | self.val = val 25 | -------------------------------------------------------------------------------- /src/ch08/interpreter/vm_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_memory.py 6 | @time: 2020/8/21 16:01 7 | @project: wasm-python-book 8 | @desc: 内存实现 9 | """ 10 | from ch08.binary.module import PageSize, MaxPageCount 11 | from ch08.interpreter.errors import ErrMemOutOfBounds 12 | 13 | 14 | class Memory: 15 | def __init__(self, mem_type=None): 16 | self.type = mem_type 17 | # 内存初始页分配 18 | self.data = [0x00] * mem_type.min * PageSize 19 | 20 | @property 21 | def size(self): 22 | return int(len(self.data) / PageSize) 23 | 24 | def grow(self, n): 25 | old_size = self.size 26 | if n == 0: 27 | return old_size 28 | 29 | # 检查页数,防止超出限制 30 | max = self.type.max 31 | max_page_count = max if max > 0 else MaxPageCount 32 | if old_size + n > max_page_count: 33 | return -1 34 | 35 | self.data.extend([0x00] * n * PageSize) 36 | return old_size 37 | 38 | def read(self, offset, buf): 39 | self.check_offset(offset, len(buf)) 40 | buf = self.data[offset:(offset + len(buf))] 41 | return buf 42 | 43 | def write(self, offset, data): 44 | self.check_offset(offset, len(data)) 45 | self.data[offset:(offset + len(data))] = data 46 | 47 | def check_offset(self, offset, length): 48 | """检查边界""" 49 | if len(self.data) - length < offset: 50 | raise ErrMemOutOfBounds 51 | -------------------------------------------------------------------------------- /src/ch08/interpreter/vm_stack_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_stack_control.py 6 | @time: 2020/8/23 10:41 7 | @project: wasm-python-book 8 | @desc: 控制帧 9 | """ 10 | from ch08.binary.opcodes import Call 11 | 12 | 13 | class ControlFrame: 14 | """控制帧""" 15 | 16 | def __init__(self, opcode, bt, instrs, bp): 17 | self.opcode = opcode 18 | self.bt = bt 19 | self.instrs = instrs 20 | self.bp = bp 21 | # 程序计数器,用于记录指令执行的位置 22 | self.pc = 0 23 | 24 | 25 | class ControlStack: 26 | def __init__(self): 27 | self.frames = [] 28 | 29 | @property 30 | def control_depth(self): 31 | return len(self.frames) 32 | 33 | @property 34 | def top_control_frame(self): 35 | return self.frames[-1] 36 | 37 | def top_call_frame(self): 38 | n = len(self.frames) - 1 39 | while n >= 0: 40 | cf = self.frames[n] 41 | if cf.opcode == Call: 42 | return cf, len(self.frames) - 1 - n 43 | n -= 1 44 | return None, -1 45 | 46 | def push_control_frame(self, cf): 47 | self.frames.append(cf) 48 | 49 | def pop_control_frame(self): 50 | return self.frames.pop() 51 | -------------------------------------------------------------------------------- /src/ch09/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch09.binary import reader 11 | 12 | decode_file = reader.decode_file -------------------------------------------------------------------------------- /src/ch09/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch09/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch09.binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self): 27 | # 操作码 28 | self.opcode = None 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | self.bt = None 48 | # 内嵌的指令序列 49 | self.instrs = [] 50 | 51 | 52 | class IfArgs: 53 | """if指令的参数""" 54 | 55 | def __init__(self): 56 | # block type 57 | self.bt = None 58 | self.instrs1 = [] 59 | self.instrs2 = [] 60 | 61 | 62 | class BrTableArgs: 63 | """br_table指令的参数""" 64 | 65 | def __init__(self, labels=None, default=None): 66 | # 跳转表 67 | if labels is None: 68 | labels = [] 69 | self.labels = labels 70 | # 默认跳转标签 71 | self.default = default 72 | 73 | 74 | class MemArg: 75 | """内存指令参数""" 76 | 77 | def __init__(self, align=0, offset=0): 78 | # 对齐提示 79 | self.align = align 80 | # 内存偏移量 81 | self.offset = offset 82 | -------------------------------------------------------------------------------- /src/ch09/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch09.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch09/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/ch09/cmd/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: Main.py 6 | @time: 2020/8/18 16:23 7 | @desc: 主函数 8 | """ 9 | import os 10 | from optparse import OptionParser 11 | 12 | from ch09 import binary 13 | from ch09.cmd.dumper import dump 14 | from ch09.interpreter.vm import exec_main_func 15 | 16 | 17 | def main(input_args): 18 | # 设置传入参数 19 | parser = OptionParser(usage="usage:%prog [-d] filename") 20 | 21 | parser.add_option("-d", "--dump", action="store_true", default=False, dest="dump_flag", 22 | help="dump Wasm file.") 23 | parser.add_option("--verbose", action="store_true", default=False, dest="verbose_flag", 24 | help="enable verbose output") 25 | # 解析参数 26 | (options, args) = parser.parse_args(input_args) 27 | module, err = binary.decode_file(args[0]) 28 | 29 | if err is not None: 30 | raise err 31 | 32 | if options.dump_flag: 33 | dump(module) 34 | else: 35 | exec_main_func(module, options.verbose_flag) 36 | 37 | 38 | if __name__ == '__main__': 39 | # 打印帮助 40 | # fake_args = ['-h'] 41 | # main(fake_args) 42 | 43 | # 使用输入参数测试 44 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 45 | 46 | # 测试Hello, World! 47 | # file_name = os.path.join(os.path.dirname(root_path), "..\\js", "ch01_hw.wasm") 48 | # fake_args = [file_name] 49 | 50 | # 测试ch09_calc 51 | file_name = os.path.join(os.path.dirname(root_path), "..\\wat", "ch09_calc.wasm") 52 | fake_args = ["--verbose", file_name] 53 | print("main.py", *fake_args, end='\n\n') 54 | main(fake_args) 55 | -------------------------------------------------------------------------------- /src/ch09/interpreter/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/19 21:07 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import ctypes 12 | 13 | 14 | class int8(int): 15 | def __new__(cls, val): 16 | val = ctypes.c_int8(val).value 17 | return super().__new__(cls, val) 18 | 19 | 20 | class int16(int): 21 | def __new__(cls, val): 22 | val = ctypes.c_int16(val).value 23 | return super().__new__(cls, val) 24 | 25 | 26 | class int32(int): 27 | def __new__(cls, val): 28 | val = ctypes.c_int32(val).value 29 | return super().__new__(cls, val) 30 | 31 | 32 | class int64(int): 33 | def __new__(cls, val): 34 | val = ctypes.c_int64(val).value 35 | return super().__new__(cls, val) 36 | 37 | 38 | class uint16(int): 39 | def __new__(cls, val): 40 | val = ctypes.c_uint16(val).value 41 | return super().__new__(cls, val) 42 | 43 | 44 | class uint32(int): 45 | def __new__(cls, val): 46 | val = ctypes.c_uint32(val).value 47 | return super().__new__(cls, val) 48 | 49 | 50 | class uint64(int): 51 | def __new__(cls, val): 52 | val = ctypes.c_uint64(val).value 53 | return super().__new__(cls, val) 54 | 55 | 56 | class float32(float): 57 | def __new__(cls, val): 58 | val = ctypes.c_float(val).value 59 | return super().__new__(cls, val) 60 | 61 | 62 | class float64(float): 63 | def __new__(cls, val): 64 | val = ctypes.c_double(val).value 65 | return super().__new__(cls, val) 66 | -------------------------------------------------------------------------------- /src/ch09/interpreter/instr_parametric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_parametric.py 6 | @time: 2020/8/19 21:48 7 | @project: wasm-python-book 8 | @desc: 参数指令 9 | """ 10 | 11 | 12 | def drop(vm, _): 13 | """drop指令(操作码0x1A)从栈顶弹出一个操作数并把它扔掉""" 14 | vm.pop_u64() 15 | 16 | 17 | def _select(vm, _): 18 | """ 19 | select指令(操作码0x1B)从栈顶弹出3个操作数, 20 | 然后根据最先弹出的操作数从其他两个操作数中“选择”一个压栈 21 | """ 22 | v3 = vm.pop_bool() 23 | v2 = vm.pop_u64() 24 | v1 = vm.pop_u64() 25 | 26 | if v3: 27 | vm.push_u64(v1) 28 | else: 29 | vm.push_u64(v2) 30 | -------------------------------------------------------------------------------- /src/ch09/interpreter/instr_variable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_variable.py 6 | @time: 2020/8/23 11:35 7 | @project: wasm-python-book 8 | @desc: 局部变量指令 9 | """ 10 | from ch09.interpreter import uint32 11 | 12 | 13 | def local_get(vm, args): 14 | """获取局部变量""" 15 | idx = uint32(args) 16 | val = vm.get_operand(vm.local_0_idx + idx) 17 | vm.push_u64(val) 18 | 19 | 20 | def local_set(vm, args): 21 | """设置局部变量的值""" 22 | idx = uint32(args) 23 | val = vm.pop_u64() 24 | vm.set_operand(vm.local_0_idx + idx, val) 25 | 26 | 27 | def local_tee(vm, args): 28 | """用重定向操作符>把某个命令的输出重定向到文件里""" 29 | idx = uint32(args) 30 | val = vm.pop_u64() 31 | vm.push_u64(val) 32 | vm.set_operand(vm.local_0_idx + idx, val) 33 | 34 | 35 | def global_get(vm, args): 36 | idx = uint32(args) 37 | val = vm.globals[idx].get_as_u64() 38 | vm.push_u64(val) 39 | 40 | 41 | def global_set(vm, args): 42 | idx = uint32(args) 43 | val = vm.pop_u64() 44 | vm.globals[idx].set_as_u64(val) 45 | -------------------------------------------------------------------------------- /src/ch09/interpreter/native.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native.py 6 | @time: 2020/8/23 17:33 7 | @project: wasm-python-book 8 | @desc: 本地方法 9 | """ 10 | from ch09.interpreter import * 11 | 12 | 13 | def print_char(args): 14 | print("%c" % int(args[0]), end='') 15 | return None 16 | 17 | 18 | def assert_true(args): 19 | __assert_equal(int32(args[0]), int32(1)) 20 | return None 21 | 22 | 23 | def assert_false(args): 24 | __assert_equal(int32(args[0]), int32(0)) 25 | return None 26 | 27 | 28 | def assert_eq_i32(args): 29 | __assert_equal(int32(args[0]), int32(args[1])) 30 | return None 31 | 32 | 33 | def assert_eq_i64(args): 34 | __assert_equal(int64(args[0]), int64(args[1])) 35 | return None 36 | 37 | 38 | def assert_eq_f32(args): 39 | __assert_equal(float32(args[0]), float32(args[1])) 40 | return None 41 | 42 | 43 | def assert_eq_f64(args): 44 | __assert_equal(float64(args[0]), float64(args[1])) 45 | return None 46 | 47 | 48 | def __assert_equal(a, b): 49 | if a != b: 50 | raise Exception("{} != {}".format(a, b)) 51 | -------------------------------------------------------------------------------- /src/ch09/interpreter/val.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: val.py 6 | @time: 2020/8/23 17:06 7 | @project: wasm-python-book 8 | @desc: 参数和返回值的包装/解包 9 | """ 10 | import math 11 | import struct 12 | 13 | from ch09.binary.types import ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64 14 | from ch09.interpreter import int32, int64, float32, float64, uint64, uint32 15 | 16 | 17 | def wrap_u64(vt, val): 18 | if vt == ValTypeI32: 19 | return int32(val) 20 | elif vt == ValTypeI64: 21 | return int64(val) 22 | elif vt == ValTypeF32: 23 | cov_val = struct.unpack('>f', struct.pack('>l', int64(val)))[0] 24 | if math.isnan(cov_val): 25 | return float32(val) 26 | else: 27 | return float32(cov_val) 28 | elif vt == ValTypeF64: 29 | cov_val = struct.unpack('>d', struct.pack('>q', int64(val)))[0] 30 | if math.isnan(cov_val): 31 | return float64(val) 32 | else: 33 | return float64(cov_val) 34 | else: 35 | raise Exception("unreachable") 36 | 37 | 38 | def unwrap_u64(vt, val): 39 | if vt == ValTypeI32: 40 | return uint64(val) 41 | elif vt == ValTypeI64: 42 | return uint64(val) 43 | elif vt == ValTypeF32: 44 | return uint64(val) 45 | elif vt == ValTypeF64: 46 | return uint64(val) 47 | else: 48 | raise Exception("unreachable") 49 | -------------------------------------------------------------------------------- /src/ch09/interpreter/vm_func.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_func.py 6 | @time: 2020/8/23 16:47 7 | @project: wasm-python-book 8 | @desc: 函数方法 9 | """ 10 | 11 | 12 | class VMFunc: 13 | def __init__(self, ft=None, code=None, pf=None): 14 | self.type = ft 15 | self.code = code 16 | self.py_func = pf 17 | 18 | 19 | def new_external_func(ft, pf): 20 | return VMFunc(ft=ft, pf=pf) 21 | 22 | 23 | def new_internal_func(ft, code): 24 | return VMFunc(ft=ft, code=code) 25 | -------------------------------------------------------------------------------- /src/ch09/interpreter/vm_global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_global.py 6 | @time: 2020/8/23 10:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch09.interpreter.errors import ErrImmutableGlobal 11 | 12 | 13 | class GlobalVar: 14 | def __init__(self, gt, val): 15 | self.type = gt 16 | self.val = val 17 | 18 | def get_as_u64(self): 19 | return self.val 20 | 21 | def set_as_u64(self, val): 22 | if self.type.mut != 1: 23 | raise ErrImmutableGlobal 24 | self.val = val 25 | -------------------------------------------------------------------------------- /src/ch09/interpreter/vm_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_memory.py 6 | @time: 2020/8/21 16:01 7 | @project: wasm-python-book 8 | @desc: 内存实现 9 | """ 10 | from ch09.binary.module import PageSize, MaxPageCount 11 | from ch09.interpreter.errors import ErrMemOutOfBounds 12 | 13 | 14 | class Memory: 15 | def __init__(self, mem_type=None): 16 | self.type = mem_type 17 | # 内存初始页分配 18 | self.data = [0x00] * mem_type.min * PageSize 19 | 20 | @property 21 | def size(self): 22 | return int(len(self.data) / PageSize) 23 | 24 | def grow(self, n): 25 | old_size = self.size 26 | if n == 0: 27 | return old_size 28 | 29 | # 检查页数,防止超出限制 30 | max = self.type.max 31 | max_page_count = max if max > 0 else MaxPageCount 32 | if old_size + n > max_page_count: 33 | return -1 34 | 35 | self.data.extend([0x00] * n * PageSize) 36 | return old_size 37 | 38 | def read(self, offset, buf): 39 | self.check_offset(offset, len(buf)) 40 | buf = self.data[offset:(offset + len(buf))] 41 | return buf 42 | 43 | def write(self, offset, data): 44 | self.check_offset(offset, len(data)) 45 | self.data[offset:(offset + len(data))] = data 46 | 47 | def check_offset(self, offset, length): 48 | """检查边界""" 49 | if len(self.data) - length < offset: 50 | raise ErrMemOutOfBounds 51 | -------------------------------------------------------------------------------- /src/ch09/interpreter/vm_stack_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_stack_control.py 6 | @time: 2020/8/23 10:41 7 | @project: wasm-python-book 8 | @desc: 控制帧 9 | """ 10 | from ch09.binary.opcodes import Call 11 | 12 | 13 | class ControlFrame: 14 | """控制帧""" 15 | 16 | def __init__(self, opcode, bt, instrs, bp): 17 | self.opcode = opcode 18 | self.bt = bt 19 | self.instrs = instrs 20 | self.bp = bp 21 | # 程序计数器,用于记录指令执行的位置 22 | self.pc = 0 23 | 24 | 25 | class ControlStack: 26 | def __init__(self): 27 | self.frames = [] 28 | 29 | @property 30 | def control_depth(self): 31 | return len(self.frames) 32 | 33 | @property 34 | def top_control_frame(self): 35 | return self.frames[-1] 36 | 37 | def top_call_frame(self): 38 | n = len(self.frames) - 1 39 | while n >= 0: 40 | cf = self.frames[n] 41 | if cf.opcode == Call: 42 | return cf, len(self.frames) - 1 - n 43 | n -= 1 44 | return None, -1 45 | 46 | def push_control_frame(self, cf): 47 | self.frames.append(cf) 48 | 49 | def pop_control_frame(self): 50 | return self.frames.pop() 51 | -------------------------------------------------------------------------------- /src/ch09/interpreter/vm_table.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_table.py 6 | @time: 2020/8/23 17:16 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch09.interpreter.errors import ErrUndefinedElem 11 | 12 | 13 | class Table: 14 | def __init__(self, tt=None): 15 | self.type = tt 16 | self.elems = [None] * tt.limits.min 17 | 18 | @property 19 | def size(self): 20 | return len(self.elems) 21 | 22 | def grow(self, n): 23 | self.elems.extend([None] * n) 24 | 25 | def get_elem(self, idx): 26 | self.check_idx(idx) 27 | elem = self.elems[idx] 28 | return elem 29 | 30 | def set_elem(self, idx, elem): 31 | self.check_idx(idx) 32 | self.elems[idx] = elem 33 | 34 | def check_idx(self, idx): 35 | if idx >= len(self.elems): 36 | raise ErrUndefinedElem 37 | -------------------------------------------------------------------------------- /src/ch10/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch10.binary import reader 11 | 12 | decode_file = reader.decode_file 13 | -------------------------------------------------------------------------------- /src/ch10/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch10/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch10.binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self): 27 | # 操作码 28 | self.opcode = None 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | self.bt = None 48 | # 内嵌的指令序列 49 | self.instrs = [] 50 | 51 | 52 | class IfArgs: 53 | """if指令的参数""" 54 | 55 | def __init__(self): 56 | # block type 57 | self.bt = None 58 | self.instrs1 = [] 59 | self.instrs2 = [] 60 | 61 | 62 | class BrTableArgs: 63 | """br_table指令的参数""" 64 | 65 | def __init__(self, labels=None, default=None): 66 | # 跳转表 67 | if labels is None: 68 | labels = [] 69 | self.labels = labels 70 | # 默认跳转标签 71 | self.default = default 72 | 73 | 74 | class MemArg: 75 | """内存指令参数""" 76 | 77 | def __init__(self, align=0, offset=0): 78 | # 对齐提示 79 | self.align = align 80 | # 内存偏移量 81 | self.offset = offset 82 | -------------------------------------------------------------------------------- /src/ch10/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch10.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch10/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/ch10/cmd/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: Main.py 6 | @time: 2020/8/18 16:23 7 | @desc: 主函数 8 | """ 9 | import os 10 | from optparse import OptionParser 11 | 12 | from ch10 import binary 13 | from ch10.cmd.dumper import dump 14 | from ch10.cmd.native import new_env 15 | 16 | 17 | def main(input_args): 18 | # 设置传入参数 19 | parser = OptionParser(usage="usage:%prog [-d] filename") 20 | 21 | parser.add_option("-d", "--dump", action="store_true", default=False, dest="dump_flag", 22 | help="dump Wasm file.") 23 | parser.add_option("--verbose", action="store_true", default=False, dest="verbose_flag", 24 | help="enable verbose output") 25 | # 解析参数 26 | (options, args) = parser.parse_args(input_args) 27 | module, err = binary.decode_file(args[0]) 28 | 29 | if err is not None: 30 | raise err 31 | 32 | if options.dump_flag: 33 | dump(module) 34 | else: 35 | instantiate_and_exec_main_func(module) 36 | 37 | 38 | def instantiate_and_exec_main_func(module): 39 | from ch10.interpreter.vm import new 40 | 41 | mm = dict({"env": new_env()}) 42 | m, err = new(module, mm) 43 | if err is None: 44 | _, err = m.invoke_func("main") 45 | if err is not None: 46 | raise err 47 | 48 | 49 | if __name__ == '__main__': 50 | # 打印帮助 51 | # fake_args = ['-h'] 52 | # main(fake_args) 53 | 54 | # 使用输入参数测试 55 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 56 | 57 | file_name = os.path.join(os.path.dirname(root_path), "..\\js", "ch01_hw.wasm") 58 | fake_args = [file_name] 59 | print("main.py", *fake_args, end='\n\n') 60 | main(fake_args) 61 | -------------------------------------------------------------------------------- /src/ch10/cmd/native.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native.py 6 | @time: 2020/8/23 17:33 7 | @project: wasm-python-book 8 | @desc: 本地方法 9 | """ 10 | from ch10.instance.native_module import NativeModule 11 | from ch10.interpreter import * 12 | 13 | 14 | def new_env(): 15 | env = NativeModule() 16 | env.register_func("print_char(i32)->()", print_char) 17 | env.register_func("assert_true(i32)->()", assert_true) 18 | env.register_func("assert_false(i32)->()", assert_false) 19 | env.register_func("assert_eq_i32(i32,i32)->()", assert_eq_i32) 20 | env.register_func("assert_eq_i64(i64,i64)->()", assert_eq_i64) 21 | env.register_func("assert_eq_f32(f32,f32)->()", assert_eq_f32) 22 | env.register_func("assert_eq_f64(f64,f64)->()", assert_eq_f64) 23 | return env 24 | 25 | 26 | def print_char(args): 27 | print("%c" % int(args[0]), end='') 28 | return None, None 29 | 30 | 31 | def assert_true(args): 32 | __assert_equal(int32(args[0]), int32(1)) 33 | return None, None 34 | 35 | 36 | def assert_false(args): 37 | __assert_equal(int32(args[0]), int32(0)) 38 | return None, None 39 | 40 | 41 | def assert_eq_i32(args): 42 | __assert_equal(int32(args[0]), int32(args[1])) 43 | return None, None 44 | 45 | 46 | def assert_eq_i64(args): 47 | __assert_equal(int64(args[0]), int64(args[1])) 48 | return None, None 49 | 50 | 51 | def assert_eq_f32(args): 52 | __assert_equal(float32(args[0]), float32(args[1])) 53 | return None, None 54 | 55 | 56 | def assert_eq_f64(args): 57 | __assert_equal(float64(args[0]), float64(args[1])) 58 | return None, None 59 | 60 | 61 | def __assert_equal(a, b): 62 | if a != b: 63 | raise Exception("{} != {}".format(a, b)) 64 | -------------------------------------------------------------------------------- /src/ch10/instance/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/23 19:40 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/ch10/instance/module.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: module.py 6 | @time: 2020/8/23 19:43 7 | @project: wasm-python-book 8 | @desc: 模块实例 9 | """ 10 | from abc import ABCMeta, abstractmethod 11 | 12 | 13 | class Module(metaclass=ABCMeta): 14 | @abstractmethod 15 | def get_member(self, name): 16 | pass 17 | 18 | @abstractmethod 19 | def invoke_func(self, name, args): 20 | pass 21 | 22 | @abstractmethod 23 | def get_global_val(self, name): 24 | pass 25 | 26 | @abstractmethod 27 | def set_global_var(self, name, val): 28 | pass 29 | 30 | 31 | class Function(metaclass=ABCMeta): 32 | @abstractmethod 33 | def call(self, args): 34 | pass 35 | 36 | 37 | class Table(metaclass=ABCMeta): 38 | @abstractmethod 39 | def size(self): 40 | pass 41 | 42 | @abstractmethod 43 | def grow(self, n): 44 | pass 45 | 46 | @abstractmethod 47 | def get_elem(self, idx): 48 | pass 49 | 50 | @abstractmethod 51 | def set_elem(self, idx, elem): 52 | pass 53 | 54 | 55 | class Memory(metaclass=ABCMeta): 56 | @abstractmethod 57 | def size(self): 58 | pass 59 | 60 | @abstractmethod 61 | def grow(self, n): 62 | pass 63 | 64 | @abstractmethod 65 | def read(self, offset, buf): 66 | pass 67 | 68 | @abstractmethod 69 | def write(self, offset, buf): 70 | pass 71 | 72 | 73 | class Global(metaclass=ABCMeta): 74 | @abstractmethod 75 | def get_as_u64(self): 76 | pass 77 | 78 | @abstractmethod 79 | def set_as_u64(self, val): 80 | pass 81 | 82 | @abstractmethod 83 | def get(self): 84 | pass 85 | 86 | @abstractmethod 87 | def set(self, val): 88 | pass 89 | -------------------------------------------------------------------------------- /src/ch10/instance/native_function.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native_function.py 6 | @time: 2020/8/23 19:54 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch10.binary.types import FuncType 11 | from ch10.instance.module import Function 12 | 13 | 14 | class NativeFunction(Function): 15 | def __init__(self, t: FuncType = None, f=None): 16 | self.type = t 17 | self.f = f 18 | 19 | def call(self, args): 20 | return self.f(args) 21 | -------------------------------------------------------------------------------- /src/ch10/instance/native_module.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native_module.py 6 | @time: 2020/8/23 19:57 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch10.instance import module 11 | from ch10.instance.native_function import NativeFunction 12 | from ch10.instance.sig_parser import parse_name_and_sig 13 | 14 | 15 | class NativeModule(module.Module): 16 | def __init__(self): 17 | self.exported = dict() 18 | 19 | def register_func(self, name_and_sig, f): 20 | name, sig = parse_name_and_sig(name_and_sig) 21 | self.exported[name] = NativeFunction(t=sig, f=f) 22 | 23 | def register(self, name, x): 24 | self.exported[name] = x 25 | 26 | def get_member(self, name): 27 | return self.exported[name] 28 | 29 | def invoke_func(self, name, args): 30 | # TODO 31 | return self.exported[name].call(args) 32 | 33 | def get_global_val(self, name): 34 | # TODO 35 | return self.exported[name].get(), None 36 | 37 | def set_global_var(self, name, val): 38 | # TODO 39 | self.exported[name].set(val) 40 | return None 41 | -------------------------------------------------------------------------------- /src/ch10/instance/sig_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: sig_parser.py 6 | @time: 2020/8/23 19:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch10.binary.types import FuncType, ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64 11 | 12 | 13 | def parse_name_and_sig(name_and_sig: str): 14 | idx_of_lpar = name_and_sig.find('(') 15 | name = name_and_sig[:idx_of_lpar] 16 | sig = name_and_sig[idx_of_lpar:] 17 | return name, parse_sig(sig) 18 | 19 | 20 | def parse_sig(sig): 21 | params_and_results = sig.split('->') 22 | return FuncType(param_types=parse_val_types(params_and_results[0]), 23 | result_types=parse_val_types(params_and_results[1])) 24 | 25 | 26 | def parse_val_types(val_types_str: str): 27 | val_types_str = val_types_str.strip() 28 | # remove () 29 | val_types_str = val_types_str[1:-1] 30 | val_types = [] 31 | for t in val_types_str.split(','): 32 | t = t.strip() 33 | if t == 'i32': 34 | val_types.append(ValTypeI32) 35 | elif t == 'i64': 36 | val_types.append(ValTypeI64) 37 | elif t == 'f32': 38 | val_types.append(ValTypeF32) 39 | elif t == 'f64': 40 | val_types.append(ValTypeF64) 41 | 42 | return val_types 43 | -------------------------------------------------------------------------------- /src/ch10/interpreter/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/19 21:07 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import ctypes 12 | 13 | 14 | class int8(int): 15 | def __new__(cls, val): 16 | val = ctypes.c_int8(val).value 17 | return super().__new__(cls, val) 18 | 19 | 20 | class int16(int): 21 | def __new__(cls, val): 22 | val = ctypes.c_int16(val).value 23 | return super().__new__(cls, val) 24 | 25 | 26 | class int32(int): 27 | def __new__(cls, val): 28 | val = ctypes.c_int32(val).value 29 | return super().__new__(cls, val) 30 | 31 | 32 | class int64(int): 33 | def __new__(cls, val): 34 | val = ctypes.c_int64(val).value 35 | return super().__new__(cls, val) 36 | 37 | 38 | class uint16(int): 39 | def __new__(cls, val): 40 | val = ctypes.c_uint16(val).value 41 | return super().__new__(cls, val) 42 | 43 | 44 | class uint32(int): 45 | def __new__(cls, val): 46 | val = ctypes.c_uint32(val).value 47 | return super().__new__(cls, val) 48 | 49 | 50 | class uint64(int): 51 | def __new__(cls, val): 52 | val = ctypes.c_uint64(val).value 53 | return super().__new__(cls, val) 54 | 55 | 56 | class float32(float): 57 | def __new__(cls, val): 58 | val = ctypes.c_float(val).value 59 | return super().__new__(cls, val) 60 | 61 | 62 | class float64(float): 63 | def __new__(cls, val): 64 | val = ctypes.c_double(val).value 65 | return super().__new__(cls, val) 66 | -------------------------------------------------------------------------------- /src/ch10/interpreter/instr_parametric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_parametric.py 6 | @time: 2020/8/19 21:48 7 | @project: wasm-python-book 8 | @desc: 参数指令 9 | """ 10 | 11 | 12 | def drop(vm, _): 13 | """drop指令(操作码0x1A)从栈顶弹出一个操作数并把它扔掉""" 14 | vm.pop_u64() 15 | 16 | 17 | def _select(vm, _): 18 | """ 19 | select指令(操作码0x1B)从栈顶弹出3个操作数, 20 | 然后根据最先弹出的操作数从其他两个操作数中“选择”一个压栈 21 | """ 22 | v3 = vm.pop_bool() 23 | v2 = vm.pop_u64() 24 | v1 = vm.pop_u64() 25 | 26 | if v3: 27 | vm.push_u64(v1) 28 | else: 29 | vm.push_u64(v2) 30 | -------------------------------------------------------------------------------- /src/ch10/interpreter/instr_variable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_variable.py 6 | @time: 2020/8/23 11:35 7 | @project: wasm-python-book 8 | @desc: 局部变量指令 9 | """ 10 | from ch10.interpreter import uint32 11 | 12 | 13 | def local_get(vm, args): 14 | """获取局部变量""" 15 | idx = uint32(args) 16 | val = vm.get_operand(vm.local_0_idx + idx) 17 | vm.push_u64(val) 18 | 19 | 20 | def local_set(vm, args): 21 | """设置局部变量的值""" 22 | idx = uint32(args) 23 | val = vm.pop_u64() 24 | vm.set_operand(vm.local_0_idx + idx, val) 25 | 26 | 27 | def local_tee(vm, args): 28 | """用重定向操作符>把某个命令的输出重定向到文件里""" 29 | idx = uint32(args) 30 | val = vm.pop_u64() 31 | vm.push_u64(val) 32 | vm.set_operand(vm.local_0_idx + idx, val) 33 | 34 | 35 | def global_get(vm, args): 36 | idx = uint32(args) 37 | val = vm.globals[idx].get_as_u64() 38 | vm.push_u64(val) 39 | 40 | 41 | def global_set(vm, args): 42 | idx = uint32(args) 43 | val = vm.pop_u64() 44 | vm.globals[idx].set_as_u64(val) 45 | -------------------------------------------------------------------------------- /src/ch10/interpreter/val.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: val.py 6 | @time: 2020/8/23 17:06 7 | @project: wasm-python-book 8 | @desc: 参数和返回值的包装/解包 9 | """ 10 | import struct 11 | 12 | from ch10.binary.types import ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64 13 | from ch10.interpreter import int32, int64, float32, float64, uint64 14 | 15 | 16 | def wrap_u64(vt, val): 17 | if vt == ValTypeI32: 18 | return int32(val) 19 | elif vt == ValTypeI64: 20 | return int64(val) 21 | elif vt == ValTypeF32: 22 | val = struct.unpack('>f', struct.pack('>l', int64(val)))[0] 23 | return float32(val) 24 | elif vt == ValTypeF64: 25 | val = struct.unpack('>d', struct.pack('>q', int64(val)))[0] 26 | return float64(val) 27 | else: 28 | raise Exception("unreachable") 29 | 30 | 31 | def unwrap_u64(vt, val): 32 | if vt == ValTypeI32: 33 | return uint64(val) 34 | elif vt == ValTypeI64: 35 | return uint64(val) 36 | elif vt == ValTypeF32: 37 | return uint64(val) 38 | elif vt == ValTypeF64: 39 | return uint64(val) 40 | else: 41 | raise Exception("unreachable") 42 | -------------------------------------------------------------------------------- /src/ch10/interpreter/vm_global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_global.py 6 | @time: 2020/8/23 10:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch10.instance import module 11 | from ch10.interpreter.errors import ErrImmutableGlobal 12 | from ch10.interpreter.val import wrap_u64, unwrap_u64 13 | 14 | 15 | class GlobalVar(module.Global): 16 | def __init__(self, gt, val): 17 | self.type = gt 18 | self.val = val 19 | 20 | def get_as_u64(self): 21 | return self.val 22 | 23 | def set_as_u64(self, val): 24 | if self.type.mut != 1: 25 | raise ErrImmutableGlobal 26 | self.val = val 27 | 28 | def get(self): 29 | return wrap_u64(self.type.val_type, self.val) 30 | 31 | def set(self, val): 32 | self.val = unwrap_u64(self.type.val_type, val) 33 | -------------------------------------------------------------------------------- /src/ch10/interpreter/vm_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_memory.py 6 | @time: 2020/8/21 16:01 7 | @project: wasm-python-book 8 | @desc: 内存实现 9 | """ 10 | from ch10.binary.module import PageSize, MaxPageCount 11 | from ch10.interpreter.errors import ErrMemOutOfBounds 12 | 13 | 14 | class Memory: 15 | def __init__(self, mem_type=None): 16 | self.type = mem_type 17 | # 内存初始页分配 18 | self.data = [0x00] * mem_type.min * PageSize 19 | 20 | @property 21 | def size(self): 22 | return int(len(self.data) / PageSize) 23 | 24 | def grow(self, n): 25 | old_size = self.size 26 | if n == 0: 27 | return old_size 28 | 29 | # 检查页数,防止超出限制 30 | max = self.type.max 31 | max_page_count = max if max > 0 else MaxPageCount 32 | if old_size + n > max_page_count: 33 | return -1 34 | 35 | self.data.extend([0x00] * n * PageSize) 36 | return old_size 37 | 38 | def read(self, offset, buf): 39 | self.check_offset(offset, len(buf)) 40 | buf = self.data[offset:(offset + len(buf))] 41 | return buf 42 | 43 | def write(self, offset, data): 44 | self.check_offset(offset, len(data)) 45 | self.data[offset:(offset + len(data))] = data 46 | 47 | def check_offset(self, offset, length): 48 | """检查边界""" 49 | if len(self.data) - length < offset: 50 | raise ErrMemOutOfBounds 51 | -------------------------------------------------------------------------------- /src/ch10/interpreter/vm_stack_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_stack_control.py 6 | @time: 2020/8/23 10:41 7 | @project: wasm-python-book 8 | @desc: 控制帧 9 | """ 10 | from ch10.binary.opcodes import Call 11 | 12 | 13 | class ControlFrame: 14 | """控制帧""" 15 | 16 | def __init__(self, opcode, bt, instrs, bp): 17 | self.opcode = opcode 18 | self.bt = bt 19 | self.instrs = instrs 20 | self.bp = bp 21 | # 程序计数器,用于记录指令执行的位置 22 | self.pc = 0 23 | 24 | 25 | class ControlStack: 26 | def __init__(self): 27 | self.frames = [] 28 | 29 | @property 30 | def control_depth(self): 31 | return len(self.frames) 32 | 33 | @property 34 | def top_control_frame(self): 35 | return self.frames[-1] 36 | 37 | def top_call_frame(self): 38 | n = len(self.frames) - 1 39 | while n >= 0: 40 | cf = self.frames[n] 41 | if cf.opcode == Call: 42 | return cf, len(self.frames) - 1 - n 43 | n -= 1 44 | return None, -1 45 | 46 | def push_control_frame(self, cf): 47 | self.frames.append(cf) 48 | 49 | def pop_control_frame(self): 50 | return self.frames.pop() 51 | -------------------------------------------------------------------------------- /src/ch10/interpreter/vm_table.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_table.py 6 | @time: 2020/8/23 17:16 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | from ch10.instance import module 12 | from ch10.interpreter.errors import ErrUndefinedElem, ErrUninitializedElem 13 | 14 | 15 | class Table(module.Table): 16 | def __init__(self, tt=None): 17 | self.type = tt 18 | self.elems = [None] * tt.limits.min 19 | 20 | @property 21 | def size(self): 22 | return len(self.elems) 23 | 24 | def grow(self, n): 25 | self.elems.extend([None] * n) 26 | 27 | def get_elem(self, idx): 28 | self.check_idx(idx) 29 | elem = self.elems[idx] 30 | if elem is None: 31 | raise ErrUninitializedElem 32 | return elem 33 | 34 | def set_elem(self, idx, elem): 35 | self.check_idx(idx) 36 | self.elems[idx] = elem 37 | 38 | def check_idx(self, idx): 39 | if idx >= len(self.elems): 40 | raise ErrUndefinedElem 41 | -------------------------------------------------------------------------------- /src/ch11/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch11.binary import reader 11 | 12 | decode_file = reader.decode_file 13 | -------------------------------------------------------------------------------- /src/ch11/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | def __str__(self): 19 | print("unexpected end of section or function") 20 | 21 | 22 | class ErrIntTooLong(Exception): 23 | """Int类型超长""" 24 | 25 | def __init__(self): 26 | super().__init__("integer representation too long") 27 | 28 | def __str__(self): 29 | print("integer representation too long") 30 | 31 | 32 | class ErrIntTooLarge(Exception): 33 | """Int类型值太大""" 34 | 35 | def __init__(self): 36 | super().__init__("integer too large") 37 | 38 | def __str__(self): 39 | print("integer too large") 40 | -------------------------------------------------------------------------------- /src/ch11/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch11.binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self): 27 | # 操作码 28 | self.opcode = None 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | self.bt = None 48 | # 内嵌的指令序列 49 | self.instrs = [] 50 | 51 | 52 | class IfArgs: 53 | """if指令的参数""" 54 | 55 | def __init__(self): 56 | # block type 57 | self.bt = None 58 | self.instrs1 = [] 59 | self.instrs2 = [] 60 | 61 | 62 | class BrTableArgs: 63 | """br_table指令的参数""" 64 | 65 | def __init__(self, labels=None, default=None): 66 | # 跳转表 67 | if labels is None: 68 | labels = [] 69 | self.labels = labels 70 | # 默认跳转标签 71 | self.default = default 72 | 73 | 74 | class MemArg: 75 | """内存指令参数""" 76 | 77 | def __init__(self, align=0, offset=0): 78 | # 对齐提示 79 | self.align = align 80 | # 内存偏移量 81 | self.offset = offset 82 | -------------------------------------------------------------------------------- /src/ch11/binary/leb128.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: leb128.py 6 | @time: 2020/8/18 22:02 7 | @project: wasm-python-book 8 | @desc: LEB128 9 | Wasm二进制格式使用LEB1128来编码列表长度和索引等整数值 10 | """ 11 | from ch11.binary.errors import ErrIntTooLong, ErrIntTooLarge, ErrUnexpectedEnd 12 | 13 | 14 | def decode_var_uint(data, size: int): 15 | """ 16 | LEB128无符号整数解码 17 | :param data: 解码后的整数 18 | :param size: 实际消耗的字节数 19 | :return: 20 | """ 21 | result = 0 22 | for i, b in enumerate(data): 23 | if i == size / 7: 24 | if b & 0x80 != 0: 25 | raise ErrIntTooLong 26 | if b >> (size - i * 7) > 0: 27 | raise ErrIntTooLarge 28 | result |= (b & 0x7f) << (i * 7) 29 | if b & 0x80 == 0: 30 | return result, i + 1 31 | raise ErrUnexpectedEnd 32 | 33 | 34 | def decode_var_int(data, size): 35 | """ 36 | LEB128有符号整数解码 37 | :param data: 解码后的整数 38 | :param size: 实际消耗的字节数 39 | :return: 40 | """ 41 | result = 0 42 | for i, b in enumerate(data): 43 | if i == size / 7: 44 | if b & 0x80 != 0: 45 | raise ErrIntTooLong 46 | if b & 0x40 == 0 and b >> (size - i * 7 - 1) != 0 or \ 47 | b & 0x40 != 0 and int(b | 0x80) >> (size - i * 7 - 1) != -1: 48 | raise ErrIntTooLarge 49 | result |= (b & 0x7f) << (i * 7) 50 | if b & 0x80 == 0: 51 | if (i * 7 < size) and (b & 0x40 != 0): 52 | result |= -1 << ((i + 1) * 7) 53 | return result, i + 1 54 | raise ErrUnexpectedEnd 55 | -------------------------------------------------------------------------------- /src/ch11/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/ch11/cmd/native.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native.py 6 | @time: 2020/8/23 17:33 7 | @project: wasm-python-book 8 | @desc: 本地方法 9 | """ 10 | from ch11.instance.native_module import NativeModule 11 | from ch11.interpreter import * 12 | 13 | 14 | def new_env(): 15 | env = NativeModule() 16 | env.register_func("print_char(i32)->()", print_char) 17 | env.register_func("assert_true(i32)->()", assert_true) 18 | env.register_func("assert_false(i32)->()", assert_false) 19 | env.register_func("assert_eq_i32(i32,i32)->()", assert_eq_i32) 20 | env.register_func("assert_eq_i64(i64,i64)->()", assert_eq_i64) 21 | env.register_func("assert_eq_f32(f32,f32)->()", assert_eq_f32) 22 | env.register_func("assert_eq_f64(f64,f64)->()", assert_eq_f64) 23 | return env 24 | 25 | 26 | def print_char(args): 27 | print("%c" % int(args[0]), end='') 28 | return None, None 29 | 30 | 31 | def assert_true(args): 32 | __assert_equal(int32(args[0]), int32(1)) 33 | return None, None 34 | 35 | 36 | def assert_false(args): 37 | __assert_equal(int32(args[0]), int32(0)) 38 | return None, None 39 | 40 | 41 | def assert_eq_i32(args): 42 | __assert_equal(int32(args[0]), int32(args[1])) 43 | return None, None 44 | 45 | 46 | def assert_eq_i64(args): 47 | __assert_equal(int64(args[0]), int64(args[1])) 48 | return None, None 49 | 50 | 51 | def assert_eq_f32(args): 52 | __assert_equal(float32(args[0]), float32(args[1])) 53 | return None, None 54 | 55 | 56 | def assert_eq_f64(args): 57 | __assert_equal(float64(args[0]), float64(args[1])) 58 | return None, None 59 | 60 | 61 | def __assert_equal(a, b): 62 | if a != b: 63 | raise Exception("{} != {}".format(a, b)) 64 | -------------------------------------------------------------------------------- /src/ch11/instance/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/23 19:40 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/ch11/instance/module.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: module.py 6 | @time: 2020/8/23 19:43 7 | @project: wasm-python-book 8 | @desc: 模块实例 9 | """ 10 | from abc import ABCMeta, abstractmethod 11 | 12 | 13 | class Module(metaclass=ABCMeta): 14 | @abstractmethod 15 | def get_member(self, name): 16 | pass 17 | 18 | @abstractmethod 19 | def invoke_func(self, name, args): 20 | pass 21 | 22 | @abstractmethod 23 | def get_global_val(self, name): 24 | pass 25 | 26 | @abstractmethod 27 | def set_global_var(self, name, val): 28 | pass 29 | 30 | 31 | class Function(metaclass=ABCMeta): 32 | @abstractmethod 33 | def call(self, args): 34 | pass 35 | 36 | 37 | class Table(metaclass=ABCMeta): 38 | @abstractmethod 39 | def size(self): 40 | pass 41 | 42 | @abstractmethod 43 | def grow(self, n): 44 | pass 45 | 46 | @abstractmethod 47 | def get_elem(self, idx): 48 | pass 49 | 50 | @abstractmethod 51 | def set_elem(self, idx, elem): 52 | pass 53 | 54 | 55 | class Memory(metaclass=ABCMeta): 56 | @abstractmethod 57 | def size(self): 58 | pass 59 | 60 | @abstractmethod 61 | def grow(self, n): 62 | pass 63 | 64 | @abstractmethod 65 | def read(self, offset, buf): 66 | pass 67 | 68 | @abstractmethod 69 | def write(self, offset, buf): 70 | pass 71 | 72 | 73 | class Global(metaclass=ABCMeta): 74 | @abstractmethod 75 | def get_as_u64(self): 76 | pass 77 | 78 | @abstractmethod 79 | def set_as_u64(self, val): 80 | pass 81 | 82 | @abstractmethod 83 | def get(self): 84 | pass 85 | 86 | @abstractmethod 87 | def set(self, val): 88 | pass 89 | -------------------------------------------------------------------------------- /src/ch11/instance/native_function.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native_function.py 6 | @time: 2020/8/23 19:54 7 | @project: wasm-python-book 8 | @desc: 本地方法实例 9 | """ 10 | from ch11.binary.types import FuncType 11 | from ch11.instance.module import Function 12 | 13 | 14 | class NativeFunction(Function): 15 | def __init__(self, t: FuncType = None, f=None): 16 | self.type = t 17 | self.f = f 18 | 19 | def call(self, args): 20 | return self.f(args) 21 | -------------------------------------------------------------------------------- /src/ch11/instance/native_module.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native_module.py 6 | @time: 2020/8/23 19:57 7 | @project: wasm-python-book 8 | @desc: 本地模型实例 9 | """ 10 | from ch11.instance import module 11 | from ch11.instance.native_function import NativeFunction 12 | from ch11.instance.sig_parser import parse_name_and_sig 13 | 14 | 15 | class NativeModule(module.Module): 16 | def __init__(self): 17 | self.exported = dict() 18 | 19 | def register_func(self, name_and_sig, f): 20 | name, sig = parse_name_and_sig(name_and_sig) 21 | self.exported[name] = NativeFunction(t=sig, f=f) 22 | 23 | def register(self, name, x): 24 | self.exported[name] = x 25 | 26 | def get_member(self, name): 27 | return self.exported[name] 28 | 29 | def invoke_func(self, name, args): 30 | # TODO 31 | return self.exported[name].call(args) 32 | 33 | def get_global_val(self, name): 34 | # TODO 35 | return self.exported[name].get(), None 36 | 37 | def set_global_var(self, name, val): 38 | # TODO 39 | self.exported[name].set(val) 40 | return None 41 | -------------------------------------------------------------------------------- /src/ch11/instance/sig_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: sig_parser.py 6 | @time: 2020/8/23 19:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch11.binary.types import FuncType, ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64 11 | 12 | 13 | def parse_name_and_sig(name_and_sig: str): 14 | idx_of_lpar = name_and_sig.find('(') 15 | name = name_and_sig[:idx_of_lpar] 16 | sig = name_and_sig[idx_of_lpar:] 17 | return name, parse_sig(sig) 18 | 19 | 20 | def parse_sig(sig): 21 | params_and_results = sig.split('->') 22 | return FuncType(param_types=parse_val_types(params_and_results[0]), 23 | result_types=parse_val_types(params_and_results[1])) 24 | 25 | 26 | def parse_val_types(val_types_str: str): 27 | val_types_str = val_types_str.strip() 28 | # remove () 29 | val_types_str = val_types_str[1:-1] 30 | val_types = [] 31 | for t in val_types_str.split(','): 32 | t = t.strip() 33 | if t == 'i32': 34 | val_types.append(ValTypeI32) 35 | elif t == 'i64': 36 | val_types.append(ValTypeI64) 37 | elif t == 'f32': 38 | val_types.append(ValTypeF32) 39 | elif t == 'f64': 40 | val_types.append(ValTypeF64) 41 | 42 | return val_types 43 | -------------------------------------------------------------------------------- /src/ch11/interpreter/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/19 21:07 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import ctypes 12 | 13 | 14 | class int8(int): 15 | def __new__(cls, val): 16 | val = ctypes.c_int8(val).value 17 | return super().__new__(cls, val) 18 | 19 | 20 | class int16(int): 21 | def __new__(cls, val): 22 | val = ctypes.c_int16(val).value 23 | return super().__new__(cls, val) 24 | 25 | 26 | class int32(int): 27 | def __new__(cls, val): 28 | val = ctypes.c_int32(val).value 29 | return super().__new__(cls, val) 30 | 31 | 32 | class int64(int): 33 | def __new__(cls, val): 34 | val = ctypes.c_int64(val).value 35 | return super().__new__(cls, val) 36 | 37 | 38 | class uint16(int): 39 | def __new__(cls, val): 40 | val = ctypes.c_uint16(val).value 41 | return super().__new__(cls, val) 42 | 43 | 44 | class uint32(int): 45 | def __new__(cls, val): 46 | val = ctypes.c_uint32(val).value 47 | return super().__new__(cls, val) 48 | 49 | 50 | class uint64(int): 51 | def __new__(cls, val): 52 | val = ctypes.c_uint64(val).value 53 | return super().__new__(cls, val) 54 | 55 | 56 | class float32(float): 57 | def __new__(cls, val): 58 | val = ctypes.c_float(val).value 59 | return super().__new__(cls, val) 60 | 61 | 62 | class float64(float): 63 | def __new__(cls, val): 64 | val = ctypes.c_double(val).value 65 | return super().__new__(cls, val) 66 | -------------------------------------------------------------------------------- /src/ch11/interpreter/instr_parametric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_parametric.py 6 | @time: 2020/8/19 21:48 7 | @project: wasm-python-book 8 | @desc: 参数指令 9 | """ 10 | 11 | 12 | def drop(vm, _): 13 | """drop指令(操作码0x1A)从栈顶弹出一个操作数并把它扔掉""" 14 | vm.pop_u64() 15 | 16 | 17 | def _select(vm, _): 18 | """ 19 | select指令(操作码0x1B)从栈顶弹出3个操作数, 20 | 然后根据最先弹出的操作数从其他两个操作数中“选择”一个压栈 21 | """ 22 | v3 = vm.pop_bool() 23 | v2 = vm.pop_u64() 24 | v1 = vm.pop_u64() 25 | 26 | if v3: 27 | vm.push_u64(v1) 28 | else: 29 | vm.push_u64(v2) 30 | -------------------------------------------------------------------------------- /src/ch11/interpreter/instr_variable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_variable.py 6 | @time: 2020/8/23 11:35 7 | @project: wasm-python-book 8 | @desc: 局部变量指令 9 | """ 10 | from ch11.interpreter import uint32 11 | 12 | 13 | def local_get(vm, args): 14 | """获取局部变量""" 15 | idx = uint32(args) 16 | val = vm.get_operand(vm.local_0_idx + idx) 17 | vm.push_u64(val) 18 | 19 | 20 | def local_set(vm, args): 21 | """设置局部变量的值""" 22 | idx = uint32(args) 23 | val = vm.pop_u64() 24 | vm.set_operand(vm.local_0_idx + idx, val) 25 | 26 | 27 | def local_tee(vm, args): 28 | """用重定向操作符>把某个命令的输出重定向到文件里""" 29 | idx = uint32(args) 30 | val = vm.pop_u64() 31 | vm.push_u64(val) 32 | vm.set_operand(vm.local_0_idx + idx, val) 33 | 34 | 35 | def global_get(vm, args): 36 | idx = uint32(args) 37 | val = vm.globals[idx].get_as_u64() 38 | vm.push_u64(val) 39 | 40 | 41 | def global_set(vm, args): 42 | idx = uint32(args) 43 | val = vm.pop_u64() 44 | vm.globals[idx].set_as_u64(val) 45 | -------------------------------------------------------------------------------- /src/ch11/interpreter/val.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: val.py 6 | @time: 2020/8/23 17:06 7 | @project: wasm-python-book 8 | @desc: 参数和返回值的包装/解包 9 | """ 10 | import math 11 | import struct 12 | 13 | from ch11.binary.types import ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64 14 | from ch11.interpreter import int32, int64, float32, float64, uint64 15 | 16 | 17 | def wrap_u64(vt, val): 18 | if vt == ValTypeI32: 19 | return int32(val) 20 | elif vt == ValTypeI64: 21 | return int64(val) 22 | elif vt == ValTypeF32: 23 | cov_val = struct.unpack('>f', struct.pack('>l', int64(val)))[0] 24 | if math.isnan(cov_val): 25 | return float32(val) 26 | else: 27 | return float32(cov_val) 28 | elif vt == ValTypeF64: 29 | cov_val = struct.unpack('>d', struct.pack('>q', int64(val)))[0] 30 | if math.isnan(cov_val): 31 | return float64(val) 32 | else: 33 | return float64(cov_val) 34 | else: 35 | raise Exception("unreachable") 36 | 37 | 38 | def unwrap_u64(vt, val): 39 | if vt == ValTypeI32: 40 | return uint64(val) 41 | elif vt == ValTypeI64: 42 | return uint64(val) 43 | elif vt == ValTypeF32: 44 | return uint64(val) 45 | elif vt == ValTypeF64: 46 | return uint64(val) 47 | else: 48 | raise Exception("unreachable") 49 | -------------------------------------------------------------------------------- /src/ch11/interpreter/vm_global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_global.py 6 | @time: 2020/8/23 10:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from ch11.instance import module 11 | from ch11.interpreter.errors import ErrImmutableGlobal 12 | from ch11.interpreter.val import wrap_u64, unwrap_u64 13 | 14 | 15 | class GlobalVar(module.Global): 16 | def __init__(self, gt, val): 17 | self.type = gt 18 | self.val = val 19 | 20 | def get_as_u64(self): 21 | return self.val 22 | 23 | def set_as_u64(self, val): 24 | if self.type.mut != 1: 25 | raise ErrImmutableGlobal 26 | self.val = val 27 | 28 | def get(self): 29 | return wrap_u64(self.type.val_type, self.val) 30 | 31 | def set(self, val): 32 | self.val = unwrap_u64(self.type.val_type, val) 33 | -------------------------------------------------------------------------------- /src/ch11/interpreter/vm_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_memory.py 6 | @time: 2020/8/21 16:01 7 | @project: wasm-python-book 8 | @desc: 内存实现 9 | """ 10 | from ch11.binary.module import PageSize, MaxPageCount 11 | from ch11.interpreter.errors import ErrMemOutOfBounds 12 | 13 | 14 | class Memory: 15 | def __init__(self, mem_type=None): 16 | self.type = mem_type 17 | # 内存初始页分配 18 | self.data = [0x00] * mem_type.min * PageSize 19 | 20 | @property 21 | def size(self): 22 | return int(len(self.data) / PageSize) 23 | 24 | def grow(self, n): 25 | old_size = self.size 26 | if n == 0: 27 | return old_size 28 | 29 | # 检查页数,防止超出限制 30 | max = self.type.max 31 | max_page_count = max if max > 0 else MaxPageCount 32 | if old_size + n > max_page_count: 33 | return -1 34 | 35 | self.data.extend([0x00] * n * PageSize) 36 | return old_size 37 | 38 | def read(self, offset, buf): 39 | self.check_offset(offset, len(buf)) 40 | buf = self.data[offset:(offset + len(buf))] 41 | return buf 42 | 43 | def write(self, offset, data): 44 | self.check_offset(offset, len(data)) 45 | self.data[offset:(offset + len(data))] = data 46 | 47 | def check_offset(self, offset, length): 48 | """检查边界""" 49 | if len(self.data) - length < offset: 50 | raise ErrMemOutOfBounds 51 | -------------------------------------------------------------------------------- /src/ch11/interpreter/vm_stack_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_stack_control.py 6 | @time: 2020/8/23 10:41 7 | @project: wasm-python-book 8 | @desc: 控制帧 9 | """ 10 | from ch11.binary.opcodes import Call 11 | 12 | 13 | class ControlFrame: 14 | """控制帧""" 15 | 16 | def __init__(self, opcode, bt, instrs, bp): 17 | self.opcode = opcode 18 | self.bt = bt 19 | self.instrs = instrs 20 | self.bp = bp 21 | # 程序计数器,用于记录指令执行的位置 22 | self.pc = 0 23 | 24 | 25 | class ControlStack: 26 | def __init__(self): 27 | self.frames = [] 28 | 29 | @property 30 | def control_depth(self): 31 | return len(self.frames) 32 | 33 | @property 34 | def top_control_frame(self): 35 | return self.frames[-1] 36 | 37 | def top_call_frame(self): 38 | n = len(self.frames) - 1 39 | while n >= 0: 40 | cf = self.frames[n] 41 | if cf.opcode == Call: 42 | return cf, len(self.frames) - 1 - n 43 | n -= 1 44 | return None, -1 45 | 46 | def push_control_frame(self, cf): 47 | self.frames.append(cf) 48 | 49 | def pop_control_frame(self): 50 | return self.frames.pop() 51 | -------------------------------------------------------------------------------- /src/ch11/interpreter/vm_table.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_table.py 6 | @time: 2020/8/23 17:16 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | from ch11.instance import module 12 | from ch11.interpreter.errors import ErrUndefinedElem, ErrUninitializedElem 13 | 14 | 15 | class Table(module.Table): 16 | def __init__(self, tt=None): 17 | self.type = tt 18 | self.elems = [None] * tt.limits.min 19 | 20 | @property 21 | def size(self): 22 | return len(self.elems) 23 | 24 | def grow(self, n): 25 | self.elems.extend([None] * n) 26 | 27 | def get_elem(self, idx): 28 | self.check_idx(idx) 29 | elem = self.elems[idx] 30 | if elem is None: 31 | raise ErrUninitializedElem 32 | return elem 33 | 34 | def set_elem(self, idx, elem): 35 | self.check_idx(idx) 36 | self.elems[idx] = elem 37 | 38 | def check_idx(self, idx): 39 | if idx >= len(self.elems): 40 | raise ErrUndefinedElem 41 | -------------------------------------------------------------------------------- /src/ch11/validator/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/24 8:40 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/develop_code/binary/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 1:55 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from binary import reader 11 | 12 | decode_file = reader.decode_file 13 | -------------------------------------------------------------------------------- /src/develop_code/binary/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/18 16:42 7 | @project: wasm-python-book 8 | @desc: 自定义异常 9 | """ 10 | 11 | 12 | class ErrUnexpectedEnd(Exception): 13 | """方法或段结束异常""" 14 | 15 | def __init__(self): 16 | super().__init__("unexpected end of section or function") 17 | 18 | 19 | class ErrIntTooLong(Exception): 20 | """Int类型超长""" 21 | 22 | def __init__(self): 23 | super().__init__("integer representation too long") 24 | 25 | 26 | class ErrIntTooLarge(Exception): 27 | """Int类型值太大""" 28 | 29 | def __init__(self): 30 | super().__init__("integer too large") 31 | -------------------------------------------------------------------------------- /src/develop_code/binary/instruction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instruction.py 6 | @time: 2020/8/18 18:28 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from binary.opnames import opnames 11 | 12 | 13 | class Expr(list): 14 | """ 15 | 表达式 16 | expr : instr*|0x0b 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | 23 | class Instruction: 24 | """指令""" 25 | 26 | def __init__(self, opcode=None): 27 | # 操作码 28 | self.opcode = opcode 29 | # 操作数 30 | self.args = None 31 | 32 | def get_opname(self): 33 | return opnames[self.opcode] 34 | 35 | def __str__(self): 36 | return opnames[self.opcode] 37 | 38 | 39 | class BlockArgs: 40 | """block和loop指令的参数""" 41 | 42 | def __init__(self, bt=None, instrs=None): 43 | # block type: 44 | # -1表示i32类型结果,-2表示i64类型结果, 45 | # -3表示f32类型结果,-4表示f64类型结果, 46 | # -64表示没有结果 47 | if instrs is None: 48 | instrs = [] 49 | self.bt = bt 50 | # 内嵌的指令序列 51 | self.instrs = instrs 52 | 53 | 54 | class IfArgs: 55 | """if指令的参数""" 56 | 57 | def __init__(self): 58 | # block type 59 | self.bt = None 60 | self.instrs1 = [] 61 | self.instrs2 = [] 62 | 63 | 64 | class BrTableArgs: 65 | """br_table指令的参数""" 66 | 67 | def __init__(self, labels=None, default=None): 68 | # 跳转表 69 | if labels is None: 70 | labels = [] 71 | self.labels = labels 72 | # 默认跳转标签 73 | self.default = default 74 | 75 | 76 | class MemArg: 77 | """内存指令参数""" 78 | 79 | def __init__(self, align=0, offset=0): 80 | # 对齐提示 81 | self.align = align 82 | # 内存偏移量 83 | self.offset = offset 84 | -------------------------------------------------------------------------------- /src/develop_code/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/19 12:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/develop_code/instance/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/23 19:40 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/develop_code/instance/module.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: module.py 6 | @time: 2020/8/23 19:43 7 | @project: wasm-python-book 8 | @desc: 模块实例 9 | """ 10 | from abc import ABCMeta, abstractmethod 11 | 12 | 13 | class Module(metaclass=ABCMeta): 14 | @abstractmethod 15 | def get_member(self, name): 16 | pass 17 | 18 | @abstractmethod 19 | def invoke_func(self, name, args): 20 | pass 21 | 22 | @abstractmethod 23 | def get_global_val(self, name): 24 | pass 25 | 26 | @abstractmethod 27 | def set_global_var(self, name, val): 28 | pass 29 | 30 | 31 | class Function(metaclass=ABCMeta): 32 | @abstractmethod 33 | def call(self, args): 34 | pass 35 | 36 | 37 | class Table(metaclass=ABCMeta): 38 | @abstractmethod 39 | def size(self): 40 | pass 41 | 42 | @abstractmethod 43 | def grow(self, n): 44 | pass 45 | 46 | @abstractmethod 47 | def get_elem(self, idx): 48 | pass 49 | 50 | @abstractmethod 51 | def set_elem(self, idx, elem): 52 | pass 53 | 54 | 55 | class Memory(metaclass=ABCMeta): 56 | @abstractmethod 57 | def size(self): 58 | pass 59 | 60 | @abstractmethod 61 | def grow(self, n): 62 | pass 63 | 64 | @abstractmethod 65 | def read(self, offset, buf): 66 | pass 67 | 68 | @abstractmethod 69 | def write(self, offset, buf): 70 | pass 71 | 72 | 73 | class Global(metaclass=ABCMeta): 74 | @abstractmethod 75 | def get_as_u64(self): 76 | pass 77 | 78 | @abstractmethod 79 | def set_as_u64(self, val): 80 | pass 81 | 82 | @abstractmethod 83 | def get(self): 84 | pass 85 | 86 | @abstractmethod 87 | def set(self, val): 88 | pass 89 | -------------------------------------------------------------------------------- /src/develop_code/instance/native_function.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native_function.py 6 | @time: 2020/8/23 19:54 7 | @project: wasm-python-book 8 | @desc: 本地方法实例 9 | """ 10 | from binary.types import FuncType 11 | from instance.module import Function 12 | 13 | 14 | class NativeFunction(Function): 15 | def __init__(self, t: FuncType = None, f=None): 16 | self.type = t 17 | self.f = f 18 | 19 | def call(self, args): 20 | return self.f(args) 21 | -------------------------------------------------------------------------------- /src/develop_code/instance/native_module.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native_module.py 6 | @time: 2020/8/23 19:57 7 | @project: wasm-python-book 8 | @desc: 本地模型实例 9 | """ 10 | from instance import module 11 | from instance.native_function import NativeFunction 12 | from instance.sig_parser import parse_name_and_sig 13 | 14 | 15 | class NativeModule(module.Module): 16 | def __init__(self): 17 | self.exported = dict() 18 | 19 | def register_func(self, name_and_sig, f): 20 | name, sig = parse_name_and_sig(name_and_sig) 21 | self.exported[name] = NativeFunction(t=sig, f=f) 22 | 23 | def register(self, name, x): 24 | self.exported[name] = x 25 | 26 | def get_member(self, name): 27 | return self.exported.get(name, None) 28 | 29 | def invoke_func(self, name, args): 30 | # TODO 31 | return self.exported[name].call(args) 32 | 33 | def get_global_val(self, name): 34 | # TODO 35 | return self.exported[name].get(), None 36 | 37 | def set_global_var(self, name, val): 38 | # TODO 39 | self.exported[name].set(val) 40 | return None 41 | -------------------------------------------------------------------------------- /src/develop_code/instance/sig_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: sig_parser.py 6 | @time: 2020/8/23 19:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from binary.types import FuncType, ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64 11 | 12 | 13 | def parse_name_and_sig(name_and_sig: str): 14 | idx_of_lpar = name_and_sig.find('(') 15 | name = name_and_sig[:idx_of_lpar] 16 | sig = name_and_sig[idx_of_lpar:] 17 | return name, parse_sig(sig) 18 | 19 | 20 | def parse_sig(sig): 21 | params_and_results = sig.split('->') 22 | return FuncType(param_types=parse_val_types(params_and_results[0]), 23 | result_types=parse_val_types(params_and_results[1])) 24 | 25 | 26 | def parse_val_types(val_types_str: str): 27 | val_types_str = val_types_str.strip() 28 | # remove () 29 | val_types_str = val_types_str[1:-1] 30 | val_types = [] 31 | for t in val_types_str.split(','): 32 | t = t.strip() 33 | if t == 'i32': 34 | val_types.append(ValTypeI32) 35 | elif t == 'i64': 36 | val_types.append(ValTypeI64) 37 | elif t == 'f32': 38 | val_types.append(ValTypeF32) 39 | elif t == 'f64': 40 | val_types.append(ValTypeF64) 41 | 42 | return val_types 43 | -------------------------------------------------------------------------------- /src/develop_code/interpreter/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: errors.py 6 | @time: 2020/8/19 21:07 7 | @project: wasm-python-book 8 | @desc: 解释器Error异常 9 | """ 10 | 11 | 12 | class ErrTrap(Exception): 13 | def __init__(self): 14 | super().__init__("unreachable") 15 | 16 | 17 | class ErrCallStackOverflow(Exception): 18 | def __init__(self): 19 | super().__init__("call stack exhausted") 20 | 21 | 22 | class ErrTypeMismatch(Exception): 23 | def __init__(self): 24 | super().__init__("indirect call type mismatch") 25 | 26 | 27 | class ErrUndefinedElem(Exception): 28 | def __init__(self): 29 | super().__init__("undefined element") 30 | 31 | 32 | class ErrUninitializedElem(Exception): 33 | def __init__(self): 34 | super().__init__("uninitialized element") 35 | 36 | 37 | class ErrMemOutOfBounds(Exception): 38 | def __init__(self): 39 | super().__init__("out of bounds memory access") 40 | 41 | 42 | class ErrImmutableGlobal(Exception): 43 | def __init__(self): 44 | super().__init__("immutable global") 45 | 46 | 47 | class ErrIntOverflow(Exception): 48 | def __init__(self): 49 | super().__init__("integer overflow") 50 | 51 | 52 | class ErrConvertToInt(Exception): 53 | def __init__(self): 54 | super().__init__("invalid conversion to integer") 55 | 56 | 57 | class ErrIntDivideByZero(Exception): 58 | def __init__(self): 59 | super().__init__("integer divide by zero") 60 | -------------------------------------------------------------------------------- /src/develop_code/interpreter/instr_parametric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_parametric.py 6 | @time: 2020/8/19 21:48 7 | @project: wasm-python-book 8 | @desc: 参数指令 9 | """ 10 | 11 | 12 | def drop(vm, _): 13 | """drop指令(操作码0x1A)从栈顶弹出一个操作数并把它扔掉""" 14 | vm.pop_u64() 15 | 16 | 17 | def _select(vm, _): 18 | """ 19 | select指令(操作码0x1B)从栈顶弹出3个操作数, 20 | 然后根据最先弹出的操作数从其他两个操作数中“选择”一个压栈 21 | """ 22 | v3 = vm.pop_bool() 23 | v2 = vm.pop_u64() 24 | v1 = vm.pop_u64() 25 | 26 | if v3: 27 | vm.push_u64(v1) 28 | else: 29 | vm.push_u64(v2) 30 | -------------------------------------------------------------------------------- /src/develop_code/interpreter/instr_variable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_variable.py 6 | @time: 2020/8/23 11:35 7 | @project: wasm-python-book 8 | @desc: 局部变量指令 9 | """ 10 | from interpreter import uint32 11 | 12 | 13 | def local_get(vm, args): 14 | """获取局部变量""" 15 | idx = uint32(args) 16 | val = vm.get_operand(vm.local_0_idx + idx) 17 | vm.push_u64(val) 18 | 19 | 20 | def local_set(vm, args): 21 | """设置局部变量的值""" 22 | idx = uint32(args) 23 | val = vm.pop_u64() 24 | vm.set_operand(vm.local_0_idx + idx, val) 25 | 26 | 27 | def local_tee(vm, args): 28 | """用重定向操作符>把某个命令的输出重定向到文件里""" 29 | idx = uint32(args) 30 | val = vm.pop_u64() 31 | vm.push_u64(val) 32 | vm.set_operand(vm.local_0_idx + idx, val) 33 | 34 | 35 | def global_get(vm, args): 36 | idx = uint32(args) 37 | val = vm.globals[idx].get_as_u64() 38 | vm.push_u64(val) 39 | 40 | 41 | def global_set(vm, args): 42 | idx = uint32(args) 43 | val = vm.pop_u64() 44 | vm.globals[idx].set_as_u64(val) 45 | -------------------------------------------------------------------------------- /src/develop_code/interpreter/val.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: val.py 6 | @time: 2020/8/23 17:06 7 | @project: wasm-python-book 8 | @desc: 参数和返回值的包装/解包 9 | """ 10 | import math 11 | import struct 12 | 13 | from binary.types import ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64 14 | from interpreter import int32, int64, float32, float64, uint64, uint32 15 | 16 | 17 | def wrap_u64(vt, val): 18 | if vt == ValTypeI32: 19 | return int32(val) 20 | elif vt == ValTypeI64: 21 | return int64(val) 22 | elif vt == ValTypeF32: 23 | try: 24 | cov_val = struct.unpack('>f', struct.pack('>l', int64(val)))[0] 25 | except struct.error: 26 | cov_val = struct.unpack('>f', struct.pack('>L', int64(val)))[0] 27 | if math.isnan(cov_val): 28 | return float32(val) 29 | else: 30 | return float32(cov_val) 31 | elif vt == ValTypeF64: 32 | try: 33 | cov_val = struct.unpack('>d', struct.pack('>q', int64(val)))[0] 34 | except struct.error: 35 | cov_val = struct.unpack('>d', struct.pack('>Q', int64(val)))[0] 36 | if math.isnan(cov_val): 37 | return float64(val) 38 | else: 39 | return float64(cov_val) 40 | else: 41 | raise Exception("unreachable") 42 | 43 | 44 | def unwrap_u64(vt, val): 45 | if vt == ValTypeI32: 46 | return uint64(val) 47 | elif vt == ValTypeI64: 48 | return uint64(val) 49 | elif vt == ValTypeF32: 50 | val = struct.unpack('>l', struct.pack('>f', val))[0] 51 | return uint64(val) 52 | elif vt == ValTypeF64: 53 | val = struct.unpack('>q', struct.pack('>d', val))[0] 54 | return uint64(val) 55 | else: 56 | raise Exception("unreachable") 57 | -------------------------------------------------------------------------------- /src/develop_code/interpreter/vm_global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_global.py 6 | @time: 2020/8/23 10:59 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from binary.types import GlobalType 11 | from instance import module 12 | from interpreter.errors import ErrImmutableGlobal 13 | from interpreter.val import wrap_u64, unwrap_u64 14 | 15 | 16 | class GlobalVar(module.Global): 17 | def __init__(self, gt, val): 18 | self.type = gt 19 | self.val = val 20 | 21 | def get_as_u64(self): 22 | return self.val 23 | 24 | def set_as_u64(self, val): 25 | if self.type.mut != 1: 26 | raise ErrImmutableGlobal 27 | self.val = val 28 | 29 | def get(self): 30 | return wrap_u64(self.type.val_type, self.val) 31 | 32 | def set(self, val): 33 | self.val = unwrap_u64(self.type.val_type, val) 34 | 35 | 36 | def new_global(vt, mut, val): 37 | gt = GlobalType(val_type=vt) 38 | if mut: 39 | gt.mut = 1 40 | return GlobalVar(gt=gt, val=val) 41 | -------------------------------------------------------------------------------- /src/develop_code/interpreter/vm_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_memory.py 6 | @time: 2020/8/21 16:01 7 | @project: wasm-python-book 8 | @desc: 内存实现 9 | """ 10 | from binary.module import PageSize, MaxPageCount 11 | from binary.types import MemType 12 | from instance import module 13 | from interpreter.errors import ErrMemOutOfBounds 14 | 15 | 16 | class Memory(module.Memory): 17 | def __init__(self, mem_type=None): 18 | self.type = mem_type 19 | # 内存初始页分配 20 | self.data = [0x00] * mem_type.min * PageSize 21 | 22 | @property 23 | def size(self): 24 | return int(len(self.data) / PageSize) 25 | 26 | def grow(self, n): 27 | old_size = self.size 28 | if n == 0: 29 | return old_size 30 | 31 | # 检查页数,防止超出限制 32 | max = self.type.max 33 | max_page_count = max if max > 0 else MaxPageCount 34 | if old_size + n > max_page_count: 35 | return -1 36 | 37 | self.data.extend([0x00] * n * PageSize) 38 | return old_size 39 | 40 | def read(self, offset, buf): 41 | self.check_offset(offset, len(buf)) 42 | buf = self.data[offset:(offset + len(buf))] 43 | return buf 44 | 45 | def write(self, offset, data): 46 | self.check_offset(offset, len(data)) 47 | self.data[offset:(offset + len(data))] = data 48 | 49 | def check_offset(self, offset, length): 50 | """检查边界""" 51 | if len(self.data) - length < offset: 52 | raise ErrMemOutOfBounds 53 | 54 | 55 | def new_memory(min, max): 56 | mt = MemType(min=min, max=max) 57 | return Memory(mt) 58 | -------------------------------------------------------------------------------- /src/develop_code/interpreter/vm_stack_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_stack_control.py 6 | @time: 2020/8/23 10:41 7 | @project: wasm-python-book 8 | @desc: 控制帧 9 | """ 10 | from binary.opcodes import Call 11 | 12 | 13 | class ControlFrame: 14 | """控制帧""" 15 | 16 | def __init__(self, opcode, bt, instrs, bp): 17 | self.opcode = opcode 18 | self.bt = bt 19 | self.instrs = instrs 20 | self.bp = bp 21 | # 程序计数器,用于记录指令执行的位置 22 | self.pc = 0 23 | 24 | 25 | class ControlStack: 26 | def __init__(self): 27 | self.frames = [] 28 | 29 | @property 30 | def control_depth(self): 31 | return len(self.frames) 32 | 33 | @property 34 | def top_control_frame(self): 35 | return self.frames[-1] 36 | 37 | def top_call_frame(self): 38 | n = len(self.frames) - 1 39 | while n >= 0: 40 | cf = self.frames[n] 41 | if cf.opcode == Call: 42 | return cf, len(self.frames) - 1 - n 43 | n -= 1 44 | return None, -1 45 | 46 | def push_control_frame(self, cf): 47 | self.frames.append(cf) 48 | 49 | def pop_control_frame(self): 50 | return self.frames.pop() 51 | -------------------------------------------------------------------------------- /src/develop_code/interpreter/vm_table.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: vm_table.py 6 | @time: 2020/8/23 17:16 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from binary.types import TableType, FuncRef, Limits 11 | from instance import module 12 | from interpreter.errors import ErrUndefinedElem, ErrUninitializedElem 13 | 14 | 15 | class Table(module.Table): 16 | def __init__(self, table_type=None): 17 | self.type = table_type 18 | self.elems = [None] * table_type.limits.min 19 | 20 | @property 21 | def size(self): 22 | return len(self.elems) 23 | 24 | def grow(self, n): 25 | self.elems.extend([None] * n) 26 | 27 | def get_elem(self, idx): 28 | self.check_idx(idx) 29 | elem = self.elems[idx] 30 | if elem is None: 31 | raise ErrUninitializedElem 32 | return elem 33 | 34 | def set_elem(self, idx, elem): 35 | self.check_idx(idx) 36 | self.elems[idx] = elem 37 | 38 | def check_idx(self, idx): 39 | if idx >= len(self.elems): 40 | raise ErrUndefinedElem 41 | 42 | 43 | def new_table(min_value, max_value): 44 | tt = TableType(elem_type=FuncRef, 45 | limits=Limits(min=min_value, max=max_value)) 46 | return Table(tt) 47 | -------------------------------------------------------------------------------- /src/develop_code/spectest/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/31 11:01 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/develop_code/spectest/native.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: native.py 6 | @time: 2020/8/23 17:33 7 | @project: wasm-python-book 8 | @desc: 本地方法 9 | """ 10 | from binary.types import ValTypeI32, ValTypeF32, ValTypeF64 11 | from instance.native_module import NativeModule 12 | from interpreter.vm_global import new_global 13 | from interpreter.vm_memory import new_memory 14 | from interpreter.vm_table import new_table 15 | 16 | DEBUG = False 17 | 18 | 19 | def new_spectest_instance(): 20 | spec_test = NativeModule() 21 | spec_test.register_func("print()->()", _print) 22 | spec_test.register_func("print_i32(i32)->()", _print) 23 | spec_test.register_func("print_i64(i64)->()", _print) 24 | spec_test.register_func("print_f32(f32)->()", _print) 25 | spec_test.register_func("print_f64(f64)->()", _print) 26 | spec_test.register_func("print_i32_f32(i32,f32)->()", _print) 27 | spec_test.register_func("print_f64_f64(f64,f64)->()", _print) 28 | spec_test.register("global_i32", new_global(ValTypeI32, False, 666)) 29 | spec_test.register("global_f32", new_global(ValTypeF32, False, 0)) 30 | spec_test.register("global_f64", new_global(ValTypeF64, False, 0)) 31 | spec_test.register("table", new_table(10, 20)) 32 | spec_test.register("memory", new_memory(1, 2)) 33 | return spec_test 34 | 35 | 36 | def _print(args): 37 | if DEBUG: 38 | for arg in args: 39 | print("spectest> {}".format(arg)) 40 | return None, None 41 | -------------------------------------------------------------------------------- /src/develop_code/spectest/wasm_impl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: wasm_impl.py 6 | @time: 2020/9/6 10:14 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from binary import reader 11 | from interpreter import vm 12 | from validator import module_validator 13 | 14 | 15 | class WasmImpl: 16 | 17 | @staticmethod 18 | def validate(module): 19 | return module_validator.validate(module) 20 | 21 | @staticmethod 22 | def instantiate(module, instances): 23 | return vm.new(module, instances) 24 | 25 | @staticmethod 26 | def instantiate_bin(data, instances): 27 | m, err = reader.decode(data) 28 | if err is not None: 29 | return None, err 30 | return vm.new(m, instances) 31 | -------------------------------------------------------------------------------- /src/develop_code/test/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/22 15:10 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/develop_code/test/binary/module_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: module_test.py 6 | @time: 2020/8/19 8:45 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import unittest 12 | 13 | import binary 14 | from binary.module import MagicNumber, Version 15 | 16 | 17 | class TestDecode(unittest.TestCase): 18 | def test_decode(self): 19 | module, err = binary.decode_file("../testdata/hw_rust.wasm") 20 | self.assertIsNone(err) 21 | self.assertEqual(MagicNumber, module.magic) 22 | self.assertEqual(Version, module.version) 23 | self.assertEqual(2, len(module.custom_secs)) 24 | self.assertEqual(15, len(module.type_sec)) 25 | self.assertEqual(0, len(module.import_sec)) 26 | self.assertEqual(171, len(module.func_sec)) 27 | self.assertEqual(1, len(module.table_sec)) 28 | self.assertEqual(1, len(module.mem_sec)) 29 | self.assertEqual(4, len(module.global_sec)) 30 | self.assertEqual(5, len(module.export_sec)) 31 | self.assertIsNone(module.start_sec) 32 | self.assertEqual(1, len(module.elem_sec)) 33 | self.assertEqual(171, len(module.code_sec)) 34 | self.assertEqual(4, len(module.data_sec)) 35 | -------------------------------------------------------------------------------- /src/develop_code/test/binary/reader_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: reader_test.py 6 | @time: 2020/8/19 1:21 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import unittest 12 | 13 | from binary.reader import WasmReader 14 | 15 | 16 | class TestReaderFunc(unittest.TestCase): 17 | 18 | def setUp(self): 19 | data = [0x01, 20 | 0x02, 0x03, 0x04, 0x05, 21 | 0x00, 0x00, 0xc0, 0x3f, 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 23 | 0xE5, 0x8E, 0x26, # https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128 24 | 0xC0, 0xBB, 0x78, # https://en.wikipedia.org/wiki/LEB128#Signed_LEB128 25 | 0xC0, 0xBB, 0x78, 26 | 0x03, 0x01, 0x02, 0x03, 27 | 0x03, 0x66, 0x6f, 0x6f] 28 | reader = WasmReader(data) 29 | self.reader = reader 30 | 31 | def test_reads(self): 32 | self.assertEqual(0x01, self.reader.read_byte()) 33 | self.assertEqual(int(0x05040302), self.reader.read_u32()) 34 | self.assertEqual(float(1.5), self.reader.read_f32()) 35 | self.assertEqual(1.5, self.reader.read_f64()) 36 | self.assertEqual(624485, self.reader.read_var_u32()) 37 | self.assertEqual(-123456, self.reader.read_var_s32()) 38 | self.assertEqual(-123456, self.reader.read_var_s64()) 39 | self.assertEqual(bytearray([0x01, 0x02, 0x03]), self.reader.read_bytes()) 40 | self.assertEqual("foo", self.reader.read_name()) 41 | self.assertEqual(0, self.reader.remaining()) 42 | -------------------------------------------------------------------------------- /src/develop_code/test/binary/types_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: types_test.py 6 | @time: 2020/8/19 8:23 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | import unittest 11 | 12 | from binary.types import FuncType, ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64 13 | 14 | 15 | class TestTypesFunc(unittest.TestCase): 16 | def test_signature_param_types(self): 17 | ft = FuncType(param_types=[ValTypeI32, ValTypeI64, ValTypeF32, ValTypeF64], 18 | result_types=None) 19 | self.assertEqual("(i32,i64,f32,f64)->()", ft.get_signature()) 20 | 21 | def test_signature_result_types(self): 22 | ft = FuncType(param_types=None, 23 | result_types=[ValTypeI64]) 24 | self.assertEqual("()->(i64)", ft.get_signature()) 25 | -------------------------------------------------------------------------------- /src/develop_code/test/instance/sig_parser_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: sig_parser_test.py 6 | @time: 2020/8/25 15:21 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | 11 | import unittest 12 | 13 | from instance.sig_parser import parse_name_and_sig 14 | 15 | 16 | class TestSigParserFunc(unittest.TestCase): 17 | def test_sig_parser(self): 18 | self.sig_parser("(i32,f64)->(f32,i64)") 19 | self.sig_parser("(i32)->(f32,i64)") 20 | self.sig_parser("()->(f32)") 21 | self.sig_parser("(i32)->()") 22 | 23 | def sig_parser(self, name_and_sig): 24 | name, sig = parse_name_and_sig(name_and_sig) 25 | self.assertEqual(name_and_sig, name + sig.get_signature()) 26 | -------------------------------------------------------------------------------- /src/develop_code/test/interpreter/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py 6 | @time: 2020/8/22 15:09 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | -------------------------------------------------------------------------------- /src/develop_code/test/interpreter/instr_variable_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: instr_variable_test.py 6 | @time: 2020/8/23 12:43 7 | @project: wasm-python-book 8 | @desc: 局部/全局变量指令单元测试 9 | """ 10 | 11 | import unittest 12 | 13 | from binary.opcodes import * 14 | from binary.types import GlobalType 15 | from interpreter import * 16 | from interpreter.instructions import instr_table 17 | from interpreter.vm import VM 18 | from interpreter.vm_global import GlobalVar 19 | 20 | 21 | class TestVariableFunc(unittest.TestCase): 22 | def test_local(self): 23 | vm = VM() 24 | vm.slots = [123, 456, 789] 25 | vm.local_0_idx = 1 26 | 27 | instr_table[LocalGet](vm, uint32(1)) 28 | self.assertEqual(vm.slots[2], vm.pop_u64()) 29 | 30 | vm.push_u64(246) 31 | instr_table[LocalTee](vm, uint32(1)) 32 | self.assertEqual(vm.slots[3], vm.slots[2]) 33 | instr_table[LocalSet](vm, uint32(0)) 34 | self.assertEqual(vm.slots[2], vm.slots[1]) 35 | 36 | def test_global(self): 37 | vm = VM() 38 | vm.globals = [GlobalVar(GlobalType(mut=1), 100), 39 | GlobalVar(GlobalType(mut=1), 200), 40 | GlobalVar(GlobalType(mut=1), 300)] 41 | 42 | instr_table[GlobalGet](vm, uint32(0)) 43 | instr_table[GlobalGet](vm, uint32(1)) 44 | instr_table[GlobalGet](vm, uint32(2)) 45 | instr_table[GlobalSet](vm, uint32(1)) 46 | instr_table[GlobalSet](vm, uint32(0)) 47 | instr_table[GlobalSet](vm, uint32(2)) 48 | 49 | self.assertEqual(uint64(200), vm.globals[0].get_as_u64()) 50 | self.assertEqual(uint64(300), vm.globals[1].get_as_u64()) 51 | self.assertEqual(uint64(100), vm.globals[2].get_as_u64()) 52 | -------------------------------------------------------------------------------- /src/develop_code/test/testdata/err_2mem.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "x" "m1" (memory 1 8)) 3 | (import "x" "m2" (memory 1 8)) 4 | ) 5 | (;; 6 | err.wat:3:4: error: only one memory block allowed 7 | (import "x" "m2" (memory 1 8)) 8 | ^^^^^^ 9 | ;;) 10 | 11 | ;;------------------------------;; 12 | 13 | (module 14 | (import "x" "m1" (memory 1 8)) 15 | (memory (import "x" "m2") 1 8) 16 | ) 17 | (;; 18 | err.wat:3:4: error: only one memory block allowed 19 | (memory (import "x" "m2") 1 8) 20 | ^^^^^^ 21 | ;;) 22 | 23 | ;;------------------------------;; 24 | 25 | (module 26 | (import "x" "m1" (memory 1 8)) 27 | (memory 1 8) 28 | ) 29 | (;; 30 | err.wat:3:4: error: only one memory block allowed 31 | (memory 1 8) 32 | ^^^^^^ 33 | ;;) 34 | 35 | ;;------------------------------;; 36 | 37 | (module 38 | (memory (import "x" "m") 1 8) 39 | (memory 1 8) 40 | ) 41 | (;; 42 | err.wat:3:4: error: only one memory block allowed 43 | (memory 1 8) 44 | ^^^^^^ 45 | ;;) 46 | 47 | ;;------------------------------;; 48 | 49 | (module 50 | (memory 1 8) 51 | (memory 1 8) 52 | ) 53 | (;; 54 | err.wat:3:4: error: only one memory block allowed 55 | (memory 1 8) 56 | ^^^^^^ 57 | ;;) 58 | -------------------------------------------------------------------------------- /src/develop_code/test/testdata/err_2start.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $main) 3 | (start $main) 4 | (start $main) 5 | ) 6 | (;; 7 | err.wat:4:4: error: multiple start sections 8 | (start $main) 9 | ^^^^^ 10 | ;;) -------------------------------------------------------------------------------- /src/develop_code/test/testdata/err_2table.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "x" "t1" (table 1 2 funcref)) 3 | (import "x" "t2" (table 1 2 funcref)) 4 | ) 5 | (;; 6 | err.wat:3:4: error: only one table allowed 7 | (import "x" "t2" (table 1 2 funcref)) 8 | ^^^^^^ 9 | ;;) 10 | 11 | ;;------------------------------;; 12 | 13 | (module 14 | (import "x" "t1" (table 1 2 funcref)) 15 | (table (import "x" "t2") 1 8 funcref) 16 | ) 17 | (;; 18 | err.wat:3:4: error: only one table allowed 19 | (table (import "x" "t2") 1 8 funcref) 20 | ^^^^^ 21 | ;;) 22 | 23 | ;;------------------------------;; 24 | 25 | (module 26 | (import "x" "t1" (table 1 2 funcref)) 27 | (table 1 3 funcref) 28 | ) 29 | (;; 30 | err.wat:3:4: error: only one table allowed 31 | (table 1 3 funcref) 32 | ^^^^^ 33 | ;;) 34 | 35 | ;;------------------------------;; 36 | 37 | (module 38 | (table (import "x" "t") 1 8 funcref) 39 | (table 1 3 funcref) 40 | ) 41 | (;; 42 | err.wat:3:4: error: only one table allowed 43 | (table 1 3 funcref) 44 | ^^^^^ 45 | ;;) 46 | 47 | ;;------------------------------;; 48 | 49 | (module 50 | (table 1 3 funcref) 51 | (table 1 3 funcref) 52 | ) 53 | (;; 54 | err.wat:3:4: error: only one table allowed 55 | (table 1 3 funcref) 56 | ^^^^^ 57 | ;;) -------------------------------------------------------------------------------- /src/develop_code/test/testdata/err_br.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func 3 | (block $1 (br 100)) 4 | ) 5 | ) 6 | (;; 7 | err.wat:3:19: error: invalid depth: 100 (max 1) 8 | (block $1 (br 100)) 9 | ^^^ 10 | ;;) 11 | 12 | -------------------------------------------------------------------------------- /src/develop_code/test/testdata/err_type.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type $ft1 (func (param i32))) 3 | (func (type $ft1) (param i64)) 4 | ) 5 | (;; 6 | err.wat:3:10: error: type mismatch 7 | (func (type $ft1) (param i64)) 8 | ^^^^ 9 | ;;) 10 | 11 | ;;------------------------------;; 12 | 13 | (module 14 | (type $ft1 (func (param i32))) 15 | (func $f1 (type $ft1)) 16 | (table funcref (elem $f1)) 17 | (func 18 | (i64.const 1) 19 | (call_indirect (type $ft1) (param i64)) 20 | ) 21 | ) 22 | (;; 23 | err.wat:7:21: error: type mismatch in call_indirect 24 | (call_indirect (type $ft1) (param i64)) 25 | ^^^^ 26 | ;;) 27 | -------------------------------------------------------------------------------- /src/develop_code/test/testdata/hw_rust.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Relph1119/wasm-python-book/872bc8fe754a6a3573436f534a8da696c0486c24/src/develop_code/test/testdata/hw_rust.wasm -------------------------------------------------------------------------------- /src/develop_code/test/text/compiler_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: compiler_test.py 6 | @time: 2020/9/4 9:44 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | import glob 11 | import os 12 | import unittest 13 | 14 | from text.compiler import compile_module_file, compile_module_str 15 | 16 | 17 | class TestCompilerFunc(unittest.TestCase): 18 | def test_compile_errors(self): 19 | files = glob.glob(os.path.join("../testdata", "err_*.wat")) 20 | for file in files: 21 | self.compile_err(file) 22 | 23 | def compile_err(self, file_name): 24 | with open(file_name, mode="rb") as f: 25 | test_wat = str(f.read(), encoding='utf8') 26 | sep = ";;------------------------------;;" 27 | if test_wat.find(sep) < 0: 28 | expected_err = get_expected_err(file_name, test_wat) 29 | _, err = compile_module_file(file_name) 30 | self.assertEqual(expected_err, err.error) 31 | else: 32 | for wat in test_wat.split(sep): 33 | wat = wat.strip() 34 | expected_err = get_expected_err("Obtained from string", wat) 35 | _, err = compile_module_str(wat) 36 | self.assertEqual(expected_err, err.error) 37 | 38 | 39 | def get_expected_err(file_name, wat): 40 | start = wat.index("(;;") + 3 41 | end = wat.index(";;)") 42 | err = wat[start:end].strip() 43 | return err.replace("err.wat", file_name) 44 | -------------------------------------------------------------------------------- /src/develop_code/test/text/num_parser_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: num_parser_test.py 6 | @time: 2020/9/1 19:19 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | import math 11 | import unittest 12 | 13 | from interpreter import uint32, uint64, float32, float64 14 | from text.num_parser import parse_f32, parse_f64 15 | 16 | 17 | class TestNumParserFunc(unittest.TestCase): 18 | def test_nan(self): 19 | self.assertEqual(uint32(0x7fc00000), float32(str(math.nan))) 20 | self.assertEqual(uint32(0xffc00000), float32('-nan')) 21 | self.assertEqual(uint32(0x7f800000), float32(str(math.inf))) 22 | self.assertEqual(uint32(0xff800000), float32('-inf')) 23 | self.assertEqual(uint64(0x7ff8000000000001), float64(str(math.nan))) 24 | self.assertEqual(uint64(0xfff8000000000001), float64('-nan')) 25 | self.assertEqual(uint64(0x7ff0000000000000), float64(str(math.inf))) 26 | self.assertEqual(uint64(0xfff0000000000000), float64('-inf')) 27 | 28 | def test_parse_float32(self): 29 | self.assertEqual(parse_f32("+0x1.00000100000000001p-50"), parse_f32("+0x1.000001p-50")) 30 | self.assertEqual(parse_f32("+0x1.000001fffffffffffp-50"), parse_f32("+0x1.000001fp-50")) 31 | 32 | def test_parse_float64(self): 33 | f1 = parse_f64("-0x1.fffffffffffff7ffffffp1023") 34 | f2 = parse_f64("-0x1.fffffffffffffp1023") 35 | self.assertEqual(f1, f2) 36 | -------------------------------------------------------------------------------- /src/develop_code/text/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: __init__.py.py 6 | @time: 2020/8/27 11:10 7 | @project: wasm-python-book 8 | @desc: 9 | """ -------------------------------------------------------------------------------- /src/develop_code/text/builder_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: builder_script.py 6 | @time: 2020/9/3 9:51 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from text.wast_script import Script 11 | 12 | 13 | class ScriptBuilder: 14 | def __init__(self, script=None): 15 | self.script = script 16 | 17 | def add_cmd(self, cmd): 18 | self.script.cmds.append(cmd) 19 | 20 | 21 | def new_script_builder(): 22 | return ScriptBuilder(script=Script()) 23 | -------------------------------------------------------------------------------- /src/develop_code/text/error_listener.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: error_listener.py 6 | @time: 2020/8/31 10:10 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from antlr4.error.ErrorListener import ErrorListener 11 | 12 | from text.errors import SyntaxErrors, SyntaxError 13 | 14 | 15 | class TextErrorListener(ErrorListener): 16 | def __init__(self): 17 | super().__init__() 18 | self.errors = SyntaxErrors() 19 | self.all_text = [] 20 | 21 | def get_errors(self, input): 22 | if len(self.errors) > 0: 23 | errs = self.errors 24 | errs.fill_detail(input) 25 | return errs 26 | else: 27 | return None 28 | 29 | def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): 30 | err = SyntaxError(msg=msg, 31 | line=line, 32 | column=column, 33 | token=offendingSymbol) 34 | self.errors.append(err) 35 | -------------------------------------------------------------------------------- /src/develop_code/text/error_reporter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | @author: HuRuiFeng 5 | @file: error_reporter.py 6 | @time: 2020/8/31 18:29 7 | @project: wasm-python-book 8 | @desc: 9 | """ 10 | from antlr4 import Token, TerminalNode 11 | 12 | from text.errors import SemanticError, ValidationError 13 | from text.parser.WASTParser import ParserRuleContext 14 | 15 | 16 | class ErrorReporter: 17 | def __init__(self): 18 | self.reports_validation_error = False 19 | 20 | def report_err(self, err, node): 21 | if err is not None: 22 | if isinstance(err, SemanticError): 23 | err.token = get_token(node) 24 | raise err 25 | elif isinstance(err, ValidationError): 26 | err.token = get_token(node) 27 | if self.reports_validation_error: 28 | raise err 29 | else: 30 | raise err 31 | 32 | 33 | def get_token(node): 34 | if isinstance(node, Token): 35 | return node 36 | elif isinstance(node, TerminalNode): 37 | return node 38 | elif isinstance(node, ParserRuleContext): 39 | return get_token(node.getChild(0)) 40 | else: 41 | raise Exception("TODO") 42 | -------------------------------------------------------------------------------- /src/develop_code/text/grammar/WAST.g4: -------------------------------------------------------------------------------- 1 | grammar WAST; 2 | import WAT; 3 | 4 | // https://github.com/WebAssembly/spec/tree/master/interpreter#scripts 5 | 6 | script : cmd* EOF ; 7 | 8 | cmd : wastModule 9 | | '(' 'register' STRING NAME? ')' 10 | | action_ 11 | | assertion 12 | | meta 13 | ; 14 | 15 | wastModule : watModule 16 | | '(' 'module' NAME? kind='binary' STRING* ')' 17 | | '(' 'module' NAME? kind='quote' STRING* ')' 18 | ; 19 | 20 | action_ : '(' kind='invoke' NAME? STRING expr ')' 21 | | '(' kind='get' NAME? STRING ')' 22 | ; 23 | 24 | assertion : '(' kind='assert_return' action_ expected* ')' 25 | | '(' kind='assert_trap' action_ STRING ')' 26 | | '(' kind='assert_exhaustion' action_ STRING ')' 27 | | '(' kind='assert_malformed' wastModule STRING ')' 28 | | '(' kind='assert_invalid' wastModule STRING ')' 29 | | '(' kind='assert_unlinkable' wastModule STRING ')' 30 | | '(' kind='assert_trap' wastModule STRING ')' 31 | ; 32 | expected : '(' constInstr ')' 33 | | '(' op=CST_OPS nan='nan:canonical' ')' 34 | | '(' op=CST_OPS nan='nan:arithmetic' ')' 35 | ; 36 | 37 | meta : '(' 'script' NAME? script ')' 38 | | '(' 'input' NAME? STRING ')' 39 | | '(' 'output' NAME? STRING? ')' 40 | ; 41 | -------------------------------------------------------------------------------- /src/develop_code/text/grammar/pygrun.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | IF NOT "%~f0" == "~f0" GOTO :WinNT 3 | @"python.exe"