wasm-python-book
wasm-python-book copied to clipboard
《WebAssembly原理与核心技术》WebAssembly解释器的Python实现
WebAssembly解释器的Python实现
项目完全参考张秀宏的《WebAssembly原理与核心技术》代码结构,在此向本书作者表示感谢。
该书的随书代码github地址:https://github.com/zxh0/wasmgo-book
运行环境
Python 版本:3.7.2
PyCharm 版本:PyCharm 2018.3.7 (Professional Edition)
Wabt版本:wabt-1.0.19
环境及工具下载地址
链接:https://pan.baidu.com/s/10R8n71TNTwBvkUZ9WLmCHg 提取码:citl
代码结构
images---------------------------------运行截图 js-------------------------------------对应第1、2章中的wasm文件 spec-----------------------------------spec test文件 src------------------------------------WebAssembly代码 +-----ch02-----------------------------对应书中第2章实现代码 +-----ch03-----------------------------对应书中第3章实现代码 +-----ch05-----------------------------对应书中第5章实现代码 +-----ch06-----------------------------对应书中第6章实现代码 +-----ch07-----------------------------对应书中第7章实现代码 +-----ch08-----------------------------对应书中第8章实现代码 +-----ch09-----------------------------对应书中第9章实现代码 +-----ch10-----------------------------对应书中第10章实现代码 +-----ch11-----------------------------对应书中第11章实现代码 +-----develop_code---------------------持续开发的实现代码 +-----binary---------------------指令解码 +-----cmd------------------------主函数执行 +-----instance-------------------实例 +-----interpreter----------------指令集 +-----validator------------------模块验证 +-----test-----------------------单元测试 wat------------------------------------测试的wat格式文件
注意:
- 将src和develop_code设置成sources Root,可避免代码的引包报错。
- 可通过使用wat2wasm指令将wat文件编译成wasm格式文件
wat2wasm xxx.wat
代码编写与运行结果
项目的所有运行都是采用直接运行cmd/main.py的方式,请读者运行时注意。
第2章-二进制格式
完成了模块解码器和dump程序。 传入参数:
-d "wasm-python-book\js\ch01_hw.wasm"
- 采用unittest进行单元测试
- 由于使用小端格式读取数值,python可采用byteorder的little入参
int.from_bytes(self.data[:8], byteorder='little')
- 如果该结构体是数组,由于Python无法表示结构数组,故类初始化的时候初始一个数组。
第3章-指令集
完成了指令的解码。
第5章-操作数栈
实现了操作栈和虚拟机框架,然后实现了参数和数值指令。
- 针对大整数除法,python需要引入decimal包的Decimal类。
from decimal import Decimal v1 = Decimal(18446744073709551608) v2 = 2 result = int(v1 / v2) assert result * v2 == v1
- 由于python没有uint32和int32等类,故在interpreter/__init__.py代码中实现了相关的类(int8、int16、int32、int64、uint32、uint64、float32、float64)
- 在操作数栈中,float32和float64存储的是编码之后的整数,pop均需要进行对应的解码:
def push_f32(self, val): val = struct.unpack('>l', struct.pack('>f', val))[0] self.push_u64(val) def pop_f32(self): val = self.pop_u64() val = struct.unpack('>f', struct.pack('>l', val))[0] return float32(val)
第6章-内存
实现了Wasm内存和相关指令,Wasm内存是一块抽象的RAM(本质上就是个线性的字节数组),并且可以在限制范围内按页动态增长。Wasm提供了丰富的内存指令,用于读写各种基本类型的数值,这些数值在Wasm内存中按小端方式存储。简而言之,Wasm内存和真实内存非常接近,只具备最基本的读写能力,像内存管理、垃圾回收这些高级功能都要靠高级语言自己去解决。也正是因为贴近底层,Wasm程序才能够以接近本地程序的速度被执行。
第7章-函数调用(上)
实现了直接函数调用指令、局部变量和全局变量指令。
第8章-控制指令
传统的goto/jump指令被Wasm摒弃,取而代之的是结构化控制指令(block、loop、if)和受限的分支指令(br等)。
新增"--verbose"命令,调用如下:
--verbose "wasm-python-book\wat\ch08_fac.wasm"
第9章-函数调用(下)
设计了本地函数调用接口,并且实现了本地函数调用,然后实现了表和间接函数调用指令。
-
执行HelloWorld程序,得到打印结果为"Hello, World!"
-
执行ch09_calc程序,得到加法、减法和乘法的验证结果
第10章-链接和实例化
块从二进制格式到函数执行分为3个语义阶段:解码、验证、执行(实例化、函数调用)。模块可以导出或导入4种成员:函数、表、内存、全局变量,多个模块实例链接在一起,共享这4种成员,本章实现模块的链接和实例化。
第11章-错误处理和验证
Wasm采用类型安全的操作数栈以及结构化控制指令和受限的跳转指令,这使得函数的字节码很容易被验证,而且可以在线性时间内完成。
总结
历时7天(2020年08月18日——2020年8月24日)完成1-11章的代码,基本实现了一个Wasm解析器,提供如下命令:
-v, --version : 版本号
-d, --dump : 打印模块信息
由于之前有JVMByPython项目的代码实现基础,目前已完成的功能有Wasm的二进制格式解码、指令集、操作数栈和虚拟机、内存及内存指令、函数调用(本地方法和外部方法调用)、实例化模块、模块错误处理和验证等。
另:挺感谢作者在我实现代码期间给予的指导和帮助,目前该项目还需要完成spec的wast格式解析和测试、AOT和JIT的实现等。
Spec Test
未测试通过的文件:
- br_table.wast:运行时间太长
- const.wast:float的精度问题超过小数点后面7位就不精确了
- conversions.wast:float的精度问题
- f32.wast
- f32_bitwise.wast
- f64.wast
- f64_bitwise.wast
- float_exprs.wast
- float_literals.wast
- float_memory.wast
- float_misc.wast
- i64.wast:如果数值超过了uint64表示的最大数值,应显示为1的问题
已测试通过的文件:
- address.wast
- align.wast
- binary.wast
- binary-leb128.wast
- block.wast
- br.wast
- br_if.wast
- call.wast
- call_indirect.wast
- comments.wast
- custom.wast
- data.wast
- elem.wast
- endianness.wast
- exports.wast
- f32_cmp.wast
- f64_cmp.wast
- fac.wast
- forward.wast
- func.wast
- func_ptrs.wast
- global.wast
- i32.wast
- if.wast
- imports.wast
- inline-module.wast
- table.wast