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

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"math"
     8  
     9  	"github.com/bananabytelabs/wazero/internal/leb128"
    10  	"github.com/bananabytelabs/wazero/internal/wasm"
    11  )
    12  
    13  func decodeCode(r *bytes.Reader, codeSectionStart uint64, ret *wasm.Code) (err error) {
    14  	ss, _, err := leb128.DecodeUint32(r)
    15  	if err != nil {
    16  		return fmt.Errorf("get the size of code: %w", err)
    17  	}
    18  	remaining := int64(ss)
    19  
    20  	// Parse #locals.
    21  	ls, bytesRead, err := leb128.DecodeUint32(r)
    22  	remaining -= int64(bytesRead)
    23  	if err != nil {
    24  		return fmt.Errorf("get the size locals: %v", err)
    25  	} else if remaining < 0 {
    26  		return io.EOF
    27  	}
    28  
    29  	// Validate the locals.
    30  	bytesRead = 0
    31  	var sum uint64
    32  	for i := uint32(0); i < ls; i++ {
    33  		num, n, err := leb128.DecodeUint32(r)
    34  		if err != nil {
    35  			return fmt.Errorf("read n of locals: %v", err)
    36  		} else if remaining < 0 {
    37  			return io.EOF
    38  		}
    39  
    40  		sum += uint64(num)
    41  
    42  		b, err := r.ReadByte()
    43  		if err != nil {
    44  			return fmt.Errorf("read type of local: %v", err)
    45  		}
    46  
    47  		bytesRead += n + 1
    48  		switch vt := b; vt {
    49  		case wasm.ValueTypeI32, wasm.ValueTypeF32, wasm.ValueTypeI64, wasm.ValueTypeF64,
    50  			wasm.ValueTypeFuncref, wasm.ValueTypeExternref, wasm.ValueTypeV128:
    51  		default:
    52  			return fmt.Errorf("invalid local type: 0x%x", vt)
    53  		}
    54  	}
    55  
    56  	if sum > math.MaxUint32 {
    57  		return fmt.Errorf("too many locals: %d", sum)
    58  	}
    59  
    60  	// Rewind the buffer.
    61  	_, err = r.Seek(-int64(bytesRead), io.SeekCurrent)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	localTypes := make([]wasm.ValueType, 0, sum)
    67  	for i := uint32(0); i < ls; i++ {
    68  		num, bytesRead, err := leb128.DecodeUint32(r)
    69  		remaining -= int64(bytesRead) + 1 // +1 for the subsequent ReadByte
    70  		if err != nil {
    71  			return fmt.Errorf("read n of locals: %v", err)
    72  		} else if remaining < 0 {
    73  			return io.EOF
    74  		}
    75  
    76  		b, err := r.ReadByte()
    77  		if err != nil {
    78  			return fmt.Errorf("read type of local: %v", err)
    79  		}
    80  
    81  		for j := uint32(0); j < num; j++ {
    82  			localTypes = append(localTypes, b)
    83  		}
    84  	}
    85  
    86  	bodyOffsetInCodeSection := codeSectionStart - uint64(r.Len())
    87  	body := make([]byte, remaining)
    88  	if _, err = io.ReadFull(r, body); err != nil {
    89  		return fmt.Errorf("read body: %w", err)
    90  	}
    91  
    92  	if endIndex := len(body) - 1; endIndex < 0 || body[endIndex] != wasm.OpcodeEnd {
    93  		return fmt.Errorf("expr not end with OpcodeEnd")
    94  	}
    95  
    96  	ret.BodyOffsetInCodeSection = bodyOffsetInCodeSection
    97  	ret.LocalTypes = localTypes
    98  	ret.Body = body
    99  	return nil
   100  }