wa-lang.org/wazero@v1.0.2/internal/wasm/binary/names.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 "wa-lang.org/wazero/internal/leb128" 9 "wa-lang.org/wazero/internal/wasm" 10 ) 11 12 const ( 13 // subsectionIDModuleName contains only the module name. 14 subsectionIDModuleName = uint8(0) 15 // subsectionIDFunctionNames is a map of indices to function names, in ascending order by function index 16 subsectionIDFunctionNames = uint8(1) 17 // subsectionIDLocalNames contain a map of function indices to a map of local indices to their names, in ascending 18 // order by function and local index 19 subsectionIDLocalNames = uint8(2) 20 ) 21 22 // decodeNameSection deserializes the data associated with the "name" key in SectionIDCustom according to the 23 // standard: 24 // 25 // * ModuleName decode from subsection 0 26 // * FunctionNames decode from subsection 1 27 // * LocalNames decode from subsection 2 28 // 29 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namesec 30 func decodeNameSection(r *bytes.Reader, limit uint64) (result *wasm.NameSection, err error) { 31 // TODO: add leb128 functions that work on []byte and offset. While using a reader allows us to reuse reader-based 32 // leb128 functions, it is less efficient, causes untestable code and in some cases more complex vs plain []byte. 33 result = &wasm.NameSection{} 34 35 // subsectionID is decoded if known, and skipped if not 36 var subsectionID uint8 37 // subsectionSize is the length to skip when the subsectionID is unknown 38 var subsectionSize uint32 39 var bytesRead uint64 40 for limit > 0 { 41 if subsectionID, err = r.ReadByte(); err != nil { 42 if err == io.EOF { 43 return result, nil 44 } 45 // TODO: untestable as this can't fail for a reason beside EOF reading a byte from a buffer 46 return nil, fmt.Errorf("failed to read a subsection ID: %w", err) 47 } 48 limit-- 49 50 if subsectionSize, bytesRead, err = leb128.DecodeUint32(r); err != nil { 51 return nil, fmt.Errorf("failed to read the size of subsection[%d]: %w", subsectionID, err) 52 } 53 limit -= bytesRead 54 55 switch subsectionID { 56 case subsectionIDModuleName: 57 if result.ModuleName, _, err = decodeUTF8(r, "module name"); err != nil { 58 return nil, err 59 } 60 case subsectionIDFunctionNames: 61 if result.FunctionNames, err = decodeFunctionNames(r); err != nil { 62 return nil, err 63 } 64 case subsectionIDLocalNames: 65 if result.LocalNames, err = decodeLocalNames(r); err != nil { 66 return nil, err 67 } 68 default: // Skip other subsections. 69 // Note: Not Seek because it doesn't err when given an offset past EOF. Rather, it leads to undefined state. 70 if _, err = io.CopyN(io.Discard, r, int64(subsectionSize)); err != nil { 71 return nil, fmt.Errorf("failed to skip subsection[%d]: %w", subsectionID, err) 72 } 73 } 74 limit -= uint64(subsectionSize) 75 } 76 return 77 } 78 79 func decodeFunctionNames(r *bytes.Reader) (wasm.NameMap, error) { 80 functionCount, err := decodeFunctionCount(r, subsectionIDFunctionNames) 81 if err != nil { 82 return nil, err 83 } 84 85 result := make(wasm.NameMap, functionCount) 86 for i := uint32(0); i < functionCount; i++ { 87 functionIndex, err := decodeFunctionIndex(r, subsectionIDFunctionNames) 88 if err != nil { 89 return nil, err 90 } 91 92 name, _, err := decodeUTF8(r, "function[%d] name", functionIndex) 93 if err != nil { 94 return nil, err 95 } 96 result[i] = &wasm.NameAssoc{Index: functionIndex, Name: name} 97 } 98 return result, nil 99 } 100 101 func decodeLocalNames(r *bytes.Reader) (wasm.IndirectNameMap, error) { 102 functionCount, err := decodeFunctionCount(r, subsectionIDLocalNames) 103 if err != nil { 104 return nil, err 105 } 106 107 result := make(wasm.IndirectNameMap, functionCount) 108 for i := uint32(0); i < functionCount; i++ { 109 functionIndex, err := decodeFunctionIndex(r, subsectionIDLocalNames) 110 if err != nil { 111 return nil, err 112 } 113 114 localCount, _, err := leb128.DecodeUint32(r) 115 if err != nil { 116 return nil, fmt.Errorf("failed to read the local count for function[%d]: %w", functionIndex, err) 117 } 118 119 locals := make(wasm.NameMap, localCount) 120 for j := uint32(0); j < localCount; j++ { 121 localIndex, _, err := leb128.DecodeUint32(r) 122 if err != nil { 123 return nil, fmt.Errorf("failed to read a local index of function[%d]: %w", functionIndex, err) 124 } 125 126 name, _, err := decodeUTF8(r, "function[%d] local[%d] name", functionIndex, localIndex) 127 if err != nil { 128 return nil, err 129 } 130 locals[j] = &wasm.NameAssoc{Index: localIndex, Name: name} 131 } 132 result[i] = &wasm.NameMapAssoc{Index: functionIndex, NameMap: locals} 133 } 134 return result, nil 135 } 136 137 func decodeFunctionIndex(r *bytes.Reader, subsectionID uint8) (uint32, error) { 138 functionIndex, _, err := leb128.DecodeUint32(r) 139 if err != nil { 140 return 0, fmt.Errorf("failed to read a function index in subsection[%d]: %w", subsectionID, err) 141 } 142 return functionIndex, nil 143 } 144 145 func decodeFunctionCount(r *bytes.Reader, subsectionID uint8) (uint32, error) { 146 functionCount, _, err := leb128.DecodeUint32(r) 147 if err != nil { 148 return 0, fmt.Errorf("failed to read the function count of subsection[%d]: %w", subsectionID, err) 149 } 150 return functionCount, nil 151 } 152 153 // encodeNameSectionData serializes the data for the "name" key in wasm.SectionIDCustom according to the 154 // standard: 155 // 156 // Note: The result can be nil because this does not encode empty subsections 157 // 158 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namesec 159 func encodeNameSectionData(n *wasm.NameSection) (data []byte) { 160 if n.ModuleName != "" { 161 data = append(data, encodeNameSubsection(subsectionIDModuleName, encodeSizePrefixed([]byte(n.ModuleName)))...) 162 } 163 if fd := encodeFunctionNameData(n); len(fd) > 0 { 164 data = append(data, encodeNameSubsection(subsectionIDFunctionNames, fd)...) 165 } 166 if ld := encodeLocalNameData(n); len(ld) > 0 { 167 data = append(data, encodeNameSubsection(subsectionIDLocalNames, ld)...) 168 } 169 return 170 } 171 172 // encodeFunctionNameData encodes the data for the function name subsection. 173 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-funcnamesec 174 func encodeFunctionNameData(n *wasm.NameSection) []byte { 175 if len(n.FunctionNames) == 0 { 176 return nil 177 } 178 179 return encodeNameMap(n.FunctionNames) 180 } 181 182 func encodeNameMap(m wasm.NameMap) []byte { 183 count := uint32(len(m)) 184 data := leb128.EncodeUint32(count) 185 for _, na := range m { 186 data = append(data, encodeNameAssoc(na)...) 187 } 188 return data 189 } 190 191 // encodeLocalNameData encodes the data for the local name subsection. 192 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-localnamesec 193 func encodeLocalNameData(n *wasm.NameSection) []byte { 194 if len(n.LocalNames) == 0 { 195 return nil 196 } 197 198 funcNameCount := uint32(len(n.LocalNames)) 199 subsection := leb128.EncodeUint32(funcNameCount) 200 201 for _, na := range n.LocalNames { 202 locals := encodeNameMap(na.NameMap) 203 subsection = append(subsection, append(leb128.EncodeUint32(na.Index), locals...)...) 204 } 205 return subsection 206 } 207 208 // encodeNameSubsection returns a buffer encoding the given subsection 209 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#subsections%E2%91%A0 210 func encodeNameSubsection(subsectionID uint8, content []byte) []byte { 211 contentSizeInBytes := leb128.EncodeUint32(uint32(len(content))) 212 result := []byte{subsectionID} 213 result = append(result, contentSizeInBytes...) 214 result = append(result, content...) 215 return result 216 } 217 218 // encodeNameAssoc encodes the index and data prefixed by their size. 219 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namemap 220 func encodeNameAssoc(na *wasm.NameAssoc) []byte { 221 return append(leb128.EncodeUint32(na.Index), encodeSizePrefixed([]byte(na.Name))...) 222 } 223 224 // encodeSizePrefixed encodes the data prefixed by their size. 225 func encodeSizePrefixed(data []byte) []byte { 226 size := leb128.EncodeUint32(uint32(len(data))) 227 return append(size, data...) 228 }