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 }