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

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/bananabytelabs/wazero/api"
     9  	"github.com/bananabytelabs/wazero/internal/leb128"
    10  	"github.com/bananabytelabs/wazero/internal/wasm"
    11  )
    12  
    13  func ensureElementKindFuncRef(r *bytes.Reader) error {
    14  	elemKind, err := r.ReadByte()
    15  	if err != nil {
    16  		return fmt.Errorf("read element prefix: %w", err)
    17  	}
    18  	if elemKind != 0x0 { // ElemKind is fixed to 0x0 now: https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
    19  		return fmt.Errorf("element kind must be zero but was 0x%x", elemKind)
    20  	}
    21  	return nil
    22  }
    23  
    24  func decodeElementInitValueVector(r *bytes.Reader) ([]wasm.Index, error) {
    25  	vs, _, err := leb128.DecodeUint32(r)
    26  	if err != nil {
    27  		return nil, fmt.Errorf("get size of vector: %w", err)
    28  	}
    29  
    30  	vec := make([]wasm.Index, vs)
    31  	for i := range vec {
    32  		u32, _, err := leb128.DecodeUint32(r)
    33  		if err != nil {
    34  			return nil, fmt.Errorf("read function index: %w", err)
    35  		}
    36  
    37  		if u32 >= wasm.MaximumFunctionIndex {
    38  			return nil, fmt.Errorf("too large function index in Element init: %d", u32)
    39  		}
    40  		vec[i] = u32
    41  	}
    42  	return vec, nil
    43  }
    44  
    45  func decodeElementConstExprVector(r *bytes.Reader, elemType wasm.RefType, enabledFeatures api.CoreFeatures) ([]wasm.Index, error) {
    46  	vs, _, err := leb128.DecodeUint32(r)
    47  	if err != nil {
    48  		return nil, fmt.Errorf("failed to get the size of constexpr vector: %w", err)
    49  	}
    50  	vec := make([]wasm.Index, vs)
    51  	for i := range vec {
    52  		var expr wasm.ConstantExpression
    53  		err := decodeConstantExpression(r, enabledFeatures, &expr)
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  		switch expr.Opcode {
    58  		case wasm.OpcodeRefFunc:
    59  			if elemType != wasm.RefTypeFuncref {
    60  				return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has funcref", wasm.RefTypeName(elemType))
    61  			}
    62  			v, _, _ := leb128.LoadUint32(expr.Data)
    63  			if v >= wasm.MaximumFunctionIndex {
    64  				return nil, fmt.Errorf("too large function index in Element init: %d", v)
    65  			}
    66  			vec[i] = v
    67  		case wasm.OpcodeRefNull:
    68  			if elemType != expr.Data[0] {
    69  				return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has %s",
    70  					wasm.RefTypeName(elemType), wasm.RefTypeName(expr.Data[0]))
    71  			}
    72  			vec[i] = wasm.ElementInitNullReference
    73  		case wasm.OpcodeGlobalGet:
    74  			i32, _, _ := leb128.LoadInt32(expr.Data)
    75  			if elemType != wasm.RefTypeFuncref {
    76  				return nil, fmt.Errorf("element type mismatch: want %s, but requires funcref", wasm.RefTypeName(elemType))
    77  			}
    78  			// Resolving the function index is done at instantiation phase. See the comment on
    79  			// wasm.ElementInitImportedGlobalFunctionReference.
    80  			vec[i] = wasm.ElementInitImportedGlobalFunctionReference | wasm.Index(i32)
    81  		default:
    82  			return nil, fmt.Errorf("const expr must be either ref.null or ref.func but was %s", wasm.InstructionName(expr.Opcode))
    83  		}
    84  	}
    85  	return vec, nil
    86  }
    87  
    88  func decodeElementRefType(r *bytes.Reader) (ret wasm.RefType, err error) {
    89  	ret, err = r.ReadByte()
    90  	if err != nil {
    91  		err = fmt.Errorf("read element ref type: %w", err)
    92  		return
    93  	}
    94  	if ret != wasm.RefTypeFuncref && ret != wasm.RefTypeExternref {
    95  		return 0, errors.New("ref type must be funcref or externref for element as of WebAssembly 2.0")
    96  	}
    97  	return
    98  }
    99  
   100  const (
   101  	// The prefix is explained at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
   102  
   103  	// elementSegmentPrefixLegacy is the legacy prefix and is only valid one before CoreFeatureBulkMemoryOperations.
   104  	elementSegmentPrefixLegacy = iota
   105  	// elementSegmentPrefixPassiveFuncrefValueVector is the passive element whose indexes are encoded as vec(varint), and reftype is fixed to funcref.
   106  	elementSegmentPrefixPassiveFuncrefValueVector
   107  	// elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex is the same as elementSegmentPrefixPassiveFuncrefValueVector but active and table index is encoded.
   108  	elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex
   109  	// elementSegmentPrefixDeclarativeFuncrefValueVector is the same as elementSegmentPrefixPassiveFuncrefValueVector but declarative.
   110  	elementSegmentPrefixDeclarativeFuncrefValueVector
   111  	// elementSegmentPrefixActiveFuncrefConstExprVector is active whoce reftype is fixed to funcref and indexes are encoded as vec(const_expr).
   112  	elementSegmentPrefixActiveFuncrefConstExprVector
   113  	// elementSegmentPrefixPassiveConstExprVector is passive whoce indexes are encoded as vec(const_expr), and reftype is encoded.
   114  	elementSegmentPrefixPassiveConstExprVector
   115  	// elementSegmentPrefixPassiveConstExprVector is active whoce indexes are encoded as vec(const_expr), and reftype and table index are encoded.
   116  	elementSegmentPrefixActiveConstExprVector
   117  	// elementSegmentPrefixDeclarativeConstExprVector is declarative whoce indexes are encoded as vec(const_expr), and reftype is encoded.
   118  	elementSegmentPrefixDeclarativeConstExprVector
   119  )
   120  
   121  func decodeElementSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.ElementSegment) error {
   122  	prefix, _, err := leb128.DecodeUint32(r)
   123  	if err != nil {
   124  		return fmt.Errorf("read element prefix: %w", err)
   125  	}
   126  
   127  	if prefix != elementSegmentPrefixLegacy {
   128  		if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
   129  			return fmt.Errorf("non-zero prefix for element segment is invalid as %w", err)
   130  		}
   131  	}
   132  
   133  	// Encoding depends on the prefix and described at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
   134  	switch prefix {
   135  	case elementSegmentPrefixLegacy:
   136  		// Legacy prefix which is WebAssembly 1.0 compatible.
   137  		err = decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
   138  		if err != nil {
   139  			return fmt.Errorf("read expr for offset: %w", err)
   140  		}
   141  
   142  		ret.Init, err = decodeElementInitValueVector(r)
   143  		if err != nil {
   144  			return err
   145  		}
   146  
   147  		ret.Mode = wasm.ElementModeActive
   148  		ret.Type = wasm.RefTypeFuncref
   149  		return nil
   150  	case elementSegmentPrefixPassiveFuncrefValueVector:
   151  		// Prefix 1 requires funcref.
   152  		if err = ensureElementKindFuncRef(r); err != nil {
   153  			return err
   154  		}
   155  
   156  		ret.Init, err = decodeElementInitValueVector(r)
   157  		if err != nil {
   158  			return err
   159  		}
   160  		ret.Mode = wasm.ElementModePassive
   161  		ret.Type = wasm.RefTypeFuncref
   162  		return nil
   163  	case elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex:
   164  		ret.TableIndex, _, err = leb128.DecodeUint32(r)
   165  		if err != nil {
   166  			return fmt.Errorf("get size of vector: %w", err)
   167  		}
   168  
   169  		if ret.TableIndex != 0 {
   170  			if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
   171  				return fmt.Errorf("table index must be zero but was %d: %w", ret.TableIndex, err)
   172  			}
   173  		}
   174  
   175  		err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
   176  		if err != nil {
   177  			return fmt.Errorf("read expr for offset: %w", err)
   178  		}
   179  
   180  		// Prefix 2 requires funcref.
   181  		if err = ensureElementKindFuncRef(r); err != nil {
   182  			return err
   183  		}
   184  
   185  		ret.Init, err = decodeElementInitValueVector(r)
   186  		if err != nil {
   187  			return err
   188  		}
   189  
   190  		ret.Mode = wasm.ElementModeActive
   191  		ret.Type = wasm.RefTypeFuncref
   192  		return nil
   193  	case elementSegmentPrefixDeclarativeFuncrefValueVector:
   194  		// Prefix 3 requires funcref.
   195  		if err = ensureElementKindFuncRef(r); err != nil {
   196  			return err
   197  		}
   198  		ret.Init, err = decodeElementInitValueVector(r)
   199  		if err != nil {
   200  			return err
   201  		}
   202  		ret.Type = wasm.RefTypeFuncref
   203  		ret.Mode = wasm.ElementModeDeclarative
   204  		return nil
   205  	case elementSegmentPrefixActiveFuncrefConstExprVector:
   206  		err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
   207  		if err != nil {
   208  			return fmt.Errorf("read expr for offset: %w", err)
   209  		}
   210  
   211  		ret.Init, err = decodeElementConstExprVector(r, wasm.RefTypeFuncref, enabledFeatures)
   212  		if err != nil {
   213  			return err
   214  		}
   215  		ret.Mode = wasm.ElementModeActive
   216  		ret.Type = wasm.RefTypeFuncref
   217  		return nil
   218  	case elementSegmentPrefixPassiveConstExprVector:
   219  		ret.Type, err = decodeElementRefType(r)
   220  		if err != nil {
   221  			return err
   222  		}
   223  		ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
   224  		if err != nil {
   225  			return err
   226  		}
   227  		ret.Mode = wasm.ElementModePassive
   228  		return nil
   229  	case elementSegmentPrefixActiveConstExprVector:
   230  		ret.TableIndex, _, err = leb128.DecodeUint32(r)
   231  		if err != nil {
   232  			return fmt.Errorf("get size of vector: %w", err)
   233  		}
   234  
   235  		if ret.TableIndex != 0 {
   236  			if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
   237  				return fmt.Errorf("table index must be zero but was %d: %w", ret.TableIndex, err)
   238  			}
   239  		}
   240  		err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
   241  		if err != nil {
   242  			return fmt.Errorf("read expr for offset: %w", err)
   243  		}
   244  
   245  		ret.Type, err = decodeElementRefType(r)
   246  		if err != nil {
   247  			return err
   248  		}
   249  
   250  		ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
   251  		if err != nil {
   252  			return err
   253  		}
   254  
   255  		ret.Mode = wasm.ElementModeActive
   256  		return nil
   257  	case elementSegmentPrefixDeclarativeConstExprVector:
   258  		ret.Type, err = decodeElementRefType(r)
   259  		if err != nil {
   260  			return err
   261  		}
   262  		ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
   263  		if err != nil {
   264  			return err
   265  		}
   266  
   267  		ret.Mode = wasm.ElementModeDeclarative
   268  		return nil
   269  	default:
   270  		return fmt.Errorf("invalid element segment prefix: 0x%x", prefix)
   271  	}
   272  }