wa-lang.org/wazero@v1.0.2/api/wasm.go (about) 1 // Package api includes constants and interfaces used by both end-users and internal implementations. 2 package api 3 4 import ( 5 "context" 6 "fmt" 7 "math" 8 ) 9 10 // ExternType classifies imports and exports with their respective types. 11 // 12 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#external-types%E2%91%A0 13 type ExternType = byte 14 15 const ( 16 ExternTypeFunc ExternType = 0x00 17 ExternTypeTable ExternType = 0x01 18 ExternTypeMemory ExternType = 0x02 19 ExternTypeGlobal ExternType = 0x03 20 ) 21 22 // The below are exported to consolidate parsing behavior for external types. 23 const ( 24 // ExternTypeFuncName is the name of the WebAssembly 1.0 (20191205) Text Format field for ExternTypeFunc. 25 ExternTypeFuncName = "func" 26 // ExternTypeTableName is the name of the WebAssembly 1.0 (20191205) Text Format field for ExternTypeTable. 27 ExternTypeTableName = "table" 28 // ExternTypeMemoryName is the name of the WebAssembly 1.0 (20191205) Text Format field for ExternTypeMemory. 29 ExternTypeMemoryName = "memory" 30 // ExternTypeGlobalName is the name of the WebAssembly 1.0 (20191205) Text Format field for ExternTypeGlobal. 31 ExternTypeGlobalName = "global" 32 ) 33 34 // ExternTypeName returns the name of the WebAssembly 1.0 (20191205) Text Format field of the given type. 35 // 36 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A4 37 func ExternTypeName(et ExternType) string { 38 switch et { 39 case ExternTypeFunc: 40 return ExternTypeFuncName 41 case ExternTypeTable: 42 return ExternTypeTableName 43 case ExternTypeMemory: 44 return ExternTypeMemoryName 45 case ExternTypeGlobal: 46 return ExternTypeGlobalName 47 } 48 return fmt.Sprintf("%#x", et) 49 } 50 51 // ValueType describes a parameter or result type mapped to a WebAssembly 52 // function signature. 53 // 54 // The following describes how to convert between Wasm and Golang types: 55 // 56 // - ValueTypeI32 - EncodeU32 DecodeU32 for uint32 / EncodeI32 DecodeI32 for int32 57 // - ValueTypeI64 - uint64(int64) 58 // - ValueTypeF32 - EncodeF32 DecodeF32 from float32 59 // - ValueTypeF64 - EncodeF64 DecodeF64 from float64 60 // - ValueTypeExternref - unintptr(unsafe.Pointer(p)) where p is any pointer 61 // type in Go (e.g. *string) 62 // 63 // e.g. Given a Text Format type use (param i64) (result i64), no conversion is 64 // necessary. 65 // 66 // results, _ := fn(ctx, input) 67 // result := result[0] 68 // 69 // e.g. Given a Text Format type use (param f64) (result f64), conversion is 70 // necessary. 71 // 72 // results, _ := fn(ctx, api.EncodeF64(input)) 73 // result := api.DecodeF64(result[0]) 74 // 75 // Note: This is a type alias as it is easier to encode and decode in the 76 // binary format. 77 // 78 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-valtype 79 type ValueType = byte 80 81 const ( 82 // ValueTypeI32 is a 32-bit integer. 83 ValueTypeI32 ValueType = 0x7f 84 // ValueTypeI64 is a 64-bit integer. 85 ValueTypeI64 ValueType = 0x7e 86 // ValueTypeF32 is a 32-bit floating point number. 87 ValueTypeF32 ValueType = 0x7d 88 // ValueTypeF64 is a 64-bit floating point number. 89 ValueTypeF64 ValueType = 0x7c 90 91 // ValueTypeExternref is a externref type. 92 // 93 // Note: in wazero, externref type value are opaque raw 64-bit pointers, 94 // and the ValueTypeExternref type in the signature will be translated as 95 // uintptr in wazero's API level. 96 // 97 // For example, given the import function: 98 // (func (import "env" "f") (param externref) (result externref)) 99 // 100 // This can be defined in Go as: 101 // r.NewHostModuleBuilder("env"). 102 // NewFunctionBuilder(). 103 // WithFunc(func(context.Context, _ uintptr) (_ uintptr) { return }). 104 // Export("f") 105 // 106 // Note: The usage of this type is toggled with api.CoreFeatureBulkMemoryOperations. 107 ValueTypeExternref ValueType = 0x6f 108 ) 109 110 // ValueTypeName returns the type name of the given ValueType as a string. 111 // These type names match the names used in the WebAssembly text format. 112 // 113 // Note: This returns "unknown", if an undefined ValueType value is passed. 114 func ValueTypeName(t ValueType) string { 115 switch t { 116 case ValueTypeI32: 117 return "i32" 118 case ValueTypeI64: 119 return "i64" 120 case ValueTypeF32: 121 return "f32" 122 case ValueTypeF64: 123 return "f64" 124 case ValueTypeExternref: 125 return "externref" 126 } 127 return "unknown" 128 } 129 130 // Module return functions exported in a module, post-instantiation. 131 // 132 // # Notes 133 // 134 // - Closing the wazero.Runtime closes any Module it instantiated. 135 // - This is an interface for decoupling, not third-party implementations. All implementations are in wazero. 136 // 137 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#external-types%E2%91%A0 138 type Module interface { 139 fmt.Stringer 140 141 // Name is the name this module was instantiated with. Exported functions can be imported with this name. 142 Name() string 143 144 // Memory returns a memory defined in this module or nil if there are none wasn't. 145 Memory() Memory 146 147 // ExportedFunction returns a function exported from this module or nil if it wasn't. 148 ExportedFunction(name string) Function 149 150 // TODO: Table 151 152 // ExportedMemory returns a memory exported from this module or nil if it wasn't. 153 // 154 // WASI modules require exporting a Memory named "memory". This means that a module successfully initialized 155 // as a WASI Command or Reactor will never return nil for this name. 156 // 157 // See https://github.com/WebAssembly/WASI/blob/snapshot-01/design/application-abi.md#current-unstable-abi 158 ExportedMemory(name string) Memory 159 160 // ExportedGlobal a global exported from this module or nil if it wasn't. 161 ExportedGlobal(name string) Global 162 163 // CloseWithExitCode releases resources allocated for this Module. Use a non-zero exitCode parameter to indicate a 164 // failure to ExportedFunction callers. 165 // 166 // The error returned here, if present, is about resource de-allocation (such as I/O errors). Only the last error is 167 // returned, so a non-nil return means at least one error happened. Regardless of error, this module instance will 168 // be removed, making its name available again. 169 // 170 // Calling this inside a host function is safe, and may cause ExportedFunction callers to receive a sys.ExitError 171 // with the exitCode. 172 CloseWithExitCode(ctx context.Context, exitCode uint32) error 173 174 // Closer closes this module by delegating to CloseWithExitCode with an exit code of zero. 175 Closer 176 } 177 178 // Closer closes a resource. 179 // 180 // Note: This is an interface for decoupling, not third-party implementations. All implementations are in wazero. 181 type Closer interface { 182 // Close closes the resource. 183 Close(context.Context) error 184 } 185 186 // ExportDefinition is a WebAssembly type exported in a module 187 // (wazero.CompiledModule). 188 // 189 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A0 190 type ExportDefinition interface { 191 // ModuleName is the possibly empty name of the module defining this 192 // export. 193 // 194 // Note: This may be different from Module.Name, because a compiled module 195 // can be instantiated multiple times as different names. 196 ModuleName() string 197 198 // Index is the position in the module's index namespace, imports first. 199 Index() uint32 200 201 // Import returns true with the module and name when this was imported. 202 // Otherwise, it returns false. 203 // 204 // Note: Empty string is valid for both names in the WebAssembly Core 205 // Specification, so "" "" is possible. 206 Import() (moduleName, name string, isImport bool) 207 208 // ExportNames include all exported names. 209 // 210 // Note: The empty name is allowed in the WebAssembly Core Specification, 211 // so "" is possible. 212 ExportNames() []string 213 } 214 215 // MemoryDefinition is a WebAssembly memory exported in a module 216 // (wazero.CompiledModule). Units are in pages (64KB). 217 // 218 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A0 219 type MemoryDefinition interface { 220 ExportDefinition 221 222 // Min returns the possibly zero initial count of 64KB pages. 223 Min() uint32 224 225 // Max returns the possibly zero max count of 64KB pages, or false if 226 // unbounded. 227 Max() (uint32, bool) 228 } 229 230 // FunctionDefinition is a WebAssembly function exported in a module 231 // (wazero.CompiledModule). 232 // 233 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A0 234 type FunctionDefinition interface { 235 ExportDefinition 236 237 // Name is the module-defined name of the function, which is not necessarily 238 // the same as its export name. 239 Name() string 240 241 // DebugName identifies this function based on its Index or Name in the 242 // module. This is used for errors and stack traces. e.g. "env.abort". 243 // 244 // When the function name is empty, a substitute name is generated by 245 // prefixing '$' to its position in the index namespace. Ex ".$0" is the 246 // first function (possibly imported) in an unnamed module. 247 // 248 // The format is dot-delimited module and function name, but there are no 249 // restrictions on the module and function name. This means either can be 250 // empty or include dots. e.g. "x.x.x" could mean module "x" and name "x.x", 251 // or it could mean module "x.x" and name "x". 252 // 253 // Note: This name is stable regardless of import or export. For example, 254 // if Import returns true, the value is still based on the Name or Index 255 // and not the imported function name. 256 DebugName() string 257 258 // GoFunction is non-nil when implemented by the embedder instead of a wasm 259 // binary, e.g. via wazero.HostModuleBuilder 260 // 261 // The expected results are nil, GoFunction or GoModuleFunction. 262 GoFunction() interface{} 263 264 // ParamTypes are the possibly empty sequence of value types accepted by a 265 // function with this signature. 266 // 267 // See ValueType documentation for encoding rules. 268 ParamTypes() []ValueType 269 270 // ParamNames are index-correlated with ParamTypes or nil if not available 271 // for one or more parameters. 272 ParamNames() []string 273 274 // ResultTypes are the results of the function. 275 // 276 // When WebAssembly 1.0 (20191205), there can be at most one result. 277 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#result-types%E2%91%A0 278 // 279 // See ValueType documentation for encoding rules. 280 ResultTypes() []ValueType 281 } 282 283 // Function is a WebAssembly function exported from an instantiated module 284 // (wazero.Runtime InstantiateModule). 285 // 286 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-func 287 type Function interface { 288 // Definition is metadata about this function from its defining module. 289 Definition() FunctionDefinition 290 291 // Call invokes the function with the given parameters and returns any 292 // results or an error for any failure looking up or invoking the function. 293 // 294 // Encoding is described in Definition, and supplying an incorrect count of 295 // parameters vs FunctionDefinition.ParamTypes is an error. 296 // 297 // If the exporting Module was closed during this call, the error returned 298 // may be a sys.ExitError. See Module.CloseWithExitCode for details. 299 // 300 // Call is not goroutine-safe, therefore it is recommended to create 301 // another Function if you want to invoke the same function concurrently. 302 // On the other hand, sequential invocations of Call is allowed. 303 // 304 // To safely encode/decode params/results expressed as uint64, users are encouraged to 305 // use api.EncodeXXX or DecodeXXX functions. See the docs on api.ValueType. 306 Call(ctx context.Context, params ...uint64) ([]uint64, error) 307 } 308 309 // GoModuleFunction is a Function implemented in Go instead of a wasm binary. 310 // The Module parameter is the calling module, used to access memory or 311 // exported functions. See GoModuleFunc for an example. 312 // 313 // The stack is includes any parameters encoded according to their ValueType. 314 // Its length is the max of parameter or result length. When there are results, 315 // write them in order beginning at index zero. Do not use the stack after the 316 // function returns. 317 // 318 // Here's a typical way to read three parameters and write back one. 319 // 320 // // read parameters off the stack in index order 321 // argv, argvBuf := api.DecodeU32(stack[0]), api.DecodeU32(stack[1]) 322 // 323 // // write results back to the stack in index order 324 // stack[0] = api.EncodeU32(ErrnoSuccess) 325 // 326 // This function can be non-deterministic or cause side effects. It also 327 // has special properties not defined in the WebAssembly Core specification. 328 // Notably, this uses the caller's memory (via Module.Memory). See 329 // https://www.w3.org/TR/wasm-core-1/#host-functions%E2%91%A0 330 // 331 // Most end users will not define functions directly with this, as they will 332 // use reflection or code generators instead. These approaches are more 333 // idiomatic as they can map go types to ValueType. This type is exposed for 334 // those willing to trade usability and safety for performance. 335 // 336 // To safely decode/encode values from/to the uint64 stack, users are encouraged to use 337 // api.EncodeXXX or api.DecodeXXX functions. See the docs on api.ValueType. 338 type GoModuleFunction interface { 339 Call(ctx context.Context, mod Module, stack []uint64) 340 } 341 342 // GoModuleFunc is a convenience for defining an inlined function. 343 // 344 // For example, the following returns an uint32 value read from parameter zero: 345 // 346 // api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) { 347 // offset := api.DecodeU32(params[0]) // read the parameter from the stack 348 // 349 // ret, ok := mod.Memory().ReadUint32Le(ctx, offset) 350 // if !ok { 351 // panic("out of memory") 352 // } 353 // 354 // results[0] = api.EncodeU32(ret) // add the result back to the stack. 355 // }) 356 type GoModuleFunc func(ctx context.Context, mod Module, stack []uint64) 357 358 // Call implements GoModuleFunction.Call. 359 func (f GoModuleFunc) Call(ctx context.Context, mod Module, stack []uint64) { 360 f(ctx, mod, stack) 361 } 362 363 // GoFunction is an optimized form of GoModuleFunction which doesn't require 364 // the Module parameter. See GoFunc for an example. 365 // 366 // For example, this function does not need to use the importing module's 367 // memory or exported functions. 368 type GoFunction interface { 369 Call(ctx context.Context, stack []uint64) 370 } 371 372 // GoFunc is a convenience for defining an inlined function. 373 // 374 // For example, the following returns the sum of two uint32 parameters: 375 // 376 // api.GoFunc(func(ctx context.Context, stack []uint64) { 377 // x, y := api.DecodeU32(params[0]), api.DecodeU32(params[1]) 378 // results[0] = api.EncodeU32(x + y) 379 // }) 380 type GoFunc func(ctx context.Context, stack []uint64) 381 382 // Call implements GoFunction.Call. 383 func (f GoFunc) Call(ctx context.Context, stack []uint64) { 384 f(ctx, stack) 385 } 386 387 // Global is a WebAssembly 1.0 (20191205) global exported from an instantiated module (wazero.Runtime InstantiateModule). 388 // 389 // For example, if the value is not mutable, you can read it once: 390 // 391 // offset := module.ExportedGlobal("memory.offset").Get() 392 // 393 // Globals are allowed by specification to be mutable. However, this can be disabled by configuration. When in doubt, 394 // safe cast to find out if the value can change. Here's an example: 395 // 396 // offset := module.ExportedGlobal("memory.offset") 397 // if _, ok := offset.(api.MutableGlobal); ok { 398 // // value can change 399 // } else { 400 // // value is constant 401 // } 402 // 403 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#globals%E2%91%A0 404 type Global interface { 405 fmt.Stringer 406 407 // Type describes the numeric type of the global. 408 Type() ValueType 409 410 // Get returns the last known value of this global. 411 // 412 // See Type for how to encode this value from a Go type. 413 Get(context.Context) uint64 414 } 415 416 // MutableGlobal is a Global whose value can be updated at runtime (variable). 417 type MutableGlobal interface { 418 Global 419 420 // Set updates the value of this global. 421 // 422 // See Global.Type for how to decode this value to a Go type. 423 Set(ctx context.Context, v uint64) 424 } 425 426 // Memory allows restricted access to a module's memory. Notably, this does not allow growing. 427 // 428 // # Notes 429 // 430 // - This is an interface for decoupling, not third-party implementations. All implementations are in wazero. 431 // - This includes all value types available in WebAssembly 1.0 (20191205) and all are encoded little-endian. 432 // 433 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#storage%E2%91%A0 434 type Memory interface { 435 // Definition is metadata about this memory from its defining module. 436 Definition() MemoryDefinition 437 438 // Size returns the size in bytes available. e.g. If the underlying memory 439 // has 1 page: 65536 440 // 441 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#-hrefsyntax-instr-memorymathsfmemorysize%E2%91%A0 442 Size(context.Context) uint32 443 444 // Grow increases memory by the delta in pages (65536 bytes per page). 445 // The return val is the previous memory size in pages, or false if the 446 // delta was ignored as it exceeds MemoryDefinition.Max. 447 // 448 // # Notes 449 // 450 // - This is the same as the "memory.grow" instruction defined in the 451 // WebAssembly Core Specification, except returns false instead of -1. 452 // - When this returns true, any shared views via Read must be refreshed. 453 // 454 // See MemorySizer Read and https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem 455 Grow(ctx context.Context, deltaPages uint32) (previousPages uint32, ok bool) 456 457 // ReadByte reads a single byte from the underlying buffer at the offset or returns false if out of range. 458 ReadByte(ctx context.Context, offset uint32) (byte, bool) 459 460 // ReadUint16Le reads a uint16 in little-endian encoding from the underlying buffer at the offset in or returns 461 // false if out of range. 462 ReadUint16Le(ctx context.Context, offset uint32) (uint16, bool) 463 464 // ReadUint32Le reads a uint32 in little-endian encoding from the underlying buffer at the offset in or returns 465 // false if out of range. 466 ReadUint32Le(ctx context.Context, offset uint32) (uint32, bool) 467 468 // ReadFloat32Le reads a float32 from 32 IEEE 754 little-endian encoded bits in the underlying buffer at the offset 469 // or returns false if out of range. 470 // See math.Float32bits 471 ReadFloat32Le(ctx context.Context, offset uint32) (float32, bool) 472 473 // ReadUint64Le reads a uint64 in little-endian encoding from the underlying buffer at the offset or returns false 474 // if out of range. 475 ReadUint64Le(ctx context.Context, offset uint32) (uint64, bool) 476 477 // ReadFloat64Le reads a float64 from 64 IEEE 754 little-endian encoded bits in the underlying buffer at the offset 478 // or returns false if out of range. 479 // 480 // See math.Float64bits 481 ReadFloat64Le(ctx context.Context, offset uint32) (float64, bool) 482 483 // Read reads byteCount bytes from the underlying buffer at the offset or 484 // returns false if out of range. 485 // 486 // For example, to search for a NUL-terminated string: 487 // buf, _ = memory.Read(ctx, offset, byteCount) 488 // n := bytes.IndexByte(buf, 0) 489 // if n < 0 { 490 // // Not found! 491 // } 492 // 493 // Write-through 494 // 495 // This returns a view of the underlying memory, not a copy. This means any 496 // writes to the slice returned are visible to Wasm, and any updates from 497 // Wasm are visible reading the returned slice. 498 // 499 // For example: 500 // buf, _ = memory.Read(ctx, offset, byteCount) 501 // buf[1] = 'a' // writes through to memory, meaning Wasm code see 'a'. 502 // 503 // If you don't intend-write through, make a copy of the returned slice. 504 // 505 // When to refresh Read 506 // 507 // The returned slice disconnects on any capacity change. For example, 508 // `buf = append(buf, 'a')` might result in a slice that is no longer 509 // shared. The same exists Wasm side. For example, if Wasm changes its 510 // memory capacity, ex via "memory.grow"), the host slice is no longer 511 // shared. Those who need a stable view must set Wasm memory min=max, or 512 // use wazero.RuntimeConfig WithMemoryCapacityPages to ensure max is always 513 // allocated. 514 Read(ctx context.Context, offset, byteCount uint32) ([]byte, bool) 515 516 // WriteByte writes a single byte to the underlying buffer at the offset in or returns false if out of range. 517 WriteByte(ctx context.Context, offset uint32, v byte) bool 518 519 // WriteUint16Le writes the value in little-endian encoding to the underlying buffer at the offset in or returns 520 // false if out of range. 521 WriteUint16Le(ctx context.Context, offset uint32, v uint16) bool 522 523 // WriteUint32Le writes the value in little-endian encoding to the underlying buffer at the offset in or returns 524 // false if out of range. 525 WriteUint32Le(ctx context.Context, offset, v uint32) bool 526 527 // WriteFloat32Le writes the value in 32 IEEE 754 little-endian encoded bits to the underlying buffer at the offset 528 // or returns false if out of range. 529 // 530 // See math.Float32bits 531 WriteFloat32Le(ctx context.Context, offset uint32, v float32) bool 532 533 // WriteUint64Le writes the value in little-endian encoding to the underlying buffer at the offset in or returns 534 // false if out of range. 535 WriteUint64Le(ctx context.Context, offset uint32, v uint64) bool 536 537 // WriteFloat64Le writes the value in 64 IEEE 754 little-endian encoded bits to the underlying buffer at the offset 538 // or returns false if out of range. 539 // 540 // See math.Float64bits 541 WriteFloat64Le(ctx context.Context, offset uint32, v float64) bool 542 543 // Write writes the slice to the underlying buffer at the offset or returns false if out of range. 544 Write(ctx context.Context, offset uint32, v []byte) bool 545 546 // WriteString writes the string to the underlying buffer at the offset or returns false if out of range. 547 WriteString(ctx context.Context, offset uint32, v string) bool 548 } 549 550 // EncodeExternref encodes the input as a ValueTypeExternref. 551 // 552 // See DecodeExternref 553 func EncodeExternref(input uintptr) uint64 { 554 return uint64(input) 555 } 556 557 // DecodeExternref decodes the input as a ValueTypeExternref. 558 // 559 // See EncodeExternref 560 func DecodeExternref(input uint64) uintptr { 561 return uintptr(input) 562 } 563 564 // EncodeI32 encodes the input as a ValueTypeI32. 565 func EncodeI32(input int32) uint64 { 566 return uint64(uint32(input)) 567 } 568 569 // DecodeI32 decodes the input as a ValueTypeI32. 570 func DecodeI32(input uint64) int32 { 571 return int32(input) 572 } 573 574 // EncodeU32 encodes the input as a ValueTypeI32. 575 func EncodeU32(input uint32) uint64 { 576 return uint64(input) 577 } 578 579 // DecodeU32 decodes the input as a ValueTypeI32. 580 func DecodeU32(input uint64) uint32 { 581 return uint32(input) 582 } 583 584 // EncodeI64 encodes the input as a ValueTypeI64. 585 func EncodeI64(input int64) uint64 { 586 return uint64(input) 587 } 588 589 // EncodeF32 encodes the input as a ValueTypeF32. 590 // 591 // See DecodeF32 592 func EncodeF32(input float32) uint64 { 593 return uint64(math.Float32bits(input)) 594 } 595 596 // DecodeF32 decodes the input as a ValueTypeF32. 597 // 598 // See EncodeF32 599 func DecodeF32(input uint64) float32 { 600 return math.Float32frombits(uint32(input)) 601 } 602 603 // EncodeF64 encodes the input as a ValueTypeF64. 604 // 605 // See EncodeF32 606 func EncodeF64(input float64) uint64 { 607 return math.Float64bits(input) 608 } 609 610 // DecodeF64 decodes the input as a ValueTypeF64. 611 // 612 // See EncodeF64 613 func DecodeF64(input uint64) float64 { 614 return math.Float64frombits(input) 615 }