Files
wazero/internal/wasm/binary/function.go
Crypt Keeper b01effc8a9 Top-levels CoreFeatures and defaults to 2.0 (#800)
While compilers should be conservative when targeting WebAssembly Core
features, runtimes should be lenient as otherwise people need to
constantly turn on all features. Currently, most examples have to turn
on 2.0 features because compilers such as AssemblyScript and TinyGo use
them by default. This matches the policy with the reality, and should
make first time use easier.

This top-levels an internal type as `api.CoreFeatures` and defaults to
2.0 as opposed to 1.0, our previous default. This is less cluttered than
the excess of `WithXXX` methods we had prior to implementing all
planned WebAssembly Core Specification 1.0 features.

Finally, this backfills rationale as flat config types were a distinct
decision even if feature set selection muddied the topic.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-09-06 15:14:36 +08:00

102 lines
3.1 KiB
Go

package binary
import (
"bytes"
"fmt"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/leb128"
"github.com/tetratelabs/wazero/internal/wasm"
)
var nullary = []byte{0x60, 0, 0}
// encodedOneParam is a cache of wasm.FunctionType values for param length 1 and result length 0
var encodedOneParam = map[wasm.ValueType][]byte{
wasm.ValueTypeI32: {0x60, 1, wasm.ValueTypeI32, 0},
wasm.ValueTypeI64: {0x60, 1, wasm.ValueTypeI64, 0},
wasm.ValueTypeF32: {0x60, 1, wasm.ValueTypeF32, 0},
wasm.ValueTypeF64: {0x60, 1, wasm.ValueTypeF64, 0},
}
// encodedOneResult is a cache of wasm.FunctionType values for param length 0 and result length 1
var encodedOneResult = map[wasm.ValueType][]byte{
wasm.ValueTypeI32: {0x60, 0, 1, wasm.ValueTypeI32},
wasm.ValueTypeI64: {0x60, 0, 1, wasm.ValueTypeI64},
wasm.ValueTypeF32: {0x60, 0, 1, wasm.ValueTypeF32},
wasm.ValueTypeF64: {0x60, 0, 1, wasm.ValueTypeF64},
}
// encodeFunctionType returns the wasm.FunctionType encoded in WebAssembly 1.0 (20191205) Binary Format.
//
// Note: Function types are encoded by the byte 0x60 followed by the respective vectors of parameter and result types.
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-types%E2%91%A4
func encodeFunctionType(t *wasm.FunctionType) []byte {
paramCount, resultCount := len(t.Params), len(t.Results)
if paramCount == 0 && resultCount == 0 {
return nullary
}
if resultCount == 0 {
if paramCount == 1 {
if encoded, ok := encodedOneParam[t.Params[0]]; ok {
return encoded
}
}
return append(append([]byte{0x60}, encodeValTypes(t.Params)...), 0)
} else if resultCount == 1 {
if paramCount == 0 {
if encoded, ok := encodedOneResult[t.Results[0]]; ok {
return encoded
}
}
return append(append([]byte{0x60}, encodeValTypes(t.Params)...), 1, t.Results[0])
}
// Only reached when "multi-value" is enabled because WebAssembly 1.0 (20191205) supports at most 1 result.
data := append([]byte{0x60}, encodeValTypes(t.Params)...)
return append(data, encodeValTypes(t.Results)...)
}
func decodeFunctionType(enabledFeatures api.CoreFeatures, 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)
}
paramCount, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("could not read parameter count: %w", err)
}
paramTypes, err := decodeValueTypes(r, paramCount)
if err != nil {
return nil, fmt.Errorf("could not read parameter types: %w", err)
}
resultCount, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("could not read result count: %w", err)
}
// Guard >1.0 feature multi-value
if resultCount > 1 {
if err = enabledFeatures.RequireEnabled(api.CoreFeatureMultiValue); err != nil {
return nil, fmt.Errorf("multiple result types invalid as %v", err)
}
}
resultTypes, err := decodeValueTypes(r, resultCount)
if err != nil {
return nil, fmt.Errorf("could not read result types: %w", err)
}
ret := &wasm.FunctionType{
Params: paramTypes,
Results: resultTypes,
}
return ret, nil
}