github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/wasm/binary/element.go (about)

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/tetratelabs/wazero/api"
     9  	"github.com/tetratelabs/wazero/internal/leb128"
    10  	"github.com/tetratelabs/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  			// Resolving the reference type from globals is done at instantiation phase. See the comment on
    76  			// wasm.elementInitImportedGlobalReferenceType.
    77  			vec[i] = wasm.WrapGlobalIndexAsElementInit(wasm.Index(i32))
    78  		default:
    79  			return nil, fmt.Errorf("const expr must be either ref.null or ref.func but was %s", wasm.InstructionName(expr.Opcode))
    80  		}
    81  	}
    82  	return vec, nil
    83  }
    84  
    85  func decodeElementRefType(r *bytes.Reader) (ret wasm.RefType, err error) {
    86  	ret, err = r.ReadByte()
    87  	if err != nil {
    88  		err = fmt.Errorf("read element ref type: %w", err)
    89  		return
    90  	}
    91  	if ret != wasm.RefTypeFuncref && ret != wasm.RefTypeExternref {
    92  		return 0, errors.New("ref type must be funcref or externref for element as of WebAssembly 2.0")
    93  	}
    94  	return
    95  }
    96  
    97  const (
    98  	// The prefix is explained at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
    99  
   100  	// elementSegmentPrefixLegacy is the legacy prefix and is only valid one before CoreFeatureBulkMemoryOperations.
   101  	elementSegmentPrefixLegacy = iota
   102  	// elementSegmentPrefixPassiveFuncrefValueVector is the passive element whose indexes are encoded as vec(varint), and reftype is fixed to funcref.
   103  	elementSegmentPrefixPassiveFuncrefValueVector
   104  	// elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex is the same as elementSegmentPrefixPassiveFuncrefValueVector but active and table index is encoded.
   105  	elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex
   106  	// elementSegmentPrefixDeclarativeFuncrefValueVector is the same as elementSegmentPrefixPassiveFuncrefValueVector but declarative.
   107  	elementSegmentPrefixDeclarativeFuncrefValueVector
   108  	// elementSegmentPrefixActiveFuncrefConstExprVector is active whoce reftype is fixed to funcref and indexes are encoded as vec(const_expr).
   109  	elementSegmentPrefixActiveFuncrefConstExprVector
   110  	// elementSegmentPrefixPassiveConstExprVector is passive whoce indexes are encoded as vec(const_expr), and reftype is encoded.
   111  	elementSegmentPrefixPassiveConstExprVector
   112  	// elementSegmentPrefixPassiveConstExprVector is active whoce indexes are encoded as vec(const_expr), and reftype and table index are encoded.
   113  	elementSegmentPrefixActiveConstExprVector
   114  	// elementSegmentPrefixDeclarativeConstExprVector is declarative whoce indexes are encoded as vec(const_expr), and reftype is encoded.
   115  	elementSegmentPrefixDeclarativeConstExprVector
   116  )
   117  
   118  func decodeElementSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.ElementSegment) error {
   119  	prefix, _, err := leb128.DecodeUint32(r)
   120  	if err != nil {
   121  		return fmt.Errorf("read element prefix: %w", err)
   122  	}
   123  
   124  	if prefix != elementSegmentPrefixLegacy {
   125  		if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
   126  			return fmt.Errorf("non-zero prefix for element segment is invalid as %w", err)
   127  		}
   128  	}
   129  
   130  	// Encoding depends on the prefix and described at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
   131  	switch prefix {
   132  	case elementSegmentPrefixLegacy:
   133  		// Legacy prefix which is WebAssembly 1.0 compatible.
   134  		err = decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
   135  		if err != nil {
   136  			return fmt.Errorf("read expr for offset: %w", err)
   137  		}
   138  
   139  		ret.Init, err = decodeElementInitValueVector(r)
   140  		if err != nil {
   141  			return err
   142  		}
   143  
   144  		ret.Mode = wasm.ElementModeActive
   145  		ret.Type = wasm.RefTypeFuncref
   146  		return nil
   147  	case elementSegmentPrefixPassiveFuncrefValueVector:
   148  		// Prefix 1 requires funcref.
   149  		if err = ensureElementKindFuncRef(r); err != nil {
   150  			return err
   151  		}
   152  
   153  		ret.Init, err = decodeElementInitValueVector(r)
   154  		if err != nil {
   155  			return err
   156  		}
   157  		ret.Mode = wasm.ElementModePassive
   158  		ret.Type = wasm.RefTypeFuncref
   159  		return nil
   160  	case elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex:
   161  		ret.TableIndex, _, err = leb128.DecodeUint32(r)
   162  		if err != nil {
   163  			return fmt.Errorf("get size of vector: %w", err)
   164  		}
   165  
   166  		if ret.TableIndex != 0 {
   167  			if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
   168  				return fmt.Errorf("table index must be zero but was %d: %w", ret.TableIndex, err)
   169  			}
   170  		}
   171  
   172  		err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
   173  		if err != nil {
   174  			return fmt.Errorf("read expr for offset: %w", err)
   175  		}
   176  
   177  		// Prefix 2 requires funcref.
   178  		if err = ensureElementKindFuncRef(r); err != nil {
   179  			return err
   180  		}
   181  
   182  		ret.Init, err = decodeElementInitValueVector(r)
   183  		if err != nil {
   184  			return err
   185  		}
   186  
   187  		ret.Mode = wasm.ElementModeActive
   188  		ret.Type = wasm.RefTypeFuncref
   189  		return nil
   190  	case elementSegmentPrefixDeclarativeFuncrefValueVector:
   191  		// Prefix 3 requires funcref.
   192  		if err = ensureElementKindFuncRef(r); err != nil {
   193  			return err
   194  		}
   195  		ret.Init, err = decodeElementInitValueVector(r)
   196  		if err != nil {
   197  			return err
   198  		}
   199  		ret.Type = wasm.RefTypeFuncref
   200  		ret.Mode = wasm.ElementModeDeclarative
   201  		return nil
   202  	case elementSegmentPrefixActiveFuncrefConstExprVector:
   203  		err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
   204  		if err != nil {
   205  			return fmt.Errorf("read expr for offset: %w", err)
   206  		}
   207  
   208  		ret.Init, err = decodeElementConstExprVector(r, wasm.RefTypeFuncref, enabledFeatures)
   209  		if err != nil {
   210  			return err
   211  		}
   212  		ret.Mode = wasm.ElementModeActive
   213  		ret.Type = wasm.RefTypeFuncref
   214  		return nil
   215  	case elementSegmentPrefixPassiveConstExprVector:
   216  		ret.Type, err = decodeElementRefType(r)
   217  		if err != nil {
   218  			return err
   219  		}
   220  		ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
   221  		if err != nil {
   222  			return err
   223  		}
   224  		ret.Mode = wasm.ElementModePassive
   225  		return nil
   226  	case elementSegmentPrefixActiveConstExprVector:
   227  		ret.TableIndex, _, err = leb128.DecodeUint32(r)
   228  		if err != nil {
   229  			return fmt.Errorf("get size of vector: %w", err)
   230  		}
   231  
   232  		if ret.TableIndex != 0 {
   233  			if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
   234  				return fmt.Errorf("table index must be zero but was %d: %w", ret.TableIndex, err)
   235  			}
   236  		}
   237  		err := decodeConstantExpression(r, enabledFeatures, &ret.OffsetExpr)
   238  		if err != nil {
   239  			return fmt.Errorf("read expr for offset: %w", err)
   240  		}
   241  
   242  		ret.Type, err = decodeElementRefType(r)
   243  		if err != nil {
   244  			return err
   245  		}
   246  
   247  		ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
   248  		if err != nil {
   249  			return err
   250  		}
   251  
   252  		ret.Mode = wasm.ElementModeActive
   253  		return nil
   254  	case elementSegmentPrefixDeclarativeConstExprVector:
   255  		ret.Type, err = decodeElementRefType(r)
   256  		if err != nil {
   257  			return err
   258  		}
   259  		ret.Init, err = decodeElementConstExprVector(r, ret.Type, enabledFeatures)
   260  		if err != nil {
   261  			return err
   262  		}
   263  
   264  		ret.Mode = wasm.ElementModeDeclarative
   265  		return nil
   266  	default:
   267  		return fmt.Errorf("invalid element segment prefix: 0x%x", prefix)
   268  	}
   269  }