Watgo: Bộ công cụ WebAssembly mạnh mẽ viết bằng ngôn ngữ Go
Watgo là bộ công cụ WebAssembly (WASM) mới được phát triển hoàn toàn bằng ngôn ngữ lập trình Go, không phụ thuộc vào thư viện bên ngoài. Dự án này cung cấp giao diện dòng lệnh và API linh hoạt để phân tích cú pháp, xác thực và mã hóa các tệp WAT sang định dạng nhị phân WASM.

Tôi rất vui mừng thông báo sự ra mắt chính thức của watgo — Bộ công cụ WebAssembly dành cho Go. Dự án này có chức năng tương tự như wabt (viết bằng C++) hay wasm-tools (viết bằng Rust), nhưng điểm khác biệt cốt lõi là watgo được viết hoàn toàn bằng ngôn ngữ Go thuần túy với zero-dependency (không có các phụ thuộc bên ngoài).
Watgo đi kèm với một giao diện dòng lệnh (CLI) và một API dành cho Go, cho phép người dùng phân tích cú pháp WAT (WebAssembly Text), xác thực tính hợp lệ của nó và mã hóa thành các tệp nhị phân WASM. Ngoài ra, công cụ này cũng hỗ trợ giải mã (decode) từ định dạng nhị phân của WASM.
Trọng tâm của toàn bộ dự án là wasmir — một biểu diễn ngữ nghĩa của mô-đun WebAssembly mà người dùng có thể kiểm tra và thao tác một cách dễ dàng.
Sơ đồ các chức năng chính do watgo cung cấp
Các chức năng chính
Watgo cung cấp bốn chức năng cốt lõi làm việc với các mô-đun WebAssembly:
- Parse (Phân tích): Trình phân tích từ định dạng WAT sang cấu trúc
wasmir. - Validate (Xác thực): Sử dụng ngữ nghĩa xác thực chính thức của WebAssembly để kiểm tra xem mô-đun có được định dạng đúng và an toàn hay không.
- Encode (Mã hóa): Xuất cấu trúc
wasmirsang dạng biểu diễn nhị phân WASM. - Decode (Giải mã): Đọc biểu diễn nhị phân WASM và chuyển đổi thành cấu trúc
wasmir.
Sử dụng qua dòng lệnh (CLI)
Watgo được tích hợp sẵn một công cụ dòng lệnh mạnh mẽ. Bạn có thể cài đặt nó bằng lệnh sau:
go install github.com/eliben/watgo/cmd/watgo@latest
CLI này được thiết kế để tương thích với wasm-tools. Tôi đã chuyển đổi các dự án mẫu của mình sang sử dụng watgo. Ví dụ, lệnh dưới đây sẽ phân tích một tệp WAT, xác thực nó và mã hóa sang định dạng nhị phân:
watgo parse stack.wat -o stack.wasm
Sử dụng qua API
wasmir biểu diễn ngữ nghĩa một mô-đun WASM thông qua một API dễ làm việc. Dưới đây là ví dụ về việc sử dụng watgo để phân tích một chương trình WAT đơn giản và thực hiện một số phân tích dữ liệu:
package main
import (
"fmt"
"github.com/eliben/watgo"
"github.com/eliben/watgo/wasmir"
)
const wasmText = `
(module
(func (export "add") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add)
(func (param f32 i32) (result i32)
local.get 1
i32.const 1
i32.add
drop
i32.const 0)
)`
func main() {
m, err := watgo.ParseWAT([]byte(wasmText))
if err != nil {
panic(err)
}
i32Params := 0
localGets := 0
i32Adds := 0
// Các hàm do mô-đun định nghĩa mang một chỉ số kiểu vào m.Types.
// Thân hàm thực tế là một chuỗi phẳng các giá trị wasmir.Instruction.
for _, fn := range m.Funcs {
sig := m.Types[fn.TypeIdx]
for _, param := range sig.Params {
if param.Kind == wasmir.ValueKindI32 {
i32Params++
}
}
for _, instr := range fn.Body {
switch instr.Kind {
case wasmir.InstrLocalGet:
localGets++
case wasmir.InstrI32Add:
i32Adds++
}
}
}
fmt.Printf("module-defined funcs: %d\n", len(m.Funcs))
fmt.Printf("i32 params: %d\n", i32Params)
fmt.Printf("local.get instructions: %d\n", localGets)
fmt.Printf("i32.add instructions: %d\n", i32Adds)
}
Một điểm quan trọng cần lưu ý là định dạng WAT hỗ trợ nhiều cú pháp tiện lợi, nhưng chúng sẽ được làm phẳng (flattened) hoặc chuẩn hóa (canonicalized) khi chuyển xuống cấp wasmir. Ví dụ, tất cả các lệnh gấp (folded instructions) đều được chuyển thành dạng không gấp (linear form), tên hàm và kiểu được giải quyết thành các chỉ số số, v.v. Điều này phù hợp với ngữ nghĩa xác thực và thực thi của WASM cũng như biểu diễn nhị phân của nó.
Các chi tiết cú pháp này có trong watgo thông qua gói textformat (gói này phân tích WAT thành AST) và sẽ bị loại bỏ khi chuyển xuống wasmir. Hiện tại, gói textformat được giữ nội bộ, nhưng trong tương lai, tôi có thể cân nhắc công khai nó nếu có sự quan tâm từ cộng đồng.
Chiến lược kiểm thử
Mặc dù vẫn còn ở những ngày đầu, tôi khá tự tin vào độ chính xác của watgo nhờ chiến lược kiểm thử rất kỹ lưỡng ngay từ đầu.
WebAssembly đi kèm với một bộ kiểm thử chính thức rất lớn, hoàn hảo cho việc kiểm thử đầu cuối (end-to-testing) đối với các bản triển khai mới. Bộ kiểm thử cốt lõi bao gồm gần 200.000 dòng tệp WAT chứa nhiều mô-đun với ngữ nghĩa thực thi mong đợi và nhiều kịch bản lỗi khác nhau. Các tệp này nằm trong các tệp .wast được thiết kế đặc biệt và sử dụng một trình thông dịch spec tùy chỉnh.
Watgo tận dụng cách tiếp cận này bằng cách sử dụng bộ kiểm thử chính thức cho mục đích kiểm thử của riêng mình. Một bộ khung (harness) tùy chỉnh sẽ phân tích các tệp .wast và sử dụng watgo để chuyển đổi WAT bên trong thành nhị phân WASM, sau đó thực thi bởi Node.js. Việc xây dựng bộ khung này tốn nhiều công sức, nhưng hoàn toàn xứng đáng — kết quả là độ phủ kiểm thử tuyệt vời. Watgo đã vượt qua toàn bộ bộ kiểm thử cốt lõi của spec WASM.
Tương tự, chúng tôi tận dụng bộ kiểm thử interp của wabt, vốn cũng bao gồm các kiểm thử đầu cuối, sử dụng một bộ khung dựa trên Node.js đơn giản hơn để kiểm tra chúng với watgo.
Cuối cùng, tôi duy trì một bộ sưu tập các mẫu chương trình thực tế viết bằng WAT trong kho lưu trữ wasm-wat-samples; các mẫu này cũng được watgo sử dụng để tự kiểm thử.
Để duy trì cách tiếp cận thuần Go cả trong việc kiểm thử, tôi ban đầu đã thử sử dụng wazero cho việc này, nhưng buộc phải từ bỏ vì wazero không hỗ trợ một số đề xuất WASM gần đây đã được đưa vào tiêu chuẩn (đáng chú ý nhất là Garbage Collection).



