#语法树 #Wasm 解释器 #解释器

wain-exec

Wain 项目中的 WebAssembly 语法树执行器

8 个版本

0.3.1 2023年11月18日
0.3.0 2020年6月11日
0.2.2 2020年5月24日
0.2.1 2020年4月14日
0.1.0 2020年3月29日

#318WebAssembly

Download history 33/week @ 2024-04-16 21/week @ 2024-04-23 18/week @ 2024-07-02 52/week @ 2024-07-30

每月 52 次下载
用于 wain

MIT 许可证

150KB
3K SLoC

wain-exec

crates.io CI

wain-exec 是一个用于执行 WebAssembly 抽象语法树的 crate。执行逻辑在 规范 中定义

此 crate 是更大 wain 项目的组成部分。

安装

[dependencies]
wain-exec = "0"

使用

此 crate 假设给定的 Wasm 语法树已被 验证

它接受 wain_ast::Module 值并验证它。该树可以通过 wain-syntax-binarywain-syntax-text 解析器进行解析,并通过 wain-validate 验证器进行验证

使用 wain_exec::execute() 是最简单的方法。如果存在,它会调用给定模块中的启动函数。否则,它将调用作为 _start 导出的函数。

extern crate wain_syntax_binary;
extern crate wain_validate;
extern crate wain_exec;

use std::fs;
use std::process::exit;
use wain_syntax_binary::parse;
use wain_validate::validate;
use wain_exec::execute;

// Read wasm binary
let source = fs::read("foo.wasm").unwrap();

// Parse binary into syntax tree
let tree = match parse(&source) {
    Ok(tree) => tree,
    Err(err) => {
        eprintln!("Could not parse: {}", err);
        exit(1);
    }
};

// Validate module
if let Err(err) = validate(&tree) {
    eprintln!("This .wasm file is invalid!: {}", err);
    exit(1);
}

// Execute module
if let Err(trap) = execute(&tree.module) {
    eprintln!("Execution was trapped: {}", trap);
    exit(1);
}

或者调用具有参数的特定导出函数

// ...(snip)

use wain_exec::{Runtime, DefaultImporter, Value};
use std::io;

// Create default importer to call external function supported by default
let stdin = io::stdin();
let stdout = io::stdout();
let importer = DefaultImporter::with_stdio(stdin.lock(), stdout.lock());

// Make abstract machine runtime. It instantiates a module instance
let mut runtime = match Runtime::instantiate(&tree.module, importer) {
    Ok(m) => m,
    Err(err) => {
        eprintln!("could not instantiate module: {}", err);
        exit(1);
    }
};

// Let's say `int add(int, int)` is exported
match runtime.invoke("add", &[Value::I32(10), Value::I32(32)]) {
    Ok(ret) => {
        // `ret` is type of `Option<Value>` where it contains `Some` value when the invoked
        // function returned a value. Otherwise it's `None` value.
        if let Some(Value::I32(i)) = ret {
            println!("10 + 32 = {}", i);
        } else {
            unreachable!();
        }
    }
    Err(trap) => eprintln!("Execution was trapped: {}", trap),
}

陷阱 作为 Err 部分返回。

wain_exec::execute() 默认情况下缓冲 stdin 和 stdout(此行为可能在未来发生变化)。如果不接受这种行为,请在 wain_exec::Runtime::new() 中指定您的 stdout/stdin 的 io::Write/io::Read 值。然后通过 wain_exec::Runtime::invoke() 运行模块。

默认情况下,以下 C 函数在 env 模块中作为外部函数受支持

  • int putchar(int) (在wasm中 (func (param i32) (result i32)))
  • int getchar(void) (在wasm中 (func (param) (result i32)))
  • void *memcpy(void *, void *, size_t) (在wasm中 (func (param i32 i32 i32) (result i32)))
  • void abort(void) (在wasm中 (func (param) (result)))

但您可以自己实现一个结构体,该结构体实现了从Rust侧定义外部函数的wain_exec::Importer

extern crate wain_exec;
extern crate wain_ast;
use wain_exec::{Runtime, Stack, Memory, Importer, ImportInvokeError, ImportInvalidError}
use wain_ast::ValType;

struct YourOwnImporter {
    // ...
}

impl Importer for YourOwnImporter {
    fn validate(&self, name: &str, params: &[ValType], ret: Option<ValType>) -> Option<ImportInvalidError> {
        // `name` is a name of function to validate. `params` and `ret` are the function's signature.
        // Return ImportInvalidError::NotFound when the name is unknown.
        // Return ImportInvalidError::SignatureMismatch when signature does not match.
        // wain_exec::check_func_signature() utility is would be useful for the check.
    }
    fn call(&mut self, name: &str, stack: &mut Stack, memory: &mut Memory) -> Result<(), ImportInvokeError> {
        // Implement your own function call. `name` is a name of function and you have full access
        // to stack and linear memory. Pop values from stack for getting arguments and push value to
        // set return value.
        // Note: Consistency between imported function signature and implementation of this method
        // is your responsibility.
        // On invocation failure, return ImportInvokeError::Fatal. It is trapped by interpreter and it
        // stops execution immediately.
    };
}

let ast = ...; // Parse abstract syntax tree and validate it

let mut runtime = Runtime::instantiate(&ast.module, YourOwnImporter{ /* ... */ }).unwrap();
let result = runtime.invoke("do_something", &[]);

工作示例可以在examples/api/目录中查看。

请阅读文档(尚未)以获取详细信息。

实现

由于验证,运行时检查是最小的(例如,间接调用上的函数签名)。

  1. 分配内存、表、全局变量。初始化栈
  2. 解释语法树节点,将值从栈中推入或弹出

目前wain直接解释Wasm语法树。我计划定义一个中间表示形式,它可以更快地解释。

入口点是“启动函数”,它可以是以下之一

  1. start部分中的函数集
  2. 导出部分中名为_start的导出函数

第1项是标准入口点,但Clang不生成start部分。相反,它将_start函数作为入口点处理。wain实现了这两个入口点(1.优先)。

许可证

MIT许可证

依赖项