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}