github.com/taubyte/vm-wasm-utils@v1.0.2/module.go (about) 1 package wasm 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "errors" 7 "fmt" 8 "reflect" 9 "sort" 10 "strings" 11 12 "github.com/taubyte/vm-wasm-utils/ieee754" 13 "github.com/taubyte/vm-wasm-utils/leb128" 14 "github.com/tetratelabs/wazero/api" 15 experimental "github.com/tetratelabs/wazero/experimental" 16 ) 17 18 // DecodeModule parses the WebAssembly Binary Format (%.wasm) into a Module. This function returns when the input is 19 // exhausted or an error occurs. The result can be initialized for use via Store.Instantiate. 20 // 21 // Here's a description of the return values: 22 // * result is the module parsed or nil on error 23 // * err is a FormatError invoking the parser, dangling block comments or unexpected characters. 24 // See binary.DecodeModule and text.DecodeModule 25 type DecodeModule func( 26 wasm []byte, 27 enabledFeatures Features, 28 memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32), 29 ) (result *Module, err error) 30 31 // EncodeModule encodes the given module into a byte slice depending on the format of the implementation. 32 // See binary.EncodeModule 33 type EncodeModule func(m *Module) (bytes []byte) 34 35 // Module is a WebAssembly binary representation. 36 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#modules%E2%91%A8 37 // 38 // Differences from the specification: 39 // * NameSection is the only key ("name") decoded from the SectionIDCustom. 40 // * ExportSection is represented as a map for lookup convenience. 41 // * Code.GoFunc is contains any go `func`. It may be present when Code.Body is not. 42 type Module struct { 43 // TypeSection contains the unique FunctionType of functions imported or defined in this module. 44 // 45 // Note: Currently, there is no type ambiguity in the index as WebAssembly 1.0 only defines function type. 46 // In the future, other types may be introduced to support Features such as module linking. 47 // 48 // Note: In the Binary Format, this is SectionIDType. 49 // 50 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#types%E2%91%A0%E2%91%A0 51 TypeSection []*FunctionType 52 53 // ImportSection contains imported functions, tables, memories or globals required for instantiation 54 // (Store.Instantiate). 55 // 56 // Note: there are no unique constraints relating to the two-level namespace of Import.Module and Import.Name. 57 // 58 // Note: In the Binary Format, this is SectionIDImport. 59 // 60 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#import-section%E2%91%A0 61 ImportSection []*Import 62 63 // FunctionSection contains the index in TypeSection of each function defined in this module. 64 // 65 // Note: The function Index namespace begins with imported functions and ends with those defined in this module. 66 // For example, if there are two imported functions and one defined in this module, the function Index 3 is defined 67 // in this module at FunctionSection[0]. 68 // 69 // Note: FunctionSection is index correlated with the CodeSection. If given the same position, ex. 2, a function 70 // type is at TypeSection[FunctionSection[2]], while its locals and body are at CodeSection[2]. 71 // 72 // Note: In the Binary Format, this is SectionIDFunction. 73 // 74 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-section%E2%91%A0 75 FunctionSection []Index 76 77 // TableSection contains each table defined in this module. 78 // 79 // Note: The table Index namespace begins with imported tables and ends with those defined in this module. 80 // For example, if there are two imported tables and one defined in this module, the table Index 3 is defined in 81 // this module at TableSection[0]. 82 // 83 // Note: Version 1.0 (20191205) of the WebAssembly spec allows at most one table definition per module, so the 84 // length of the TableSection can be zero or one, and can only be one if there is no imported table. 85 // 86 // Note: In the Binary Format, this is SectionIDTable. 87 // 88 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-section%E2%91%A0 89 TableSection []*Table 90 91 // MemorySection contains each memory defined in this module. 92 // 93 // Note: The memory Index namespace begins with imported memories and ends with those defined in this module. 94 // For example, if there are two imported memories and one defined in this module, the memory Index 3 is defined in 95 // this module at TableSection[0]. 96 // 97 // Note: Version 1.0 (20191205) of the WebAssembly spec allows at most one memory definition per module, so the 98 // length of the MemorySection can be zero or one, and can only be one if there is no imported memory. 99 // 100 // Note: In the Binary Format, this is SectionIDMemory. 101 // 102 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-section%E2%91%A0 103 MemorySection *Memory 104 105 // GlobalSection contains each global defined in this module. 106 // 107 // Global indexes are offset by any imported globals because the global index space begins with imports, followed by 108 // ones defined in this module. For example, if there are two imported globals and three defined in this module, the 109 // global at index 3 is defined in this module at GlobalSection[0]. 110 // 111 // Note: In the Binary Format, this is SectionIDGlobal. 112 // 113 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#global-section%E2%91%A0 114 GlobalSection []*Global 115 116 // ExportSection contains each export defined in this module. 117 // 118 // Note: In the Binary Format, this is SectionIDExport. 119 // 120 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A0 121 ExportSection []*Export 122 123 // StartSection is the index of a function to call before returning from Store.Instantiate. 124 // 125 // Note: The index here is not the position in the FunctionSection, rather in the function index namespace, which 126 // begins with imported functions. 127 // 128 // Note: In the Binary Format, this is SectionIDStart. 129 // 130 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#start-section%E2%91%A0 131 StartSection *Index 132 133 // Note: In the Binary Format, this is SectionIDElement. 134 ElementSection []*ElementSegment 135 136 // CodeSection is index-correlated with FunctionSection and contains each 137 // function's locals and body. 138 // 139 // When present, the HostFunctionSection of the same index must be nil. 140 // 141 // Note: In the Binary Format, this is SectionIDCode. 142 // 143 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#code-section%E2%91%A0 144 CodeSection []*Code 145 146 // Note: In the Binary Format, this is SectionIDData. 147 DataSection []*DataSegment 148 149 // NameSection is set when the SectionIDCustom "name" was successfully decoded from the binary format. 150 // 151 // Note: This is the only SectionIDCustom defined in the WebAssembly 1.0 (20191205) Binary Format. 152 // Others are skipped as they are not used in wazero. 153 // 154 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#name-section%E2%91%A0 155 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#custom-section%E2%91%A0 156 NameSection *NameSection 157 158 // validatedActiveElementSegments are built on Validate when 159 // SectionIDElement is non-empty and all inputs are valid. 160 // 161 // Note: elementSegments retain Module.ElementSection order. Since an 162 // ElementSegment can overlap with another, order preservation ensures a 163 // consistent initialization result. 164 // 165 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-instances%E2%91%A0 166 validatedActiveElementSegments []*validatedActiveElementSegment 167 168 // DataCountSection is the optional section and holds the number of data segments in the data section. 169 // 170 // Note: This may exist in WebAssembly 2.0 or WebAssembly 1.0 with FeatureBulkMemoryOperations. 171 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-count-section 172 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions 173 DataCountSection *uint32 174 175 // ID is the sha256 value of the source wasm and is used for caching. 176 ID ModuleID 177 178 // FunctionDefinitionSection is a wazero specific section built on Validate. 179 FunctionDefinitionSection []*FunctionDefinition 180 } 181 182 // ModuleID represents sha256 hash value uniquely assigned to Module. 183 type ModuleID = [sha256.Size]byte 184 185 // The wazero specific limitation described at RATIONALE.md. 186 // TL;DR; We multiply by 8 (to get offsets in bytes) and the multiplication result must be less than 32bit max 187 const ( 188 MaximumGlobals = uint32(1 << 27) 189 MaximumFunctionIndex = uint32(1 << 27) 190 MaximumTableIndex = uint32(1 << 27) 191 ) 192 193 // AssignModuleID calculates a sha256 checksum on `wasm` and set Module.ID to the result. 194 func (m *Module) AssignModuleID(wasm []byte) { 195 m.ID = sha256.Sum256(wasm) 196 } 197 198 // TypeOfFunction returns the wasm.SectionIDType index for the given function namespace index or nil. 199 // Note: The function index namespace is preceded by imported functions. 200 // TODO: Returning nil should be impossible when decode results are validated. Validate decode before back-filling tests. 201 func (m *Module) TypeOfFunction(funcIdx Index) *FunctionType { 202 typeSectionLength := uint32(len(m.TypeSection)) 203 if typeSectionLength == 0 { 204 return nil 205 } 206 funcImportCount := Index(0) 207 for _, im := range m.ImportSection { 208 if im.Type == ExternTypeFunc { 209 if funcIdx == funcImportCount { 210 if im.DescFunc >= typeSectionLength { 211 return nil 212 } 213 return m.TypeSection[im.DescFunc] 214 } 215 funcImportCount++ 216 } 217 } 218 funcSectionIdx := funcIdx - funcImportCount 219 if funcSectionIdx >= uint32(len(m.FunctionSection)) { 220 return nil 221 } 222 typeIdx := m.FunctionSection[funcSectionIdx] 223 if typeIdx >= typeSectionLength { 224 return nil 225 } 226 return m.TypeSection[typeIdx] 227 } 228 229 func (m *Module) Validate(enabledFeatures Features) error { 230 for _, tp := range m.TypeSection { 231 tp.CacheNumInUint64() 232 } 233 234 if err := m.validateStartSection(); err != nil { 235 return err 236 } 237 238 functions, globals, memory, tables, err := m.AllDeclarations() 239 if err != nil { 240 return err 241 } 242 243 if err = m.validateImports(enabledFeatures); err != nil { 244 return err 245 } 246 247 if err = m.validateGlobals(globals, uint32(len(functions)), MaximumGlobals); err != nil { 248 return err 249 } 250 251 if err = m.validateMemory(memory, globals, enabledFeatures); err != nil { 252 return err 253 } 254 255 if err = m.validateExports(enabledFeatures, functions, globals, memory, tables); err != nil { 256 return err 257 } 258 259 if m.CodeSection != nil { 260 if err = m.validateFunctions(enabledFeatures, functions, globals, memory, tables, MaximumFunctionIndex); err != nil { 261 return err 262 } 263 } // No need to validate host functions as NewHostModule validates 264 265 if _, err = m.validateTable(enabledFeatures, tables, MaximumTableIndex); err != nil { 266 return err 267 } 268 269 if err = m.validateDataCountSection(); err != nil { 270 return err 271 } 272 return nil 273 } 274 275 func (m *Module) validateStartSection() error { 276 // Check the start function is valid. 277 // TODO: this should be verified during decode so that errors have the correct source positions 278 if m.StartSection != nil { 279 startIndex := *m.StartSection 280 ft := m.TypeOfFunction(startIndex) 281 if ft == nil { // TODO: move this check to decoder so that a module can never be decoded invalidly 282 return fmt.Errorf("invalid start function: func[%d] has an invalid type", startIndex) 283 } 284 if len(ft.Params) > 0 || len(ft.Results) > 0 { 285 return fmt.Errorf("invalid start function: func[%d] must have an empty (nullary) signature: %s", startIndex, ft) 286 } 287 } 288 return nil 289 } 290 291 func (m *Module) validateGlobals(globals []*GlobalType, numFuncts, maxGlobals uint32) error { 292 if uint32(len(globals)) > maxGlobals { 293 return fmt.Errorf("too many globals in a module") 294 } 295 296 // Global initialization constant expression can only reference the imported globals. 297 // See the note on https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#constant-expressions%E2%91%A0 298 importedGlobals := globals[:m.ImportGlobalCount()] 299 for _, g := range m.GlobalSection { 300 if err := validateConstExpression(importedGlobals, numFuncts, g.Init, g.Type.ValType); err != nil { 301 return err 302 } 303 } 304 return nil 305 } 306 307 func (m *Module) validateFunctions(enabledFeatures Features, functions []Index, globals []*GlobalType, memory *Memory, tables []*Table, maximumFunctionIndex uint32) error { 308 if uint32(len(functions)) > maximumFunctionIndex { 309 return fmt.Errorf("too many functions in a store") 310 } 311 312 functionCount := m.SectionElementCount(SectionIDFunction) 313 codeCount := m.SectionElementCount(SectionIDCode) 314 if functionCount == 0 && codeCount == 0 { 315 return nil 316 } 317 318 typeCount := m.SectionElementCount(SectionIDType) 319 if codeCount != functionCount { 320 return fmt.Errorf("code count (%d) != function count (%d)", codeCount, functionCount) 321 } 322 323 declaredFuncIndexes, err := m.declaredFunctionIndexes() 324 if err != nil { 325 return err 326 } 327 328 for idx, typeIndex := range m.FunctionSection { 329 if typeIndex >= typeCount { 330 return fmt.Errorf("invalid %s: type section index %d out of range", m.funcDesc(SectionIDFunction, Index(idx)), typeIndex) 331 } 332 if m.CodeSection[idx].GoFunc != nil { 333 continue 334 } 335 if err = m.validateFunction(enabledFeatures, Index(idx), functions, globals, memory, tables, declaredFuncIndexes); err != nil { 336 return fmt.Errorf("invalid %s: %w", m.funcDesc(SectionIDFunction, Index(idx)), err) 337 } 338 } 339 return nil 340 } 341 342 // declaredFunctionIndexes returns a set of function indexes that can be used as an immediate for OpcodeRefFunc instruction. 343 // 344 // The criteria for which function indexes can be available for that instruction is vague in the spec: 345 // 346 // - "References: the list of function indices that occur in the module outside functions and can hence be used to form references inside them." 347 // - https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/valid/conventions.html#contexts 348 // - "Ref is the set funcidx(module with functions=ε, start=ε) , i.e., the set of function indices occurring in the module, except in its functions or start function." 349 // - https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/valid/modules.html#valid-module 350 // 351 // To clarify, we reverse-engineer logic required to pass the WebAssembly Core specification 2.0 test suite: 352 // https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/ref_func.wast#L78-L115 353 // 354 // To summarize, the function indexes OpcodeRefFunc can refer include: 355 // - existing in an element section regardless of its mode (active, passive, declarative). 356 // - defined as globals whose value type is ValueRefFunc. 357 // - used as an exported function. 358 // 359 // See https://github.com/WebAssembly/reference-types/issues/31 360 // See https://github.com/WebAssembly/reference-types/issues/76 361 func (m *Module) declaredFunctionIndexes() (ret map[Index]struct{}, err error) { 362 ret = map[uint32]struct{}{} 363 364 for _, exp := range m.ExportSection { 365 if exp.Type == ExternTypeFunc { 366 ret[exp.Index] = struct{}{} 367 } 368 } 369 370 for i, g := range m.GlobalSection { 371 if g.Init.Opcode == OpcodeRefFunc { 372 var index uint32 373 index, _, err = leb128.DecodeUint32(bytes.NewReader(g.Init.Data)) 374 if err != nil { 375 err = fmt.Errorf("%s[%d] failed to initialize: %w", SectionIDName(SectionIDGlobal), i, err) 376 return 377 } 378 ret[index] = struct{}{} 379 } 380 } 381 382 for _, elem := range m.ElementSection { 383 for _, index := range elem.Init { 384 if index != nil { 385 ret[*index] = struct{}{} 386 } 387 } 388 } 389 return 390 } 391 392 func (m *Module) funcDesc(sectionID SectionID, sectionIndex Index) string { 393 // Try to improve the error message by collecting any exports: 394 var exportNames []string 395 funcIdx := sectionIndex + m.importCount(ExternTypeFunc) 396 for _, e := range m.ExportSection { 397 if e.Index == funcIdx && e.Type == ExternTypeFunc { 398 exportNames = append(exportNames, fmt.Sprintf("%q", e.Name)) 399 } 400 } 401 sectionIDName := SectionIDName(sectionID) 402 if exportNames == nil { 403 return fmt.Sprintf("%s[%d]", sectionIDName, sectionIndex) 404 } 405 sort.Strings(exportNames) // go map keys do not iterate consistently 406 return fmt.Sprintf("%s[%d] export[%s]", sectionIDName, sectionIndex, strings.Join(exportNames, ",")) 407 } 408 409 func (m *Module) validateMemory(memory *Memory, globals []*GlobalType, enabledFeatures Features) error { 410 var activeElementCount int 411 for _, sec := range m.DataSection { 412 if !sec.IsPassive() { 413 activeElementCount++ 414 } 415 } 416 if activeElementCount > 0 && memory == nil { 417 return fmt.Errorf("unknown memory") 418 } 419 420 for _, d := range m.DataSection { 421 if !d.IsPassive() { 422 if err := validateConstExpression(globals, 0, d.OffsetExpression, ValueTypeI32); err != nil { 423 return fmt.Errorf("calculate offset: %w", err) 424 } 425 } 426 } 427 return nil 428 } 429 430 func (m *Module) validateImports(enabledFeatures Features) error { 431 for _, i := range m.ImportSection { 432 switch i.Type { 433 case ExternTypeGlobal: 434 if !i.DescGlobal.Mutable { 435 continue 436 } 437 if err := enabledFeatures.Require(FeatureMutableGlobal); err != nil { 438 return fmt.Errorf("invalid import[%q.%q] global: %w", i.Module, i.Name, err) 439 } 440 } 441 } 442 return nil 443 } 444 445 func (m *Module) validateExports(enabledFeatures Features, functions []Index, globals []*GlobalType, memory *Memory, tables []*Table) error { 446 for _, exp := range m.ExportSection { 447 index := exp.Index 448 switch exp.Type { 449 case ExternTypeFunc: 450 if index >= uint32(len(functions)) { 451 return fmt.Errorf("unknown function for export[%q]", exp.Name) 452 } 453 case ExternTypeGlobal: 454 if index >= uint32(len(globals)) { 455 return fmt.Errorf("unknown global for export[%q]", exp.Name) 456 } 457 if !globals[index].Mutable { 458 continue 459 } 460 if err := enabledFeatures.Require(FeatureMutableGlobal); err != nil { 461 return fmt.Errorf("invalid export[%q] global[%d]: %w", exp.Name, index, err) 462 } 463 case ExternTypeMemory: 464 if index > 0 || memory == nil { 465 return fmt.Errorf("memory for export[%q] out of range", exp.Name) 466 } 467 case ExternTypeTable: 468 if index >= uint32(len(tables)) { 469 return fmt.Errorf("table for export[%q] out of range", exp.Name) 470 } 471 } 472 } 473 return nil 474 } 475 476 func validateConstExpression(globals []*GlobalType, numFuncs uint32, expr *ConstantExpression, expectedType ValueType) (err error) { 477 var actualType ValueType 478 r := bytes.NewReader(expr.Data) 479 switch expr.Opcode { 480 case OpcodeI32Const: 481 // Treat constants as signed as their interpretation is not yet known per /RATIONALE.md 482 _, _, err = leb128.DecodeInt32(r) 483 if err != nil { 484 return fmt.Errorf("read i32: %w", err) 485 } 486 actualType = ValueTypeI32 487 case OpcodeI64Const: 488 // Treat constants as signed as their interpretation is not yet known per /RATIONALE.md 489 _, _, err = leb128.DecodeInt64(r) 490 if err != nil { 491 return fmt.Errorf("read i64: %w", err) 492 } 493 actualType = ValueTypeI64 494 case OpcodeF32Const: 495 _, err = ieee754.DecodeFloat32(r) 496 if err != nil { 497 return fmt.Errorf("read f32: %w", err) 498 } 499 actualType = ValueTypeF32 500 case OpcodeF64Const: 501 _, err = ieee754.DecodeFloat64(r) 502 if err != nil { 503 return fmt.Errorf("read f64: %w", err) 504 } 505 actualType = ValueTypeF64 506 case OpcodeGlobalGet: 507 id, _, err := leb128.DecodeUint32(r) 508 if err != nil { 509 return fmt.Errorf("read index of global: %w", err) 510 } 511 if uint32(len(globals)) <= id { 512 return fmt.Errorf("global index out of range") 513 } 514 actualType = globals[id].ValType 515 case OpcodeRefNull: 516 reftype, err := r.ReadByte() 517 if err != nil { 518 return fmt.Errorf("read reference type for ref.null: %w", err) 519 } else if reftype != RefTypeFuncref && reftype != RefTypeExternref { 520 return fmt.Errorf("invalid type for ref.null: 0x%x", reftype) 521 } 522 actualType = reftype 523 case OpcodeRefFunc: 524 index, _, err := leb128.DecodeUint32(r) 525 if err != nil { 526 return fmt.Errorf("read i32: %w", err) 527 } else if index >= numFuncs { 528 return fmt.Errorf("ref.func index out of range [%d] with length %d", index, numFuncs-1) 529 } 530 actualType = ValueTypeFuncref 531 case OpcodeVecV128Const: 532 if len(expr.Data) != 16 { 533 return fmt.Errorf("%s needs 16 bytes but was %d bytes", OpcodeVecV128ConstName, len(expr.Data)) 534 } 535 actualType = ValueTypeV128 536 default: 537 return fmt.Errorf("invalid opcode for const expression: 0x%x", expr.Opcode) 538 } 539 540 if actualType != expectedType { 541 return fmt.Errorf("const expression type mismatch expected %s but got %s", 542 ValueTypeName(expectedType), ValueTypeName(actualType)) 543 } 544 return nil 545 } 546 547 func (m *Module) validateDataCountSection() (err error) { 548 if m.DataCountSection != nil && int(*m.DataCountSection) != len(m.DataSection) { 549 err = fmt.Errorf("data count section (%d) doesn't match the length of data section (%d)", 550 *m.DataCountSection, len(m.DataSection)) 551 } 552 return 553 } 554 555 func (m *Module) buildGlobals(importedGlobals []*GlobalInstance) (globals []*GlobalInstance) { 556 for _, gs := range m.GlobalSection { 557 g := &GlobalInstance{Type: gs.Type} 558 switch v := executeConstExpression(importedGlobals, gs.Init).(type) { 559 case uint32: 560 g.Val = uint64(v) 561 case int32: 562 g.Val = uint64(uint32(v)) 563 case int64: 564 g.Val = uint64(v) 565 case float32: 566 g.Val = api.EncodeF32(v) 567 case float64: 568 g.Val = api.EncodeF64(v) 569 case [2]uint64: 570 g.Val, g.ValHi = v[0], v[1] 571 default: 572 panic(fmt.Errorf("BUG: invalid conversion %d", v)) 573 } 574 globals = append(globals, g) 575 } 576 return 577 } 578 579 // BuildFunctions generates function instances for all host or wasm-defined 580 // functions in this module. 581 // 582 // # Notes 583 // - This relies on data generated by Module.BuildFunctionDefinitions. 584 // - This is exported for tests that don't call Instantiate, notably only 585 // enginetest.go. 586 func (m *ModuleInstance) BuildFunctions(mod *Module, listeners []experimental.FunctionListener) (fns []*FunctionInstance) { 587 fns = make([]*FunctionInstance, 0, len(mod.FunctionDefinitionSection)) 588 for i := range mod.FunctionSection { 589 code := mod.CodeSection[i] 590 fns = append(fns, &FunctionInstance{ 591 IsHostFunction: code.IsHostFunction, 592 Kind: code.Kind, 593 LocalTypes: code.LocalTypes, 594 Body: code.Body, 595 GoFunc: code.GoFunc, 596 TypeID: m.TypeIDs[mod.FunctionSection[i]], 597 }) 598 } 599 600 importCount := mod.ImportFuncCount() 601 for i, f := range fns { 602 d := mod.FunctionDefinitionSection[uint32(i)+importCount] 603 f.Module = m 604 f.Idx = d.index 605 f.Type = d.funcType 606 f.definition = d 607 if listeners != nil { 608 f.FunctionListener = listeners[i] 609 } 610 } 611 return 612 } 613 614 func paramNames(localNames IndirectNameMap, funcIdx uint32, paramLen int) []string { 615 for _, nm := range localNames { 616 // Only build parameter names if we have one for each. 617 if nm.Index != funcIdx || len(nm.NameMap) < paramLen { 618 continue 619 } 620 621 ret := make([]string, paramLen) 622 for _, p := range nm.NameMap { 623 if int(p.Index) < paramLen { 624 ret[p.Index] = p.Name 625 } 626 } 627 return ret 628 } 629 return nil 630 } 631 632 func (m *Module) buildMemory() (mem *MemoryInstance) { 633 memSec := m.MemorySection 634 if memSec != nil { 635 mem = NewMemoryInstance(memSec) 636 } 637 return 638 } 639 640 // Index is the offset in an index namespace, not necessarily an absolute position in a Module section. This is because 641 // index namespaces are often preceded by a corresponding type in the Module.ImportSection. 642 // 643 // For example, the function index namespace starts with any ExternTypeFunc in the Module.ImportSection followed by 644 // the Module.FunctionSection 645 // 646 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-index 647 type Index = uint32 648 649 // FunctionType is a possibly empty function signature. 650 // 651 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-types%E2%91%A0 652 type FunctionType struct { 653 // Params are the possibly empty sequence of value types accepted by a function with this signature. 654 Params []ValueType 655 656 // Results are the possibly empty sequence of value types returned by a function with this signature. 657 // 658 // Note: In WebAssembly 1.0 (20191205), there can be at most one result. 659 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#result-types%E2%91%A0 660 Results []ValueType 661 662 // string is cached as it is used both for String and key 663 string string 664 665 // ParamNumInUint64 is the number of uint64 values requires to represent the Wasm param type. 666 ParamNumInUint64 int 667 668 // ResultsNumInUint64 is the number of uint64 values requires to represent the Wasm result type. 669 ResultNumInUint64 int 670 } 671 672 func (f *FunctionType) CacheNumInUint64() { 673 if f.ParamNumInUint64 == 0 { 674 for _, tp := range f.Params { 675 f.ParamNumInUint64++ 676 if tp == ValueTypeV128 { 677 f.ParamNumInUint64++ 678 } 679 } 680 } 681 682 if f.ResultNumInUint64 == 0 { 683 for _, tp := range f.Results { 684 f.ResultNumInUint64++ 685 if tp == ValueTypeV128 { 686 f.ResultNumInUint64++ 687 } 688 } 689 } 690 } 691 692 // EqualsSignature returns true if the function type has the same parameters and results. 693 func (f *FunctionType) EqualsSignature(params []ValueType, results []ValueType) bool { 694 return bytes.Equal(f.Params, params) && bytes.Equal(f.Results, results) 695 } 696 697 // key gets or generates the key for Store.typeIDs. Ex. "i32_v" for one i32 parameter and no (void) result. 698 func (f *FunctionType) key() string { 699 if f.string != "" { 700 return f.string 701 } 702 var ret string 703 for _, b := range f.Params { 704 ret += ValueTypeName(b) 705 } 706 if len(f.Params) == 0 { 707 ret += "v" 708 } 709 ret += "_" 710 for _, b := range f.Results { 711 ret += ValueTypeName(b) 712 } 713 if len(f.Results) == 0 { 714 ret += "v" 715 } 716 f.string = ret 717 return ret 718 } 719 720 // String implements fmt.Stringer. 721 func (f *FunctionType) String() string { 722 return f.key() 723 } 724 725 // Import is the binary representation of an import indicated by Type 726 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-import 727 type Import struct { 728 Type ExternType 729 // Module is the possibly empty primary namespace of this import 730 Module string 731 // Module is the possibly empty secondary namespace of this import 732 Name string 733 // DescFunc is the index in Module.TypeSection when Type equals ExternTypeFunc 734 DescFunc Index 735 // DescTable is the inlined Table when Type equals ExternTypeTable 736 DescTable *Table 737 // DescMem is the inlined Memory when Type equals ExternTypeMemory 738 DescMem *Memory 739 // DescGlobal is the inlined GlobalType when Type equals ExternTypeGlobal 740 DescGlobal *GlobalType 741 } 742 743 // Memory describes the limits of pages (64KB) in a memory. 744 type Memory struct { 745 Min, Cap, Max uint32 746 // IsMaxEncoded true if the Max is encoded in the original source (binary or text). 747 IsMaxEncoded bool 748 } 749 750 // Validate ensures values assigned to Min, Cap and Max are within valid thresholds. 751 func (m *Memory) Validate() error { 752 min, capacity, max := m.Min, m.Cap, m.Max 753 754 if max > MemoryLimitPages { 755 return fmt.Errorf("max %d pages (%s) over limit of %d pages (%s)", 756 max, PagesToUnitOfBytes(max), MemoryLimitPages, PagesToUnitOfBytes(MemoryLimitPages)) 757 } else if min > MemoryLimitPages { 758 return fmt.Errorf("min %d pages (%s) over limit of %d pages (%s)", 759 min, PagesToUnitOfBytes(min), MemoryLimitPages, PagesToUnitOfBytes(MemoryLimitPages)) 760 } else if min > max { 761 return fmt.Errorf("min %d pages (%s) > max %d pages (%s)", 762 min, PagesToUnitOfBytes(min), max, PagesToUnitOfBytes(max)) 763 } else if capacity < min { 764 return fmt.Errorf("capacity %d pages (%s) less than minimum %d pages (%s)", 765 capacity, PagesToUnitOfBytes(capacity), min, PagesToUnitOfBytes(min)) 766 } else if capacity > MemoryLimitPages { 767 return fmt.Errorf("capacity %d pages (%s) over limit of %d pages (%s)", 768 capacity, PagesToUnitOfBytes(capacity), MemoryLimitPages, PagesToUnitOfBytes(MemoryLimitPages)) 769 } 770 return nil 771 } 772 773 type GlobalType struct { 774 ValType ValueType 775 Mutable bool 776 } 777 778 type Global struct { 779 Type *GlobalType 780 Init *ConstantExpression 781 } 782 783 type ConstantExpression struct { 784 Opcode Opcode 785 Data []byte 786 } 787 788 // Export is the binary representation of an export indicated by Type 789 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-export 790 type Export struct { 791 Type ExternType 792 793 // Name is what the host refers to this definition as. 794 Name string 795 796 // Index is the index of the definition to export, the index namespace is by Type 797 // Ex. If ExternTypeFunc, this is a position in the function index namespace. 798 Index Index 799 } 800 801 // Code is an entry in the Module.CodeSection containing the locals and body of the function. 802 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-code 803 type Code struct { 804 // IsHostFunction returns true if the function was implemented by the 805 // embedder (ex via wazero.ModuleBuilder) instead of a wasm binary. 806 // 807 // Notably, host functions can use the caller's memory, which might be 808 // different from its defining module. 809 // 810 // See https://www.w3.org/TR/wasm-core-1/#host-functions%E2%91%A0 811 IsHostFunction bool 812 813 // Kind describes how this function should be called. 814 Kind FunctionKind 815 816 // LocalTypes are any function-scoped variables in insertion order. 817 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-local 818 LocalTypes []ValueType 819 820 // Body is a sequence of expressions ending in OpcodeEnd 821 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-expr 822 Body []byte 823 824 // GoFunc is a host function defined in Go. 825 // 826 // When present, LocalTypes and Body must be nil. 827 // 828 // Note: This has no serialization format, so is not encodable. 829 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#host-functions%E2%91%A2 830 GoFunc *reflect.Value 831 } 832 833 type DataSegment struct { 834 OffsetExpression *ConstantExpression 835 Init []byte 836 } 837 838 // IsPassive returns true if this data segment is "passive" in the sense that memory offset and 839 // index is determined at runtime and used by OpcodeMemoryInitName instruction in the bulk memory 840 // operations proposal. 841 // 842 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions 843 func (d *DataSegment) IsPassive() bool { 844 return d.OffsetExpression == nil 845 } 846 847 // NameSection represent the known custom name subsections defined in the WebAssembly Binary Format 848 // 849 // Note: This can be nil if no names were decoded for any reason including configuration. 850 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#name-section%E2%91%A0 851 type NameSection struct { 852 // ModuleName is the symbolic identifier for a module. Ex. math 853 // 854 // Note: This can be empty for any reason including configuration. 855 ModuleName string 856 857 // FunctionNames is an association of a function index to its symbolic identifier. Ex. add 858 // 859 // * the key (idx) is in the function namespace, where module defined functions are preceded by imported ones. 860 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#functions%E2%91%A7 861 // 862 // Ex. Assuming the below text format is the second import, you would expect FunctionNames[1] = "mul" 863 // (import "Math" "Mul" (func $mul (param $x f32) (param $y f32) (result f32))) 864 // 865 // Note: FunctionNames are only used for debugging. At runtime, functions are called based on raw numeric index. 866 // Note: This can be nil for any reason including configuration. 867 FunctionNames NameMap 868 869 // LocalNames contains symbolic names for function parameters or locals that have one. 870 // 871 // Note: In the Text Format, function local names can inherit parameter names from their type. Ex. 872 // * (module (import (func (param $x i32) (param i32))) (func (type 0))) = [{0, {x,0}}] 873 // * (module (import (func (param i32) (param $y i32))) (func (type 0) (local $z i32))) = [0, [{y,1},{z,2}]] 874 // * (module (func (param $x i32) (local $y i32) (local $z i32))) = [{x,0},{y,1},{z,2}] 875 // 876 // Note: LocalNames are only used for debugging. At runtime, locals are called based on raw numeric index. 877 // Note: This can be nil for any reason including configuration. 878 LocalNames IndirectNameMap 879 } 880 881 // NameMap associates an index with any associated names. 882 // 883 // Note: Often the index namespace bridges multiple sections. For example, the function index namespace starts with any 884 // ExternTypeFunc in the Module.ImportSection followed by the Module.FunctionSection 885 // 886 // Note: NameMap is unique by NameAssoc.Index, but NameAssoc.Name needn't be unique. 887 // Note: When encoding in the Binary format, this must be ordered by NameAssoc.Index 888 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namemap 889 type NameMap []*NameAssoc 890 891 type NameAssoc struct { 892 Index Index 893 Name string 894 } 895 896 // IndirectNameMap associates an index with an association of names. 897 // 898 // Note: IndirectNameMap is unique by NameMapAssoc.Index, but NameMapAssoc.NameMap needn't be unique. 899 // Note: When encoding in the Binary format, this must be ordered by NameMapAssoc.Index 900 // https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-indirectnamemap 901 type IndirectNameMap []*NameMapAssoc 902 903 type NameMapAssoc struct { 904 Index Index 905 NameMap NameMap 906 } 907 908 // AllDeclarations returns all declarations for functions, globals, memories and tables in a module including imported ones. 909 func (m *Module) AllDeclarations() (functions []Index, globals []*GlobalType, memory *Memory, tables []*Table, err error) { 910 for _, imp := range m.ImportSection { 911 switch imp.Type { 912 case ExternTypeFunc: 913 functions = append(functions, imp.DescFunc) 914 case ExternTypeGlobal: 915 globals = append(globals, imp.DescGlobal) 916 case ExternTypeMemory: 917 memory = imp.DescMem 918 case ExternTypeTable: 919 tables = append(tables, imp.DescTable) 920 } 921 } 922 923 functions = append(functions, m.FunctionSection...) 924 for _, g := range m.GlobalSection { 925 globals = append(globals, g.Type) 926 } 927 if m.MemorySection != nil { 928 if memory != nil { // shouldn't be possible due to Validate 929 err = errors.New("at most one table allowed in module") 930 return 931 } 932 memory = m.MemorySection 933 } 934 if m.TableSection != nil { 935 tables = append(tables, m.TableSection...) 936 } 937 return 938 } 939 940 // SectionID identifies the sections of a Module in the WebAssembly 1.0 (20191205) Binary Format. 941 // 942 // Note: these are defined in the wasm package, instead of the binary package, as a key per section is needed regardless 943 // of format, and deferring to the binary type avoids confusion. 944 // 945 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#sections%E2%91%A0 946 type SectionID = byte 947 948 const ( 949 // SectionIDCustom includes the standard defined NameSection and possibly others not defined in the standard. 950 SectionIDCustom SectionID = iota // don't add anything not in https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#sections%E2%91%A0 951 SectionIDType 952 SectionIDImport 953 SectionIDFunction 954 SectionIDTable 955 SectionIDMemory 956 SectionIDGlobal 957 SectionIDExport 958 SectionIDStart 959 SectionIDElement 960 SectionIDCode 961 SectionIDData 962 963 // SectionIDDataCount may exist in WebAssembly 2.0 or WebAssembly 1.0 with FeatureBulkMemoryOperations enabled. 964 // 965 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-count-section 966 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions 967 SectionIDDataCount 968 ) 969 970 // SectionIDName returns the canonical name of a module section. 971 // https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#sections%E2%91%A0 972 func SectionIDName(sectionID SectionID) string { 973 switch sectionID { 974 case SectionIDCustom: 975 return "custom" 976 case SectionIDType: 977 return "type" 978 case SectionIDImport: 979 return "import" 980 case SectionIDFunction: 981 return "function" 982 case SectionIDTable: 983 return "table" 984 case SectionIDMemory: 985 return "memory" 986 case SectionIDGlobal: 987 return "global" 988 case SectionIDExport: 989 return "export" 990 case SectionIDStart: 991 return "start" 992 case SectionIDElement: 993 return "element" 994 case SectionIDCode: 995 return "code" 996 case SectionIDData: 997 return "data" 998 case SectionIDDataCount: 999 return "data_count" 1000 } 1001 return "unknown" 1002 } 1003 1004 // ValueType is an alias of api.ValueType defined to simplify imports. 1005 type ValueType = api.ValueType 1006 1007 const ( 1008 ValueTypeI32 = api.ValueTypeI32 1009 ValueTypeI64 = api.ValueTypeI64 1010 ValueTypeF32 = api.ValueTypeF32 1011 ValueTypeF64 = api.ValueTypeF64 1012 // TODO: ValueTypeV128 is not exposed in the api pkg yet. 1013 ValueTypeV128 ValueType = 0x7b 1014 // TODO: ValueTypeFuncref is not exposed in the api pkg yet. 1015 ValueTypeFuncref ValueType = 0x70 1016 ValueTypeExternref = api.ValueTypeExternref 1017 ) 1018 1019 // ValueTypeName is an alias of api.ValueTypeName defined to simplify imports. 1020 func ValueTypeName(t ValueType) string { 1021 if t == ValueTypeFuncref { 1022 return "funcref" 1023 } else if t == ValueTypeV128 { 1024 return "v128" 1025 } 1026 return api.ValueTypeName(t) 1027 } 1028 1029 func isReferenceValueType(vt ValueType) bool { 1030 return vt == ValueTypeExternref || vt == ValueTypeFuncref 1031 } 1032 1033 // ExternType is an alias of api.ExternType defined to simplify imports. 1034 type ExternType = api.ExternType 1035 1036 const ( 1037 ExternTypeFunc = api.ExternTypeFunc 1038 ExternTypeFuncName = api.ExternTypeFuncName 1039 ExternTypeTable = api.ExternTypeTable 1040 ExternTypeTableName = api.ExternTypeTableName 1041 ExternTypeMemory = api.ExternTypeMemory 1042 ExternTypeMemoryName = api.ExternTypeMemoryName 1043 ExternTypeGlobal = api.ExternTypeGlobal 1044 ExternTypeGlobalName = api.ExternTypeGlobalName 1045 ) 1046 1047 // ExternTypeName is an alias of api.ExternTypeName defined to simplify imports. 1048 func ExternTypeName(t ValueType) string { 1049 return api.ExternTypeName(t) 1050 }