Files
wazero/internal/wasm/binary/section.go
Crypt Keeper 5180d2d9c3 Refactors out public API from internals (#238)
This moves to a new end-user API under the root package `wazero`. This
simplifies call sites while hardening function calls to their known
return value. Most importantly, this moves most logic internal, as
noted in the RATIONALE.md.

Ex.

```go
	// Read WebAssembly binary containing an exported "fac" function.
	source, _ := os.ReadFile("./tests/engine/testdata/fac.wasm")

	// Decode the binary as WebAssembly module.
	mod, _ := wazero.DecodeModuleBinary(source)

	// Initialize the execution environment called "store" with Interpreter-based engine.
	store := wazero.NewStore()

	// Instantiate the module, which returns its exported functions
	functions, _ := store.Instantiate(mod)

	// Get the factorial function
	fac, _ := functions.GetFunctionI64Return("fac")

	// Discover 7! is 5040
	fmt.Println(fac(context.Background(), 7))

```

PS I changed the README to factorial because the wat version of
fibonacci is not consistent with the TinyGo one!

Signed-off-by: Adrian Cole <adrian@tetrate.io>
Co-authored-by: Takaya Saeki <takaya@tetrate.io>
Co-authored-by: Takeshi Yoneda <takeshi@tetrate.io>
2022-02-17 17:39:28 +08:00

297 lines
8.7 KiB
Go

package binary
import (
"bytes"
"fmt"
"io"
"github.com/tetratelabs/wazero/internal/leb128"
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
func decodeTypeSection(r io.Reader) ([]*wasm.FunctionType, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]*wasm.FunctionType, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeFunctionType(r); err != nil {
return nil, fmt.Errorf("read %d-th type: %v", i, err)
}
}
return result, nil
}
func decodeFunctionType(r io.Reader) (*wasm.FunctionType, error) {
b := make([]byte, 1)
if _, err := io.ReadFull(r, b); err != nil {
return nil, fmt.Errorf("read leading byte: %w", err)
}
if b[0] != 0x60 {
return nil, fmt.Errorf("%w: %#x != 0x60", ErrInvalidByte, b[0])
}
s, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("could not read parameter count: %w", err)
}
paramTypes, err := decodeValueTypes(r, s)
if err != nil {
return nil, fmt.Errorf("could not read parameter types: %w", err)
}
s, _, err = leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("could not read result count: %w", err)
} else if s > 1 {
return nil, fmt.Errorf("multi value results not supported")
}
resultTypes, err := decodeValueTypes(r, s)
if err != nil {
return nil, fmt.Errorf("could not read result types: %w", err)
}
return &wasm.FunctionType{
Params: paramTypes,
Results: resultTypes,
}, nil
}
func decodeImportSection(r *bytes.Reader) ([]*wasm.Import, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]*wasm.Import, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeImport(r); err != nil {
return nil, fmt.Errorf("read import: %w", err)
}
}
return result, nil
}
func decodeFunctionSection(r *bytes.Reader) ([]uint32, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]uint32, vs)
for i := uint32(0); i < vs; i++ {
if result[i], _, err = leb128.DecodeUint32(r); err != nil {
return nil, fmt.Errorf("get type index: %w", err)
}
}
return result, err
}
func decodeTableSection(r *bytes.Reader) ([]*wasm.TableType, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]*wasm.TableType, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeTableType(r); err != nil {
return nil, fmt.Errorf("read table type: %w", err)
}
}
return result, nil
}
func decodeMemorySection(r *bytes.Reader) ([]*wasm.MemoryType, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]*wasm.MemoryType, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeMemoryType(r); err != nil {
return nil, fmt.Errorf("read memory type: %w", err)
}
}
return result, nil
}
func decodeGlobalSection(r *bytes.Reader) ([]*wasm.Global, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]*wasm.Global, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeGlobal(r); err != nil {
return nil, fmt.Errorf("read global: %v ", err)
}
}
return result, nil
}
func decodeExportSection(r *bytes.Reader) (map[string]*wasm.Export, error) {
vs, _, sizeErr := leb128.DecodeUint32(r)
if sizeErr != nil {
return nil, fmt.Errorf("get size of vector: %v", sizeErr)
}
exportSection := make(map[string]*wasm.Export, vs)
for i := wasm.Index(0); i < vs; i++ {
export, err := decodeExport(r)
if err != nil {
return nil, fmt.Errorf("read export: %w", err)
}
if _, ok := exportSection[export.Name]; ok {
return nil, fmt.Errorf("export[%d] duplicates name %q", i, export.Name)
}
exportSection[export.Name] = export
}
return exportSection, nil
}
func decodeStartSection(r *bytes.Reader) (*wasm.Index, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
return &vs, nil
}
func decodeElementSection(r *bytes.Reader) ([]*wasm.ElementSegment, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]*wasm.ElementSegment, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeElementSegment(r); err != nil {
return nil, fmt.Errorf("read element: %w", err)
}
}
return result, nil
}
func decodeCodeSection(r *bytes.Reader) ([]*wasm.Code, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]*wasm.Code, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeCode(r); err != nil {
return nil, fmt.Errorf("read %d-th code segment: %v", i, err)
}
}
return result, nil
}
func decodeDataSection(r *bytes.Reader) ([]*wasm.DataSegment, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
}
result := make([]*wasm.DataSegment, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeDataSegment(r); err != nil {
return nil, fmt.Errorf("read data segment: %w", err)
}
}
return result, nil
}
// encodeSection encodes the sectionID, the size of its contents in bytes, followed by the contents.
// See https://www.w3.org/TR/wasm-core-1/#sections%E2%91%A0
func encodeSection(sectionID wasm.SectionID, contents []byte) []byte {
return append([]byte{sectionID}, encodeSizePrefixed(contents)...)
}
// encodeTypeSection encodes a SectionIDType for the given imports in WebAssembly 1.0 (MVP) Binary Format.
//
// See encodeFunctionType
// See https://www.w3.org/TR/wasm-core-1/#type-section%E2%91%A0
func encodeTypeSection(types []*wasm.FunctionType) []byte {
contents := leb128.EncodeUint32(uint32(len(types)))
for _, t := range types {
contents = append(contents, encodeFunctionType(t)...)
}
return encodeSection(wasm.SectionIDType, contents)
}
// encodeImportSection encodes a SectionIDImport for the given imports in WebAssembly 1.0 (MVP) Binary Format.
//
// See encodeImport
// See https://www.w3.org/TR/wasm-core-1/#import-section%E2%91%A0
func encodeImportSection(imports []*wasm.Import) []byte {
contents := leb128.EncodeUint32(uint32(len(imports)))
for _, i := range imports {
contents = append(contents, encodeImport(i)...)
}
return encodeSection(wasm.SectionIDImport, contents)
}
// encodeFunctionSection encodes a SectionIDFunction for the type indices associated with module-defined functions in
// WebAssembly 1.0 (MVP) Binary Format.
//
// See https://www.w3.org/TR/wasm-core-1/#function-section%E2%91%A0
func encodeFunctionSection(typeIndices []wasm.Index) []byte {
contents := leb128.EncodeUint32(uint32(len(typeIndices)))
for _, index := range typeIndices {
contents = append(contents, leb128.EncodeUint32(index)...)
}
return encodeSection(wasm.SectionIDFunction, contents)
}
// encodeCodeSection encodes a SectionIDCode for the module-defined function in WebAssembly 1.0 (MVP) Binary Format.
//
// See encodeCode
// See https://www.w3.org/TR/wasm-core-1/#code-section%E2%91%A0
func encodeCodeSection(code []*wasm.Code) []byte {
contents := leb128.EncodeUint32(uint32(len(code)))
for _, i := range code {
contents = append(contents, encodeCode(i)...)
}
return encodeSection(wasm.SectionIDCode, contents)
}
// encodeMemorySection encodes a SectionIDMemory for the module-defined function in WebAssembly 1.0 (MVP) Binary Format.
//
// See encodeMemoryType
// See https://www.w3.org/TR/wasm-core-1/#memory-section%E2%91%A0
func encodeMemorySection(memories []*wasm.MemoryType) []byte {
contents := leb128.EncodeUint32(uint32(len(memories)))
for _, i := range memories {
contents = append(contents, encodeMemoryType(i)...)
}
return encodeSection(wasm.SectionIDMemory, contents)
}
// encodeExportSection encodes a SectionIDExport for the given exports in WebAssembly 1.0 (MVP) Binary Format.
//
// See encodeExport
// See https://www.w3.org/TR/wasm-core-1/#export-section%E2%91%A0
func encodeExportSection(exports map[string]*wasm.Export) []byte {
contents := leb128.EncodeUint32(uint32(len(exports)))
for _, e := range exports {
contents = append(contents, encodeExport(e)...)
}
return encodeSection(wasm.SectionIDExport, contents)
}
// encodeStartSection encodes a SectionIDStart for the given function index in WebAssembly 1.0 (MVP) Binary Format.
//
// See https://www.w3.org/TR/wasm-core-1/#start-section%E2%91%A0
func encodeStartSection(funcidx wasm.Index) []byte {
return encodeSection(wasm.SectionIDStart, leb128.EncodeUint32(funcidx))
}