wa-lang.org/wazero@v1.0.2/internal/wasm/binary/function.go (about)

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"wa-lang.org/wazero/api"
     8  	"wa-lang.org/wazero/internal/leb128"
     9  	"wa-lang.org/wazero/internal/wasm"
    10  )
    11  
    12  var nullary = []byte{0x60, 0, 0}
    13  
    14  // encodedOneParam is a cache of wasm.FunctionType values for param length 1 and result length 0
    15  var encodedOneParam = map[wasm.ValueType][]byte{
    16  	wasm.ValueTypeI32: {0x60, 1, wasm.ValueTypeI32, 0},
    17  	wasm.ValueTypeI64: {0x60, 1, wasm.ValueTypeI64, 0},
    18  	wasm.ValueTypeF32: {0x60, 1, wasm.ValueTypeF32, 0},
    19  	wasm.ValueTypeF64: {0x60, 1, wasm.ValueTypeF64, 0},
    20  }
    21  
    22  // encodedOneResult is a cache of wasm.FunctionType values for param length 0 and result length 1
    23  var encodedOneResult = map[wasm.ValueType][]byte{
    24  	wasm.ValueTypeI32: {0x60, 0, 1, wasm.ValueTypeI32},
    25  	wasm.ValueTypeI64: {0x60, 0, 1, wasm.ValueTypeI64},
    26  	wasm.ValueTypeF32: {0x60, 0, 1, wasm.ValueTypeF32},
    27  	wasm.ValueTypeF64: {0x60, 0, 1, wasm.ValueTypeF64},
    28  }
    29  
    30  // encodeFunctionType returns the wasm.FunctionType encoded in WebAssembly 1.0 (20191205) Binary Format.
    31  //
    32  // Note: Function types are encoded by the byte 0x60 followed by the respective vectors of parameter and result types.
    33  // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-types%E2%91%A4
    34  func encodeFunctionType(t *wasm.FunctionType) []byte {
    35  	paramCount, resultCount := len(t.Params), len(t.Results)
    36  	if paramCount == 0 && resultCount == 0 {
    37  		return nullary
    38  	}
    39  	if resultCount == 0 {
    40  		if paramCount == 1 {
    41  			if encoded, ok := encodedOneParam[t.Params[0]]; ok {
    42  				return encoded
    43  			}
    44  		}
    45  		return append(append([]byte{0x60}, encodeValTypes(t.Params)...), 0)
    46  	} else if resultCount == 1 {
    47  		if paramCount == 0 {
    48  			if encoded, ok := encodedOneResult[t.Results[0]]; ok {
    49  				return encoded
    50  			}
    51  		}
    52  		return append(append([]byte{0x60}, encodeValTypes(t.Params)...), 1, t.Results[0])
    53  	}
    54  	// Only reached when "multi-value" is enabled because WebAssembly 1.0 (20191205) supports at most 1 result.
    55  	data := append([]byte{0x60}, encodeValTypes(t.Params)...)
    56  	return append(data, encodeValTypes(t.Results)...)
    57  }
    58  
    59  func decodeFunctionType(enabledFeatures api.CoreFeatures, r *bytes.Reader) (*wasm.FunctionType, error) {
    60  	b, err := r.ReadByte()
    61  	if err != nil {
    62  		return nil, fmt.Errorf("read leading byte: %w", err)
    63  	}
    64  
    65  	if b != 0x60 {
    66  		return nil, fmt.Errorf("%w: %#x != 0x60", ErrInvalidByte, b)
    67  	}
    68  
    69  	paramCount, _, err := leb128.DecodeUint32(r)
    70  	if err != nil {
    71  		return nil, fmt.Errorf("could not read parameter count: %w", err)
    72  	}
    73  
    74  	paramTypes, err := decodeValueTypes(r, paramCount)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("could not read parameter types: %w", err)
    77  	}
    78  
    79  	resultCount, _, err := leb128.DecodeUint32(r)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("could not read result count: %w", err)
    82  	}
    83  
    84  	// Guard >1.0 feature multi-value
    85  	if resultCount > 1 {
    86  		if err = enabledFeatures.RequireEnabled(api.CoreFeatureMultiValue); err != nil {
    87  			return nil, fmt.Errorf("multiple result types invalid as %v", err)
    88  		}
    89  	}
    90  
    91  	resultTypes, err := decodeValueTypes(r, resultCount)
    92  	if err != nil {
    93  		return nil, fmt.Errorf("could not read result types: %w", err)
    94  	}
    95  
    96  	ret := &wasm.FunctionType{
    97  		Params:  paramTypes,
    98  		Results: resultTypes,
    99  	}
   100  	return ret, nil
   101  }