names.go

  1package binary
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"io"
  7
  8	"github.com/tetratelabs/wazero/internal/leb128"
  9	"github.com/tetratelabs/wazero/internal/wasm"
 10)
 11
 12const (
 13	// subsectionIDModuleName contains only the module name.
 14	subsectionIDModuleName = uint8(0)
 15	// subsectionIDFunctionNames is a map of indices to function names, in ascending order by function index
 16	subsectionIDFunctionNames = uint8(1)
 17	// subsectionIDLocalNames contain a map of function indices to a map of local indices to their names, in ascending
 18	// order by function and local index
 19	subsectionIDLocalNames = uint8(2)
 20)
 21
 22// decodeNameSection deserializes the data associated with the "name" key in SectionIDCustom according to the
 23// standard:
 24//
 25// * ModuleName decode from subsection 0
 26// * FunctionNames decode from subsection 1
 27// * LocalNames decode from subsection 2
 28//
 29// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namesec
 30func decodeNameSection(r *bytes.Reader, limit uint64) (result *wasm.NameSection, err error) {
 31	// TODO: add leb128 functions that work on []byte and offset. While using a reader allows us to reuse reader-based
 32	// leb128 functions, it is less efficient, causes untestable code and in some cases more complex vs plain []byte.
 33	result = &wasm.NameSection{}
 34
 35	// subsectionID is decoded if known, and skipped if not
 36	var subsectionID uint8
 37	// subsectionSize is the length to skip when the subsectionID is unknown
 38	var subsectionSize uint32
 39	var bytesRead uint64
 40	for limit > 0 {
 41		if subsectionID, err = r.ReadByte(); err != nil {
 42			if err == io.EOF {
 43				return result, nil
 44			}
 45			// TODO: untestable as this can't fail for a reason beside EOF reading a byte from a buffer
 46			return nil, fmt.Errorf("failed to read a subsection ID: %w", err)
 47		}
 48		limit--
 49
 50		if subsectionSize, bytesRead, err = leb128.DecodeUint32(r); err != nil {
 51			return nil, fmt.Errorf("failed to read the size of subsection[%d]: %w", subsectionID, err)
 52		}
 53		limit -= bytesRead
 54
 55		switch subsectionID {
 56		case subsectionIDModuleName:
 57			if result.ModuleName, _, err = decodeUTF8(r, "module name"); err != nil {
 58				return nil, err
 59			}
 60		case subsectionIDFunctionNames:
 61			if result.FunctionNames, err = decodeFunctionNames(r); err != nil {
 62				return nil, err
 63			}
 64		case subsectionIDLocalNames:
 65			if result.LocalNames, err = decodeLocalNames(r); err != nil {
 66				return nil, err
 67			}
 68		default: // Skip other subsections.
 69			// Note: Not Seek because it doesn't err when given an offset past EOF. Rather, it leads to undefined state.
 70			if _, err = io.CopyN(io.Discard, r, int64(subsectionSize)); err != nil {
 71				return nil, fmt.Errorf("failed to skip subsection[%d]: %w", subsectionID, err)
 72			}
 73		}
 74		limit -= uint64(subsectionSize)
 75	}
 76	return
 77}
 78
 79func decodeFunctionNames(r *bytes.Reader) (wasm.NameMap, error) {
 80	functionCount, err := decodeFunctionCount(r, subsectionIDFunctionNames)
 81	if err != nil {
 82		return nil, err
 83	}
 84
 85	result := make(wasm.NameMap, functionCount)
 86	for i := uint32(0); i < functionCount; i++ {
 87		functionIndex, err := decodeFunctionIndex(r, subsectionIDFunctionNames)
 88		if err != nil {
 89			return nil, err
 90		}
 91
 92		name, _, err := decodeUTF8(r, "function[%d] name", functionIndex)
 93		if err != nil {
 94			return nil, err
 95		}
 96		result[i] = wasm.NameAssoc{Index: functionIndex, Name: name}
 97	}
 98	return result, nil
 99}
100
101func decodeLocalNames(r *bytes.Reader) (wasm.IndirectNameMap, error) {
102	functionCount, err := decodeFunctionCount(r, subsectionIDLocalNames)
103	if err != nil {
104		return nil, err
105	}
106
107	result := make(wasm.IndirectNameMap, functionCount)
108	for i := uint32(0); i < functionCount; i++ {
109		functionIndex, err := decodeFunctionIndex(r, subsectionIDLocalNames)
110		if err != nil {
111			return nil, err
112		}
113
114		localCount, _, err := leb128.DecodeUint32(r)
115		if err != nil {
116			return nil, fmt.Errorf("failed to read the local count for function[%d]: %w", functionIndex, err)
117		}
118
119		locals := make(wasm.NameMap, localCount)
120		for j := uint32(0); j < localCount; j++ {
121			localIndex, _, err := leb128.DecodeUint32(r)
122			if err != nil {
123				return nil, fmt.Errorf("failed to read a local index of function[%d]: %w", functionIndex, err)
124			}
125
126			name, _, err := decodeUTF8(r, "function[%d] local[%d] name", functionIndex, localIndex)
127			if err != nil {
128				return nil, err
129			}
130			locals[j] = wasm.NameAssoc{Index: localIndex, Name: name}
131		}
132		result[i] = wasm.NameMapAssoc{Index: functionIndex, NameMap: locals}
133	}
134	return result, nil
135}
136
137func decodeFunctionIndex(r *bytes.Reader, subsectionID uint8) (uint32, error) {
138	functionIndex, _, err := leb128.DecodeUint32(r)
139	if err != nil {
140		return 0, fmt.Errorf("failed to read a function index in subsection[%d]: %w", subsectionID, err)
141	}
142	return functionIndex, nil
143}
144
145func decodeFunctionCount(r *bytes.Reader, subsectionID uint8) (uint32, error) {
146	functionCount, _, err := leb128.DecodeUint32(r)
147	if err != nil {
148		return 0, fmt.Errorf("failed to read the function count of subsection[%d]: %w", subsectionID, err)
149	}
150	return functionCount, nil
151}