├── .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"