element.go

  1package binary
  2
  3import (
  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
 13func 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
 24func 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
 45func 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
 85func 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
 97const (
 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
118func 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}