github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/wasm/binary/names.go (about)

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  
     8  	"github.com/bananabytelabs/wazero/internal/leb128"
     9  	"github.com/bananabytelabs/wazero/internal/wasm"
    10  )
    11  
    12  const (
    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
    30  func 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  
    79  func 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  
   101  func 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  
   137  func 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  
   145  func 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  }