This adds `RuntimeConfig.WithFeatureMutableGlobal(enabled bool)`, which allows disabling of mutable globals. When disabled, any attempt to add a mutable global, either explicitly or implicitly via decoding wasm will fail. To support this, there's a new `Features` bitflag that can allow up to 63 feature toggles without passing structs. While here, I fixed a significant performance problem in decoding binary: Before ``` BenchmarkCodecExample/binary.DecodeModule-16 184243 5623 ns/op 3848 B/op 184 allocs/op ``` Now ``` BenchmarkCodecExample/binary.DecodeModule-16 294084 3520 ns/op 2176 B/op 91 allocs/op ``` Signed-off-by: Adrian Cole <adrian@tetrate.io>
296 lines
8.9 KiB
Go
296 lines
8.9 KiB
Go
package binary
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/tetratelabs/wazero/internal/leb128"
|
|
wasm "github.com/tetratelabs/wazero/internal/wasm"
|
|
)
|
|
|
|
func decodeTypeSection(r *bytes.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 *bytes.Reader) (*wasm.FunctionType, error) {
|
|
b, err := r.ReadByte()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read leading byte: %w", err)
|
|
}
|
|
|
|
if b != 0x60 {
|
|
return nil, fmt.Errorf("%w: %#x != 0x60", ErrInvalidByte, b)
|
|
}
|
|
|
|
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, features wasm.Features) ([]*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, i, features); err != nil {
|
|
return nil, 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, features wasm.Features) ([]*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, features); err != nil {
|
|
return nil, fmt.Errorf("global[%d]: %w", i, 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/2019/REC-wasm-core-1-20191205/#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 (20191205) Binary Format.
|
|
//
|
|
// See encodeFunctionType
|
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#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 (20191205) Binary Format.
|
|
//
|
|
// See encodeImport
|
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#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 (20191205) Binary Format.
|
|
//
|
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#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 (20191205) Binary Format.
|
|
//
|
|
// See encodeCode
|
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#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 (20191205) Binary Format.
|
|
//
|
|
// See encodeMemoryType
|
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#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 (20191205) Binary Format.
|
|
//
|
|
// See encodeExport
|
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#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 (20191205) Binary Format.
|
|
//
|
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#start-section%E2%91%A0
|
|
func encodeStartSection(funcidx wasm.Index) []byte {
|
|
return encodeSection(wasm.SectionIDStart, leb128.EncodeUint32(funcidx))
|
|
}
|