wa-lang.org/wazero@v1.0.2/internal/wasm/binary/element.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 "wa-lang.org/wazero/api" 9 "wa-lang.org/wazero/internal/leb128" 10 "wa-lang.org/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 vec[i] = &u32 37 } 38 return vec, nil 39 } 40 41 func decodeElementConstExprVector(r *bytes.Reader, elemType wasm.RefType, enabledFeatures api.CoreFeatures) ([]*wasm.Index, error) { 42 vs, _, err := leb128.DecodeUint32(r) 43 if err != nil { 44 return nil, fmt.Errorf("failed to get the size of constexpr vector: %w", err) 45 } 46 vec := make([]*wasm.Index, vs) 47 for i := range vec { 48 expr, err := decodeConstantExpression(r, enabledFeatures) 49 if err != nil { 50 return nil, err 51 } 52 switch expr.Opcode { 53 case wasm.OpcodeRefFunc: 54 if elemType != wasm.RefTypeFuncref { 55 return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has funcref", wasm.RefTypeName(elemType)) 56 } 57 v, _, _ := leb128.LoadUint32(expr.Data) 58 vec[i] = &v 59 case wasm.OpcodeRefNull: 60 if elemType != expr.Data[0] { 61 return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has %s", 62 wasm.RefTypeName(elemType), wasm.RefTypeName(expr.Data[0])) 63 } 64 // vec[i] is already nil, so nothing to do. 65 default: 66 return nil, fmt.Errorf("const expr must be either ref.null or ref.func but was %s", wasm.InstructionName(expr.Opcode)) 67 } 68 } 69 return vec, nil 70 } 71 72 func decodeElementRefType(r *bytes.Reader) (ret wasm.RefType, err error) { 73 ret, err = r.ReadByte() 74 if err != nil { 75 err = fmt.Errorf("read element ref type: %w", err) 76 return 77 } 78 if ret != wasm.RefTypeFuncref && ret != wasm.RefTypeExternref { 79 return 0, errors.New("ref type must be funcref or externref for element as of WebAssembly 2.0") 80 } 81 return 82 } 83 84 const ( 85 // The prefix is explained at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section 86 87 // elementSegmentPrefixLegacy is the legacy prefix and is only valid one before CoreFeatureBulkMemoryOperations. 88 elementSegmentPrefixLegacy = iota 89 // elementSegmentPrefixPassiveFuncrefValueVector is the passive element whose indexes are encoded as vec(varint), and reftype is fixed to funcref. 90 elementSegmentPrefixPassiveFuncrefValueVector 91 // elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex is the same as elementSegmentPrefixPassiveFuncrefValueVector but active and table index is encoded. 92 elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex 93 // elementSegmentPrefixDeclarativeFuncrefValueVector is the same as elementSegmentPrefixPassiveFuncrefValueVector but declarative. 94 elementSegmentPrefixDeclarativeFuncrefValueVector 95 // elementSegmentPrefixActiveFuncrefConstExprVector is active whoce reftype is fixed to funcref and indexes are encoded as vec(const_expr). 96 elementSegmentPrefixActiveFuncrefConstExprVector 97 // elementSegmentPrefixPassiveConstExprVector is passive whoce indexes are encoded as vec(const_expr), and reftype is encoded. 98 elementSegmentPrefixPassiveConstExprVector 99 // elementSegmentPrefixPassiveConstExprVector is active whoce indexes are encoded as vec(const_expr), and reftype and table index are encoded. 100 elementSegmentPrefixActiveConstExprVector 101 // elementSegmentPrefixDeclarativeConstExprVector is declarative whoce indexes are encoded as vec(const_expr), and reftype is encoded. 102 elementSegmentPrefixDeclarativeConstExprVector 103 ) 104 105 func decodeElementSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures) (*wasm.ElementSegment, error) { 106 prefix, _, err := leb128.DecodeUint32(r) 107 if err != nil { 108 return nil, fmt.Errorf("read element prefix: %w", err) 109 } 110 111 if prefix != elementSegmentPrefixLegacy { 112 if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil { 113 return nil, fmt.Errorf("non-zero prefix for element segment is invalid as %w", err) 114 } 115 } 116 117 // Encoding depends on the prefix and described at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section 118 switch prefix { 119 case elementSegmentPrefixLegacy: 120 // Legacy prefix which is WebAssembly 1.0 compatible. 121 expr, err := decodeConstantExpression(r, enabledFeatures) 122 if err != nil { 123 return nil, fmt.Errorf("read expr for offset: %w", err) 124 } 125 126 init, err := decodeElementInitValueVector(r) 127 if err != nil { 128 return nil, err 129 } 130 131 return &wasm.ElementSegment{ 132 OffsetExpr: expr, 133 Init: init, 134 Type: wasm.RefTypeFuncref, 135 Mode: wasm.ElementModeActive, 136 // Legacy prefix has the fixed table index zero. 137 TableIndex: 0, 138 }, nil 139 case elementSegmentPrefixPassiveFuncrefValueVector: 140 // Prefix 1 requires funcref. 141 if err = ensureElementKindFuncRef(r); err != nil { 142 return nil, err 143 } 144 145 init, err := decodeElementInitValueVector(r) 146 if err != nil { 147 return nil, err 148 } 149 return &wasm.ElementSegment{ 150 Init: init, 151 Type: wasm.RefTypeFuncref, 152 Mode: wasm.ElementModePassive, 153 }, nil 154 case elementSegmentPrefixActiveFuncrefValueVectorWithTableIndex: 155 tableIndex, _, err := leb128.DecodeUint32(r) 156 if err != nil { 157 return nil, fmt.Errorf("get size of vector: %w", err) 158 } 159 160 if tableIndex != 0 { 161 if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil { 162 return nil, fmt.Errorf("table index must be zero but was %d: %w", tableIndex, err) 163 } 164 } 165 166 expr, err := decodeConstantExpression(r, enabledFeatures) 167 if err != nil { 168 return nil, fmt.Errorf("read expr for offset: %w", err) 169 } 170 171 // Prefix 2 requires funcref. 172 if err = ensureElementKindFuncRef(r); err != nil { 173 return nil, err 174 } 175 176 init, err := decodeElementInitValueVector(r) 177 if err != nil { 178 return nil, err 179 } 180 return &wasm.ElementSegment{ 181 OffsetExpr: expr, 182 Init: init, 183 Type: wasm.RefTypeFuncref, 184 Mode: wasm.ElementModeActive, 185 TableIndex: tableIndex, 186 }, nil 187 case elementSegmentPrefixDeclarativeFuncrefValueVector: 188 // Prefix 3 requires funcref. 189 if err = ensureElementKindFuncRef(r); err != nil { 190 return nil, err 191 } 192 init, err := decodeElementInitValueVector(r) 193 if err != nil { 194 return nil, err 195 } 196 return &wasm.ElementSegment{ 197 Init: init, 198 Type: wasm.RefTypeFuncref, 199 Mode: wasm.ElementModeDeclarative, 200 }, nil 201 case elementSegmentPrefixActiveFuncrefConstExprVector: 202 expr, err := decodeConstantExpression(r, enabledFeatures) 203 if err != nil { 204 return nil, fmt.Errorf("read expr for offset: %w", err) 205 } 206 207 init, err := decodeElementConstExprVector(r, wasm.RefTypeFuncref, enabledFeatures) 208 if err != nil { 209 return nil, err 210 } 211 212 return &wasm.ElementSegment{ 213 OffsetExpr: expr, 214 Init: init, 215 Type: wasm.RefTypeFuncref, 216 Mode: wasm.ElementModeActive, 217 TableIndex: 0, 218 }, nil 219 case elementSegmentPrefixPassiveConstExprVector: 220 refType, err := decodeElementRefType(r) 221 if err != nil { 222 return nil, err 223 } 224 init, err := decodeElementConstExprVector(r, refType, enabledFeatures) 225 if err != nil { 226 return nil, err 227 } 228 return &wasm.ElementSegment{ 229 Init: init, 230 Type: refType, 231 Mode: wasm.ElementModePassive, 232 }, nil 233 case elementSegmentPrefixActiveConstExprVector: 234 tableIndex, _, err := leb128.DecodeUint32(r) 235 if err != nil { 236 return nil, fmt.Errorf("get size of vector: %w", err) 237 } 238 239 if tableIndex != 0 { 240 if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil { 241 return nil, fmt.Errorf("table index must be zero but was %d: %w", tableIndex, err) 242 } 243 } 244 expr, err := decodeConstantExpression(r, enabledFeatures) 245 if err != nil { 246 return nil, fmt.Errorf("read expr for offset: %w", err) 247 } 248 249 refType, err := decodeElementRefType(r) 250 if err != nil { 251 return nil, err 252 } 253 254 init, err := decodeElementConstExprVector(r, refType, enabledFeatures) 255 if err != nil { 256 return nil, err 257 } 258 259 return &wasm.ElementSegment{ 260 OffsetExpr: expr, 261 Init: init, 262 Type: refType, 263 Mode: wasm.ElementModeActive, 264 TableIndex: tableIndex, 265 }, nil 266 case elementSegmentPrefixDeclarativeConstExprVector: 267 refType, err := decodeElementRefType(r) 268 if err != nil { 269 return nil, err 270 } 271 init, err := decodeElementConstExprVector(r, refType, enabledFeatures) 272 if err != nil { 273 return nil, err 274 } 275 return &wasm.ElementSegment{ 276 Init: init, 277 Type: refType, 278 Mode: wasm.ElementModeDeclarative, 279 }, nil 280 default: 281 return nil, fmt.Errorf("invalid element segment prefix: 0x%x", prefix) 282 } 283 } 284 285 // encodeCode returns the wasm.ElementSegment encoded in WebAssembly 1.0 (20191205) Binary Format. 286 // 287 // https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#element-section%E2%91%A0 288 func encodeElement(e *wasm.ElementSegment) (ret []byte) { 289 if e.Mode == wasm.ElementModeActive { 290 ret = append(ret, leb128.EncodeInt32(int32(e.TableIndex))...) 291 ret = append(ret, encodeConstantExpression(e.OffsetExpr)...) 292 ret = append(ret, leb128.EncodeUint32(uint32(len(e.Init)))...) 293 for _, idx := range e.Init { 294 ret = append(ret, leb128.EncodeInt32(int32(*idx))...) 295 } 296 } else { 297 panic("TODO: support encoding for non-active elements in bulk-memory-operations proposal") 298 } 299 return 300 }