package wasmtime
// #include <wasm.h>
// #include <wasmtime.h>
// #include <stdlib.h>
import "C"
import (
"runtime"
"unsafe"
)
// Strategy is the compilation strategies for wasmtime
type Strategy C.wasmtime_strategy_t
const (
// StrategyAuto will let wasmtime automatically pick an appropriate compilation strategy
StrategyAuto Strategy = C.WASMTIME_STRATEGY_AUTO
// StrategyCranelift will force wasmtime to use the Cranelift backend
StrategyCranelift Strategy = C.WASMTIME_STRATEGY_CRANELIFT
)
// OptLevel decides what degree of optimization wasmtime will perform on generated machine code
type OptLevel C.wasmtime_opt_level_t
const (
// OptLevelNone will perform no optimizations
OptLevelNone OptLevel = C.WASMTIME_OPT_LEVEL_NONE
// OptLevelSpeed will optimize machine code to be as fast as possible
OptLevelSpeed OptLevel = C.WASMTIME_OPT_LEVEL_SPEED
// OptLevelSpeedAndSize will optimize machine code for speed, but also optimize
// to be small, sometimes at the cost of speed.
OptLevelSpeedAndSize OptLevel = C.WASMTIME_OPT_LEVEL_SPEED_AND_SIZE
)
// ProfilingStrategy decides what sort of profiling to enable, if any.
type ProfilingStrategy C.wasmtime_profiling_strategy_t
const (
// ProfilingStrategyNone means no profiler will be used
ProfilingStrategyNone ProfilingStrategy = C.WASMTIME_PROFILING_STRATEGY_NONE
// ProfilingStrategyJitdump will use the "jitdump" linux support
ProfilingStrategyJitdump ProfilingStrategy = C.WASMTIME_PROFILING_STRATEGY_JITDUMP
)
// Config holds options used to create an Engine and customize its behavior.
type Config struct {
_ptr *C.wasm_config_t
}
// NewConfig creates a new `Config` with all default options configured.
func NewConfig() *Config {
config := &Config{_ptr: C.wasm_config_new()}
runtime.SetFinalizer(config, func(config *Config) {
config.Close()
})
return config
}
// SetDebugInfo configures whether dwarf debug information for JIT code is enabled
func (cfg *Config) SetDebugInfo(enabled bool) {
C.wasmtime_config_debug_info_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetMaxWasmStack configures the maximum stack size, in bytes, that JIT code can use.
// The amount of stack space that wasm takes is always relative to the first invocation of wasm on the stack.
// Recursive calls with host frames in the middle will all need to fit within this setting.
// Note that this setting is not interpreted with 100% precision.
func (cfg *Config) SetMaxWasmStack(size int) {
C.wasmtime_config_max_wasm_stack_set(cfg.ptr(), C.size_t(size))
runtime.KeepAlive(cfg)
}
// SetWasmThreads configures whether the wasm threads proposal is enabled
func (cfg *Config) SetWasmThreads(enabled bool) {
C.wasmtime_config_wasm_threads_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWasmReferenceTypes configures whether the wasm reference types proposal is enabled
func (cfg *Config) SetWasmReferenceTypes(enabled bool) {
C.wasmtime_config_wasm_reference_types_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWasmSIMD configures whether the wasm SIMD proposal is enabled
func (cfg *Config) SetWasmSIMD(enabled bool) {
C.wasmtime_config_wasm_simd_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWasmRelaxedSIMD configures whether the wasm relaxed SIMD proposal is enabled
func (cfg *Config) SetWasmRelaxedSIMD(enabled bool) {
C.wasmtime_config_wasm_relaxed_simd_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWasmRelaxedSIMDDeterministic configures whether the wasm relaxed SIMD proposal is in deterministic mode
func (cfg *Config) SetWasmRelaxedSIMDDeterministic(enabled bool) {
C.wasmtime_config_wasm_relaxed_simd_deterministic_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWasmBulkMemory configures whether the wasm bulk memory proposal is enabled
func (cfg *Config) SetWasmBulkMemory(enabled bool) {
C.wasmtime_config_wasm_bulk_memory_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWasmMultiValue configures whether the wasm multi value proposal is enabled
func (cfg *Config) SetWasmMultiValue(enabled bool) {
C.wasmtime_config_wasm_multi_value_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWasmMultiMemory configures whether the wasm multi memory proposal is enabled
func (cfg *Config) SetWasmMultiMemory(enabled bool) {
C.wasmtime_config_wasm_multi_memory_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWasmMemory64 configures whether the wasm memory64 proposal is enabled
func (cfg *Config) SetWasmMemory64(enabled bool) {
C.wasmtime_config_wasm_memory64_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetTailCall configures whether tail calls are enabled
func (cfg *Config) SetWasmTailCall(enabled bool) {
C.wasmtime_config_wasm_tail_call_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetFunctionReferences configures whether function references are enabled
func (cfg *Config) SetWasmFunctionReferences(enabled bool) {
C.wasmtime_config_wasm_function_references_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetGC configures whether garbage collection is enabled
func (cfg *Config) SetWasmGC(enabled bool) {
C.wasmtime_config_wasm_gc_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetWideArithmetic configures whether wide arithmetic is enabled
func (cfg *Config) SetWasmWideArithmetic(enabled bool) {
C.wasmtime_config_wasm_wide_arithmetic_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetConsumFuel configures whether fuel is enabled
func (cfg *Config) SetConsumeFuel(enabled bool) {
C.wasmtime_config_consume_fuel_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetParallelCompilation configures whether compilation should use multiple threads
func (cfg *Config) SetParallelCompilation(enabled bool) {
C.wasmtime_config_parallel_compilation_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetCraneliftNanCanonicalization configures whether whether Cranelift should perform a
// NaN-canonicalization pass.
//
// When Cranelift is used as a code generation backend this will configure it to replace NaNs with a single
// canonical value. This is useful for users requiring entirely deterministic WebAssembly computation.
//
// This is not required by the WebAssembly spec, so it is not enabled by default.
func (cfg *Config) SetCraneliftNanCanonicalization(enabled bool) {
C.wasmtime_config_cranelift_nan_canonicalization_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetNativeUnwindInfo whether to generate native unwind information (e.g. .eh_frame on Linux).
func (cfg *Config) SetNativeUnwindInfo(enabled bool) {
C.wasmtime_config_native_unwind_info_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetMacOSUseMachPorts configures whether, when on macOS, Mach ports are used for exception handling instead
// of traditional Unix-based signal handling.
func (cfg *Config) SetMacOSUseMachPorts(enabled bool) {
C.wasmtime_config_macos_use_mach_ports_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// Configures the “static” style of memory to always be used.
// For more information see the Rust documentation at
// https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_forced.
func (cfg *Config) SetStaticMemoryForced(enabled bool) {
C.wasmtime_config_static_memory_forced_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetMemoryInitCOWSet Configures whether copy-on-write memory-mapped data is used to initialize a linear memory.
//
// Initializing linear memory via a copy-on-write mapping can drastically improve instantiation costs of a
// WebAssembly module because copying memory is deferred. Additionally if a page of memory is only ever read from
// WebAssembly and never written too then the same underlying page of data will be reused between all
// instantiations of a module meaning that if a module is instantiated many times this can lower the overall
// memory required needed to run that module.
func (cfg *Config) SetMemoryInitCOWSet(enabled bool) {
C.wasmtime_config_memory_init_cow_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetStrategy configures what compilation strategy is used to compile wasm code
func (cfg *Config) SetStrategy(strat Strategy) {
C.wasmtime_config_strategy_set(cfg.ptr(), C.wasmtime_strategy_t(strat))
runtime.KeepAlive(cfg)
}
// SetCraneliftDebugVerifier configures whether the cranelift debug verifier will be active when
// cranelift is used to compile wasm code.
func (cfg *Config) SetCraneliftDebugVerifier(enabled bool) {
C.wasmtime_config_cranelift_debug_verifier_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}
// SetCraneliftOptLevel configures the cranelift optimization level for generated code
func (cfg *Config) SetCraneliftOptLevel(level OptLevel) {
C.wasmtime_config_cranelift_opt_level_set(cfg.ptr(), C.wasmtime_opt_level_t(level))
runtime.KeepAlive(cfg)
}
// SetProfiler configures what profiler strategy to use for generated code
func (cfg *Config) SetProfiler(profiler ProfilingStrategy) {
C.wasmtime_config_profiler_set(cfg.ptr(), C.wasmtime_profiling_strategy_t(profiler))
runtime.KeepAlive(cfg)
}
// CacheConfigLoadDefault enables compiled code caching for this `Config` using the default settings
// configuration can be found.
//
// For more information about caching see
// https://bytecodealliance.github.io/wasmtime/cli-cache.html
func (cfg *Config) CacheConfigLoadDefault() error {
err := C.wasmtime_config_cache_config_load(cfg.ptr(), nil)
runtime.KeepAlive(cfg)
if err != nil {
return mkError(err)
}
return nil
}
// CacheConfigLoad enables compiled code caching for this `Config` using the settings specified
// in the configuration file `path`.
//
// For more information about caching and configuration options see
// https://bytecodealliance.github.io/wasmtime/cli-cache.html
func (cfg *Config) CacheConfigLoad(path string) error {
cstr := C.CString(path)
err := C.wasmtime_config_cache_config_load(cfg.ptr(), cstr)
C.free(unsafe.Pointer(cstr))
runtime.KeepAlive(cfg)
if err != nil {
return mkError(err)
}
return nil
}
// SetEpochInterruption enables epoch-based instrumentation of generated code to
// interrupt WebAssembly execution when the current engine epoch exceeds a
// defined threshold.
func (cfg *Config) SetEpochInterruption(enable bool) {
C.wasmtime_config_epoch_interruption_set(cfg.ptr(), C.bool(enable))
runtime.KeepAlive(cfg)
}
// SetTarget configures the target triple that this configuration will produce
// machine code for.
//
// This option defaults to the native host. Calling this method will
// additionally disable inference of the native features of the host (e.g.
// detection of SSE4.2 on x86_64 hosts). Native features can be reenabled with
// the `cranelift_flag_{set,enable}` properties.
//
// For more information see the Rust documentation at
// https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.config
func (cfg *Config) SetTarget(target string) error {
cstr := C.CString(target)
err := C.wasmtime_config_target_set(cfg.ptr(), cstr)
C.free(unsafe.Pointer(cstr))
runtime.KeepAlive(cfg)
if err != nil {
return mkError(err)
}
return nil
}
// EnableCraneliftFlag enables a target-specific flag in Cranelift.
//
// This can be used, for example, to enable SSE4.2 on x86_64 hosts. Settings can
// be explored with `wasmtime settings` on the CLI.
//
// For more information see the Rust documentation at
// https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_enable
func (cfg *Config) EnableCraneliftFlag(flag string) {
cstr := C.CString(flag)
C.wasmtime_config_cranelift_flag_enable(cfg.ptr(), cstr)
C.free(unsafe.Pointer(cstr))
runtime.KeepAlive(cfg)
}
// SetCraneliftFlag sets a target-specific flag in Cranelift to the specified value.
//
// This can be used, for example, to enable SSE4.2 on x86_64 hosts. Settings can
// be explored with `wasmtime settings` on the CLI.
//
// For more information see the Rust documentation at
// https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.cranelift_flag_set
func (cfg *Config) SetCraneliftFlag(name string, value string) {
cstrName := C.CString(name)
cstrValue := C.CString(value)
C.wasmtime_config_cranelift_flag_set(cfg.ptr(), cstrName, cstrValue)
C.free(unsafe.Pointer(cstrName))
C.free(unsafe.Pointer(cstrValue))
runtime.KeepAlive(cfg)
}
// See comments in `ffi.go` for what's going on here
func (cfg *Config) ptr() *C.wasm_config_t {
ret := cfg._ptr
if ret == nil {
panic("Config has already been used up")
}
maybeGC()
return ret
}
// Close will deallocate this config's state explicitly.
//
// For more information see the documentation for engine.Close()
func (cfg *Config) Close() {
if cfg._ptr == nil {
return
}
runtime.SetFinalizer(cfg, nil)
C.wasm_config_delete(cfg._ptr)
cfg._ptr = nil
}
package wasmtime
// #include <wasmtime.h>
import "C"
import (
"runtime"
)
// Engine is an instance of a wasmtime engine which is used to create a `Store`.
//
// Engines are a form of global configuration for wasm compilations and modules
// and such.
type Engine struct {
_ptr *C.wasm_engine_t
}
// NewEngine creates a new `Engine` with default configuration.
func NewEngine() *Engine {
engine := &Engine{_ptr: C.wasm_engine_new()}
runtime.SetFinalizer(engine, func(engine *Engine) {
engine.Close()
})
return engine
}
// NewEngineWithConfig creates a new `Engine` with the `Config` provided
//
// Note that once a `Config` is passed to this method it cannot be used again.
func NewEngineWithConfig(config *Config) *Engine {
if config.ptr() == nil {
panic("config already used")
}
engine := &Engine{_ptr: C.wasm_engine_new_with_config(config.ptr())}
runtime.SetFinalizer(config, nil)
config._ptr = nil
runtime.SetFinalizer(engine, func(engine *Engine) {
engine.Close()
})
return engine
}
// Close will deallocate this engine's state explicitly.
//
// By default state is cleaned up automatically when an engine is garbage
// collected but the Go GC. The Go GC, however, does not provide strict
// guarantees about finalizers especially in terms of timing. Additionally the
// Go GC is not aware of the full weight of an engine because it holds onto
// allocations in Wasmtime not tracked by the Go GC. For these reasons, it's
// recommended to where possible explicitly call this method and deallocate an
// engine to avoid relying on the Go GC.
//
// This method will deallocate Wasmtime-owned state. Future use of the engine
// will panic because the Wasmtime state is no longer there.
//
// Close can be called multiple times without error. Only the first time will
// deallocate resources.
func (engine *Engine) Close() {
if engine._ptr == nil {
return
}
runtime.SetFinalizer(engine, nil)
C.wasm_engine_delete(engine.ptr())
engine._ptr = nil
}
func (engine *Engine) ptr() *C.wasm_engine_t {
ret := engine._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
// IncrementEpoch will increase the current epoch number by 1 within the
// current engine which will cause any connected stores with their epoch
// deadline exceeded to now be interrupted.
//
// This method is safe to call from any goroutine.
func (engine *Engine) IncrementEpoch() {
C.wasmtime_engine_increment_epoch(engine.ptr())
runtime.KeepAlive(engine)
}
package wasmtime
// #include <wasmtime.h>
import "C"
import "runtime"
type Error struct {
_ptr *C.wasmtime_error_t
}
func mkError(ptr *C.wasmtime_error_t) *Error {
err := &Error{_ptr: ptr}
runtime.SetFinalizer(err, func(err *Error) {
err.Close()
})
return err
}
func (e *Error) ptr() *C.wasmtime_error_t {
ret := e._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (e *Error) Error() string {
message := C.wasm_byte_vec_t{}
C.wasmtime_error_message(e.ptr(), &message)
ret := C.GoStringN(message.data, C.int(message.size))
runtime.KeepAlive(e)
C.wasm_byte_vec_delete(&message)
return ret
}
// ExitStatus returns an `int32` exit status if this was a WASI-defined exit
// code. The `bool` returned indicates whether it was a WASI-defined exit or
// not.
func (e *Error) ExitStatus() (int32, bool) {
status := C.int(0)
ok := C.wasmtime_error_exit_status(e.ptr(), &status)
runtime.KeepAlive(e)
return int32(status), bool(ok)
}
// Close will deallocate this error's state explicitly.
//
// For more information see the documentation for engine.Close()
func (e *Error) Close() {
if e._ptr == nil {
return
}
runtime.SetFinalizer(e, nil)
C.wasmtime_error_delete(e._ptr)
e._ptr = nil
}
package wasmtime
// #include <wasm.h>
import "C"
import "runtime"
// ExportType is one of the exports component.
// A module defines a set of exports that become accessible to the host environment once the module has been instantiated.
type ExportType struct {
_ptr *C.wasm_exporttype_t
_owner interface{}
}
// NewExportType creates a new `ExportType` with the `name` and the type provided.
func NewExportType(name string, ty AsExternType) *ExportType {
nameVec := stringToByteVec(name)
// Creating an export type requires taking ownership, so create a copy
// so we don't have to invalidate pointers here. Shouldn't be too
// costly in theory anyway.
extern := ty.AsExternType()
ptr := C.wasm_externtype_copy(extern.ptr())
runtime.KeepAlive(extern)
// And once we've got all that create the export type!
exportPtr := C.wasm_exporttype_new(&nameVec, ptr)
return mkExportType(exportPtr, nil)
}
func mkExportType(ptr *C.wasm_exporttype_t, owner interface{}) *ExportType {
exporttype := &ExportType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(exporttype, func(exporttype *ExportType) {
exporttype.Close()
})
}
return exporttype
}
func (ty *ExportType) ptr() *C.wasm_exporttype_t {
ret := ty._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (ty *ExportType) owner() interface{} {
if ty._owner != nil {
return ty._owner
}
return ty
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (ty *ExportType) Close() {
if ty._ptr == nil || ty._owner != nil {
return
}
runtime.SetFinalizer(ty, nil)
C.wasm_exporttype_delete(ty._ptr)
ty._ptr = nil
}
// Name returns the name in the module this export type is exporting
func (ty *ExportType) Name() string {
ptr := C.wasm_exporttype_name(ty.ptr())
ret := C.GoStringN(ptr.data, C.int(ptr.size))
runtime.KeepAlive(ty)
return ret
}
// Type returns the type of item this export type expects
func (ty *ExportType) Type() *ExternType {
ptr := C.wasm_exporttype_type(ty.ptr())
return mkExternType(ptr, ty.owner())
}
package wasmtime
// #include "shims.h"
import "C"
import "runtime"
// Extern is an external value, which is the runtime representation of an entity that can be imported or exported.
// It is an address denoting either a function instance, table instance, memory instance, or global instances in the shared store.
// Read more in [spec](https://webassembly.github.io/spec/core/exec/runtime.html#external-values)
type Extern struct {
_ptr *C.wasmtime_extern_t
}
// AsExtern is an interface for all types which can be imported or exported as an Extern
type AsExtern interface {
AsExtern() C.wasmtime_extern_t
}
func mkExtern(ptr *C.wasmtime_extern_t) *Extern {
f := &Extern{_ptr: ptr}
runtime.SetFinalizer(f, func(e *Extern) {
e.Close()
})
return f
}
func (e *Extern) ptr() *C.wasmtime_extern_t {
ret := e._ptr
if ret == nil {
panic("object already closed")
}
maybeGC()
return ret
}
// Close will deallocate this extern's state explicitly.
//
// For more information see the documentation for engine.Close()
func (e *Extern) Close() {
if e._ptr == nil {
return
}
runtime.SetFinalizer(e, nil)
C.wasmtime_extern_delete(e._ptr)
e._ptr = nil
}
// Type returns the type of this export
func (e *Extern) Type(store Storelike) *ExternType {
ptr := C.wasmtime_extern_type(store.Context(), e.ptr())
runtime.KeepAlive(e)
runtime.KeepAlive(store)
return mkExternType(ptr, nil)
}
// Func returns a Func if this export is a function or nil otherwise
func (e *Extern) Func() *Func {
ptr := e.ptr()
if ptr.kind != C.WASMTIME_EXTERN_FUNC {
return nil
}
ret := mkFunc(C.go_wasmtime_extern_func_get(ptr))
runtime.KeepAlive(e)
return ret
}
// Global returns a Global if this export is a global or nil otherwise
func (e *Extern) Global() *Global {
ptr := e.ptr()
if ptr.kind != C.WASMTIME_EXTERN_GLOBAL {
return nil
}
ret := mkGlobal(C.go_wasmtime_extern_global_get(ptr))
runtime.KeepAlive(e)
return ret
}
// Memory returns a Memory if this export is a memory or nil otherwise
func (e *Extern) Memory() *Memory {
ptr := e.ptr()
if ptr.kind != C.WASMTIME_EXTERN_MEMORY {
return nil
}
ret := mkMemory(C.go_wasmtime_extern_memory_get(ptr))
runtime.KeepAlive(e)
return ret
}
// Table returns a Table if this export is a table or nil otherwise
func (e *Extern) Table() *Table {
ptr := e.ptr()
if ptr.kind != C.WASMTIME_EXTERN_TABLE {
return nil
}
ret := mkTable(C.go_wasmtime_extern_table_get(ptr))
runtime.KeepAlive(e)
return ret
}
func (e *Extern) AsExtern() C.wasmtime_extern_t {
return *e.ptr()
}
package wasmtime
// #include <wasm.h>
import "C"
import "runtime"
// ExternType means one of external types which classify imports and external values with their respective types.
type ExternType struct {
_ptr *C.wasm_externtype_t
_owner interface{}
}
// AsExternType is an interface for all types which can be ExternType.
type AsExternType interface {
AsExternType() *ExternType
}
func mkExternType(ptr *C.wasm_externtype_t, owner interface{}) *ExternType {
externtype := &ExternType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(externtype, func(externtype *ExternType) {
externtype.Close()
})
}
return externtype
}
func (ty *ExternType) ptr() *C.wasm_externtype_t {
ret := ty._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (ty *ExternType) owner() interface{} {
if ty._owner != nil {
return ty._owner
}
return ty
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (ty *ExternType) Close() {
if ty._ptr == nil || ty._owner != nil {
return
}
runtime.SetFinalizer(ty, nil)
C.wasm_externtype_delete(ty._ptr)
ty._ptr = nil
}
// FuncType returns the underlying `FuncType` for this `ExternType` if it's a function
// type. Otherwise returns `nil`.
func (ty *ExternType) FuncType() *FuncType {
ptr := C.wasm_externtype_as_functype(ty.ptr())
if ptr == nil {
return nil
}
return mkFuncType(ptr, ty.owner())
}
// GlobalType returns the underlying `GlobalType` for this `ExternType` if it's a *global* type.
// Otherwise returns `nil`.
func (ty *ExternType) GlobalType() *GlobalType {
ptr := C.wasm_externtype_as_globaltype(ty.ptr())
if ptr == nil {
return nil
}
return mkGlobalType(ptr, ty.owner())
}
// TableType returns the underlying `TableType` for this `ExternType` if it's a *table* type.
// Otherwise returns `nil`.
func (ty *ExternType) TableType() *TableType {
ptr := C.wasm_externtype_as_tabletype(ty.ptr())
if ptr == nil {
return nil
}
return mkTableType(ptr, ty.owner())
}
// MemoryType returns the underlying `MemoryType` for this `ExternType` if it's a *memory* type.
// Otherwise returns `nil`.
func (ty *ExternType) MemoryType() *MemoryType {
ptr := C.wasm_externtype_as_memorytype(ty.ptr())
if ptr == nil {
return nil
}
return mkMemoryType(ptr, ty.owner())
}
// AsExternType returns this type itself
func (ty *ExternType) AsExternType() *ExternType {
return ty
}
package wasmtime
// #cgo CFLAGS:-I${SRCDIR}/build/include
// #cgo !windows LDFLAGS:-lwasmtime -lm -ldl -pthread
// #cgo windows CFLAGS:-DWASM_API_EXTERN= -DWASI_API_EXTERN=
// #cgo windows LDFLAGS:-lwasmtime -luserenv -lole32 -lntdll -lws2_32 -lkernel32 -lbcrypt
// #cgo linux,amd64 LDFLAGS:-L${SRCDIR}/build/linux-x86_64
// #cgo linux,arm64 LDFLAGS:-L${SRCDIR}/build/linux-aarch64
// #cgo darwin,amd64 LDFLAGS:-L${SRCDIR}/build/macos-x86_64
// #cgo darwin,arm64 LDFLAGS:-L${SRCDIR}/build/macos-aarch64
// #cgo windows,amd64 LDFLAGS:-L${SRCDIR}/build/windows-x86_64
// #include <wasm.h>
import "C"
import (
"runtime"
"unsafe"
)
// # What's up with `ptr()` methods?
//
// We use `runtime.SetFinalizer` to free all objects we allocate from C. This
// is intended to make usage of the API much simpler since you don't have to
// close/free anything. The tricky part here though is laid out in
// `runtime.SetFinalizer`'s documentation which is that if you read a
// non-gc-value (like a C pointer) from a GC object then after the value is
// read the GC value might get garbage collected. This is quite bad for us
// because the garbage collection will free the C pointer, making the C pointer
// actually invalid.
//
// The solution is to add `runtime.KeepAlive` calls after C function calls to
// ensure that the GC object lives at least as long as the C function call
// itself. This is naturally quite error-prone, so the goal here with `ptr()`
// methods is to make us a bit more resilient to these sorts of errors and
// expose segfaults during development.
//
// Each `ptr()` method has the basic structure of doing these steps:
//
// 1. First it reads the pointer value from the GC object
// 2. Next it conditionally calls `runtime.GC()`, depending on build flags
// 3. Finally it returns the original pointer value
//
// The goal here is to as aggressively as we can collect GC objects when
// testing and trigger finalizers as frequently as we can. This naturally
// slows things down quite a bit, so conditional compilation (with the `debug`
// tag) is used to enable this. Our CI runs tests with `-tag debug` to make
// sure this is at least run somewhere.
//
// If anyone else has a better idea of what to handle all this it would be very
// much appreciated :)
// Convert a Go string into an owned `wasm_byte_vec_t`
func stringToByteVec(s string) C.wasm_byte_vec_t {
vec := C.wasm_byte_vec_t{}
C.wasm_byte_vec_new_uninitialized(&vec, C.size_t(len(s)))
C.memcpy(unsafe.Pointer(vec.data), unsafe.Pointer(C._GoStringPtr(s)), vec.size)
runtime.KeepAlive(s)
return vec
}
package wasmtime
// #include "shims.h"
import "C"
import (
"errors"
"reflect"
"runtime"
"unsafe"
)
// Func is a function instance, which is the runtime representation of a function.
// It effectively is a closure of the original function over the runtime module instance of its originating module.
// The module instance is used to resolve references to other definitions during execution of the function.
// Read more in [spec](https://webassembly.github.io/spec/core/exec/runtime.html#function-instances)
type Func struct {
val C.wasmtime_func_t
}
// Caller is provided to host-defined functions when they're invoked from
// WebAssembly.
//
// A `Caller` can be used for `Storelike` arguments to allow recursive execution
// or creation of wasm objects. Additionally `Caller` can be used to learn about
// the exports of the calling instance.
type Caller struct {
// Note that unlike other structures in these bindings this is named `ptr`
// instead of `_ptr` because no finalizer is configured with `Caller` so it's
// ok to access this raw value.
ptr *C.wasmtime_caller_t
}
// NewFunc creates a new `Func` with the given `ty` which, when called, will call `f`
//
// The `ty` given is the wasm type signature of the `Func` to create. When called
// the `f` callback receives two arguments. The first is a `Caller` to learn
// information about the calling context and the second is a list of arguments
// represented as a `Val`. The parameters are guaranteed to match the parameters
// types specified in `ty`.
//
// The `f` callback is expected to produce one of two values. Results can be
// returned as an array of `[]Val`. The number and types of these results much
// match the `ty` given, otherwise the program will panic. The `f` callback can
// also produce a trap which will trigger trap unwinding in wasm, and the trap
// will be returned to the original caller.
//
// If the `f` callback panics then the panic will be propagated to the caller
// as well.
func NewFunc(
store Storelike,
ty *FuncType,
f func(*Caller, []Val) ([]Val, *Trap),
) *Func {
idx := insertFuncNew(getDataInStore(store), ty, f)
ret := C.wasmtime_func_t{}
C.go_func_new(
store.Context(),
ty.ptr(),
C.size_t(idx),
0,
&ret,
)
runtime.KeepAlive(store)
runtime.KeepAlive(ty)
return mkFunc(ret)
}
//export goTrampolineNew
func goTrampolineNew(
callerPtr *C.wasmtime_caller_t,
env C.size_t,
argsPtr *C.wasmtime_val_t,
argsNum C.size_t,
resultsPtr *C.wasmtime_val_t,
resultsNum C.size_t,
) *C.wasm_trap_t {
caller := &Caller{ptr: callerPtr}
defer func() { caller.ptr = nil }()
data := getDataInStore(caller)
entry := data.getFuncNew(int(env))
params := make([]Val, int(argsNum))
var val C.wasmtime_val_t
base := unsafe.Pointer(argsPtr)
for i := 0; i < len(params); i++ {
ptr := (*C.wasmtime_val_t)(unsafe.Pointer(uintptr(base) + uintptr(i)*unsafe.Sizeof(val)))
params[i] = mkVal(caller, ptr)
}
var results []Val
var trap *Trap
var lastPanic interface{}
func() {
defer func() { lastPanic = recover() }()
results, trap = entry.callback(caller, params)
if trap != nil {
if trap._ptr == nil {
panic("returned an already-returned trap")
}
return
}
if len(results) != len(entry.results) {
panic("callback didn't produce the correct number of results")
}
for i, ty := range entry.results {
if results[i].Kind() != ty.Kind() {
panic("callback produced wrong type of result")
}
}
}()
if trap == nil && lastPanic != nil {
data.lastPanic = lastPanic
trap := NewTrap("go panicked")
runtime.SetFinalizer(trap, nil)
return trap.ptr()
}
if trap != nil {
runtime.SetFinalizer(trap, nil)
ret := trap.ptr()
trap._ptr = nil
return ret
}
base = unsafe.Pointer(resultsPtr)
for i := 0; i < len(results); i++ {
ptr := (*C.wasmtime_val_t)(unsafe.Pointer(uintptr(base) + uintptr(i)*unsafe.Sizeof(val)))
results[i].initialize(caller, ptr)
}
runtime.KeepAlive(results)
return nil
}
// WrapFunc wraps a native Go function, `f`, as a wasm `Func`.
//
// This function differs from `NewFunc` in that it will determine the type
// signature of the wasm function given the input value `f`. The `f` value
// provided must be a Go function. It may take any number of the following
// types as arguments:
//
// `int32` - a wasm `i32`
//
// `int64` - a wasm `i64`
//
// `float32` - a wasm `f32`
//
// `float64` - a wasm `f64`
//
// `*Caller` - information about the caller's instance
//
// `*Func` - a wasm `funcref`
//
// anything else - a wasm `externref`
//
// The Go function may return any number of values. It can return any number of
// primitive wasm values (integers/floats), and the last return value may
// optionally be `*Trap`. If a `*Trap` returned is `nil` then the other values
// are returned from the wasm function. Otherwise the `*Trap` is returned and
// it's considered as if the host function trapped.
//
// If the function `f` panics then the panic will be propagated to the caller.
func WrapFunc(
store Storelike,
f interface{},
) *Func {
val := reflect.ValueOf(f)
wasmTy := inferFuncType(val)
idx := insertFuncWrap(getDataInStore(store), val)
ret := C.wasmtime_func_t{}
C.go_func_new(
store.Context(),
wasmTy.ptr(),
C.size_t(idx),
1, // this is `WrapFunc`, not `NewFunc`
&ret,
)
runtime.KeepAlive(store)
runtime.KeepAlive(wasmTy)
return mkFunc(ret)
}
func inferFuncType(val reflect.Value) *FuncType {
// Make sure the `interface{}` passed in was indeed a function
ty := val.Type()
if ty.Kind() != reflect.Func {
panic("callback provided must be a `func`")
}
// infer the parameter types, and `*Caller` type is special in the
// parameters so be sure to case on that as well.
params := make([]*ValType, 0, ty.NumIn())
var caller *Caller
for i := 0; i < ty.NumIn(); i++ {
paramTy := ty.In(i)
if paramTy != reflect.TypeOf(caller) {
params = append(params, typeToValType(paramTy))
}
}
// Then infer the result types, where a final `*Trap` result value is
// also special.
results := make([]*ValType, 0, ty.NumOut())
var trap *Trap
for i := 0; i < ty.NumOut(); i++ {
resultTy := ty.Out(i)
if i == ty.NumOut()-1 && resultTy == reflect.TypeOf(trap) {
continue
}
results = append(results, typeToValType(resultTy))
}
return NewFuncType(params, results)
}
func typeToValType(ty reflect.Type) *ValType {
var a int32
if ty == reflect.TypeOf(a) {
return NewValType(KindI32)
}
var b int64
if ty == reflect.TypeOf(b) {
return NewValType(KindI64)
}
var c float32
if ty == reflect.TypeOf(c) {
return NewValType(KindF32)
}
var d float64
if ty == reflect.TypeOf(d) {
return NewValType(KindF64)
}
var f *Func
if ty == reflect.TypeOf(f) {
return NewValType(KindFuncref)
}
return NewValType(KindExternref)
}
//export goTrampolineWrap
func goTrampolineWrap(
callerPtr *C.wasmtime_caller_t,
env C.size_t,
argsPtr *C.wasmtime_val_t,
argsNum C.size_t,
resultsPtr *C.wasmtime_val_t,
resultsNum C.size_t,
) *C.wasm_trap_t {
// Convert all our parameters to `[]reflect.Value`, taking special care
// for `*Caller` but otherwise reading everything through `Val`.
caller := &Caller{ptr: callerPtr}
defer func() { caller.ptr = nil }()
data := getDataInStore(caller)
entry := data.getFuncWrap(int(env))
ty := entry.callback.Type()
params := make([]reflect.Value, ty.NumIn())
base := unsafe.Pointer(argsPtr)
var raw C.wasmtime_val_t
for i := 0; i < len(params); i++ {
if ty.In(i) == reflect.TypeOf(caller) {
params[i] = reflect.ValueOf(caller)
} else {
ptr := (*C.wasmtime_val_t)(base)
val := mkVal(caller, ptr)
params[i] = reflect.ValueOf(val.Get())
base = unsafe.Pointer(uintptr(base) + unsafe.Sizeof(raw))
}
}
// Invoke the function, catching any panics to propagate later. Panics
// result in immediately returning a trap.
var results []reflect.Value
var lastPanic interface{}
func() {
defer func() { lastPanic = recover() }()
results = entry.callback.Call(params)
}()
if lastPanic != nil {
data.lastPanic = lastPanic
trap := NewTrap("go panicked")
runtime.SetFinalizer(trap, nil)
return trap.ptr()
}
// And now we write all the results into memory depending on the type
// of value that was returned.
base = unsafe.Pointer(resultsPtr)
for _, result := range results {
ptr := (*C.wasmtime_val_t)(base)
switch val := result.Interface().(type) {
case int32:
ValI32(val).initialize(caller, ptr)
case int64:
ValI64(val).initialize(caller, ptr)
case float32:
ValF32(val).initialize(caller, ptr)
case float64:
ValF64(val).initialize(caller, ptr)
case *Func:
ValFuncref(val).initialize(caller, ptr)
case *Trap:
if val != nil {
runtime.SetFinalizer(val, nil)
ret := val._ptr
val._ptr = nil
if ret == nil {
data.lastPanic = "cannot return trap twice"
return nil
} else {
return ret
}
}
default:
ValExternref(val).initialize(caller, ptr)
}
base = unsafe.Pointer(uintptr(base) + unsafe.Sizeof(raw))
}
return nil
}
func mkFunc(val C.wasmtime_func_t) *Func {
return &Func{val}
}
// Type returns the type of this func
func (f *Func) Type(store Storelike) *FuncType {
ptr := C.wasmtime_func_type(store.Context(), &f.val)
runtime.KeepAlive(store)
return mkFuncType(ptr, nil)
}
// Call invokes this function with the provided `args`.
//
// This variadic function must be invoked with the correct number and type of
// `args` as specified by the type of this function. This property is checked
// at runtime. Each `args` may have one of the following types:
//
// `int32` - a wasm `i32`
//
// `int64` - a wasm `i64`
//
// `float32` - a wasm `f32`
//
// `float64` - a wasm `f64`
//
// `Val` - correspond to a wasm value
//
// `*Func` - a wasm `funcref`
//
// anything else - a wasm `externref`
//
// This function will have one of three results:
//
// 1. If the function returns successfully, then the `interface{}` return
// argument will be the result of the function. If there were 0 results then
// this value is `nil`. If there was one result then this is that result.
// Otherwise if there were multiple results then `[]Val` is returned.
//
// 2. If this function invocation traps, then the returned `interface{}` value
// will be `nil` and a non-`nil` `*Trap` will be returned with information
// about the trap that happened.
//
// 3. If a panic in Go ends up happening somewhere, then this function will
// panic.
func (f *Func) Call(store Storelike, args ...interface{}) (interface{}, error) {
ty := f.Type(store)
params := ty.Params()
if len(args) > len(params) {
return nil, errors.New("too many arguments provided")
}
paramVals := make([]C.wasmtime_val_t, len(args))
var externrefs []Val
for i, param := range args {
dst := ¶mVals[i]
switch val := param.(type) {
case int:
switch params[i].Kind() {
case KindI32:
dst.kind = C.WASMTIME_I32
C.go_wasmtime_val_i32_set(dst, C.int32_t(val))
case KindI64:
dst.kind = C.WASMTIME_I64
C.go_wasmtime_val_i64_set(dst, C.int64_t(val))
default:
return nil, errors.New("integer provided for non-integer argument")
}
case int32:
dst.kind = C.WASMTIME_I32
C.go_wasmtime_val_i32_set(dst, C.int32_t(val))
case int64:
dst.kind = C.WASMTIME_I64
C.go_wasmtime_val_i64_set(dst, C.int64_t(val))
case float32:
dst.kind = C.WASMTIME_F32
C.go_wasmtime_val_f32_set(dst, C.float(val))
case float64:
dst.kind = C.WASMTIME_F64
C.go_wasmtime_val_f64_set(dst, C.double(val))
case *Func:
dst.kind = C.WASMTIME_FUNCREF
if val != nil {
C.go_wasmtime_val_funcref_set(dst, val.val)
} else {
empty := C.wasmtime_func_t{}
C.go_wasmtime_val_funcref_set(dst, empty)
}
case Val:
val.initialize(store, dst)
default:
externref := ValExternref(val)
externrefs = append(externrefs, externref)
externref.initialize(store, dst)
}
}
resultVals := make([]C.wasmtime_val_t, len(ty.Results()))
err := enterWasm(store, func(trap **C.wasm_trap_t) *C.wasmtime_error_t {
var paramsPtr *C.wasmtime_val_t
if len(paramVals) > 0 {
paramsPtr = (*C.wasmtime_val_t)(unsafe.Pointer(¶mVals[0]))
}
var resultsPtr *C.wasmtime_val_t
if len(resultVals) > 0 {
resultsPtr = (*C.wasmtime_val_t)(unsafe.Pointer(&resultVals[0]))
}
return C.wasmtime_func_call(
store.Context(),
&f.val,
paramsPtr,
C.size_t(len(paramVals)),
resultsPtr,
C.size_t(len(resultVals)),
trap,
)
})
runtime.KeepAlive(store)
runtime.KeepAlive(args)
runtime.KeepAlive(resultVals)
runtime.KeepAlive(paramVals)
runtime.KeepAlive(externrefs)
if err != nil {
return nil, err
}
if len(resultVals) == 0 {
return nil, nil
} else if len(resultVals) == 1 {
ret := takeVal(store, &resultVals[0]).Get()
return ret, nil
} else {
results := make([]Val, len(resultVals))
for i := 0; i < len(results); i++ {
results[i] = takeVal(store, &resultVals[i])
}
return results, nil
}
}
// Implementation of the `AsExtern` interface for `Func`
func (f *Func) AsExtern() C.wasmtime_extern_t {
ret := C.wasmtime_extern_t{kind: C.WASMTIME_EXTERN_FUNC}
C.go_wasmtime_extern_func_set(&ret, f.val)
return ret
}
// GetExport gets an exported item from the caller's module.
//
// May return `nil` if the export doesn't, if it's not a memory, if there isn't
// a caller, etc.
func (c *Caller) GetExport(name string) *Extern {
if c.ptr == nil {
return nil
}
var ret C.wasmtime_extern_t
ok := C.wasmtime_caller_export_get(
c.ptr,
C._GoStringPtr(name),
C._GoStringLen(name),
&ret,
)
runtime.KeepAlive(name)
runtime.KeepAlive(c)
if ok {
return mkExtern(&ret)
}
return nil
}
// Implementation of the `Storelike` interface for `Caller`.
func (c *Caller) Context() *C.wasmtime_context_t {
if c.ptr == nil {
panic("cannot use caller after host function returns")
}
return C.wasmtime_caller_context(c.ptr)
}
// Shim function that's expected to wrap any invocations of WebAssembly from Go
// itself.
//
// This is used to handle traps and error returns from any invocation of
// WebAssembly. This will also automatically propagate panics that happen within
// Go from one end back to this original invocation point.
//
// The `store` object is the context being used for the invocation, and `wasm`
// is the closure which will internally execute WebAssembly. A trap pointer is
// provided to the closure and it's expected that the closure returns an error.
func enterWasm(store Storelike, wasm func(**C.wasm_trap_t) *C.wasmtime_error_t) error {
// Load the internal `storeData` that our `store` references, which is
// used for handling panics which we are going to use here.
data := getDataInStore(store)
var trap *C.wasm_trap_t
err := wasm(&trap)
// Take ownership of any returned values to ensure we properly run
// destructors for them.
var wrappedTrap *Trap
var wrappedError error
if trap != nil {
wrappedTrap = mkTrap(trap)
}
if err != nil {
wrappedError = mkError(err)
}
// Check to see if wasm panicked, and if it did then we need to
// propagate that. Note that this happens after we take ownership of
// return values to ensure they're cleaned up properly.
if data.lastPanic != nil {
lastPanic := data.lastPanic
data.lastPanic = nil
panic(lastPanic)
}
// If there wasn't a panic then we determine whether to return the trap
// or the error.
if wrappedTrap != nil {
return wrappedTrap
}
return wrappedError
}
package wasmtime
// #include <wasm.h>
import "C"
import (
"runtime"
"unsafe"
)
// FuncType is one of function types which classify the signature of functions, mapping a vector of parameters to a vector of results.
// They are also used to classify the inputs and outputs of instructions.
type FuncType struct {
_ptr *C.wasm_functype_t
_owner interface{}
}
// NewFuncType creates a new `FuncType` with the `kind` provided
func NewFuncType(params, results []*ValType) *FuncType {
paramVec := mkValTypeList(params)
resultVec := mkValTypeList(results)
ptr := C.wasm_functype_new(¶mVec, &resultVec)
return mkFuncType(ptr, nil)
}
func mkValTypeList(tys []*ValType) C.wasm_valtype_vec_t {
vec := C.wasm_valtype_vec_t{}
C.wasm_valtype_vec_new_uninitialized(&vec, C.size_t(len(tys)))
base := unsafe.Pointer(vec.data)
for i, ty := range tys {
ptr := C.wasm_valtype_new(C.wasm_valtype_kind(ty.ptr()))
*(**C.wasm_valtype_t)(unsafe.Pointer(uintptr(base) + unsafe.Sizeof(ptr)*uintptr(i))) = ptr
}
runtime.KeepAlive(tys)
return vec
}
func mkFuncType(ptr *C.wasm_functype_t, owner interface{}) *FuncType {
functype := &FuncType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(functype, func(functype *FuncType) {
functype.Close()
})
}
return functype
}
func (ty *FuncType) ptr() *C.wasm_functype_t {
ret := ty._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (ty *FuncType) owner() interface{} {
if ty._owner != nil {
return ty._owner
}
return ty
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (ty *FuncType) Close() {
if ty._ptr == nil || ty._owner != nil {
return
}
runtime.SetFinalizer(ty, nil)
C.wasm_functype_delete(ty._ptr)
ty._ptr = nil
}
// Params returns the parameter types of this function type
func (ty *FuncType) Params() []*ValType {
ptr := C.wasm_functype_params(ty.ptr())
return ty.convertTypeList(ptr)
}
// Results returns the result types of this function type
func (ty *FuncType) Results() []*ValType {
ptr := C.wasm_functype_results(ty.ptr())
return ty.convertTypeList(ptr)
}
func (ty *FuncType) convertTypeList(list *C.wasm_valtype_vec_t) []*ValType {
ret := make([]*ValType, list.size)
base := unsafe.Pointer(list.data)
var ptr *C.wasm_valtype_t
for i := 0; i < int(list.size); i++ {
ptr := *(**C.wasm_valtype_t)(unsafe.Pointer(uintptr(base) + unsafe.Sizeof(ptr)*uintptr(i)))
ty := mkValType(ptr, ty.owner())
ret[i] = ty
}
return ret
}
// AsExternType converts this type to an instance of `ExternType`
func (ty *FuncType) AsExternType() *ExternType {
ptr := C.wasm_functype_as_externtype_const(ty.ptr())
return mkExternType(ptr, ty.owner())
}
package wasmtime
// #include "shims.h"
import "C"
import "runtime"
// Global is a global instance, which is the runtime representation of a global variable.
// It holds an individual value and a flag indicating whether it is mutable.
// Read more in [spec](https://webassembly.github.io/spec/core/exec/runtime.html#global-instances)
type Global struct {
val C.wasmtime_global_t
}
// NewGlobal creates a new `Global` in the given `Store` with the specified `ty` and
// initial value `val`.
func NewGlobal(
store Storelike,
ty *GlobalType,
val Val,
) (*Global, error) {
var ret C.wasmtime_global_t
var raw_val C.wasmtime_val_t
val.initialize(store, &raw_val)
err := C.wasmtime_global_new(
store.Context(),
ty.ptr(),
&raw_val,
&ret,
)
C.wasmtime_val_unroot(store.Context(), &raw_val)
runtime.KeepAlive(store)
runtime.KeepAlive(ty)
if err != nil {
return nil, mkError(err)
}
return mkGlobal(ret), nil
}
func mkGlobal(val C.wasmtime_global_t) *Global {
return &Global{val}
}
// Type returns the type of this global
func (g *Global) Type(store Storelike) *GlobalType {
ptr := C.wasmtime_global_type(store.Context(), &g.val)
runtime.KeepAlive(store)
return mkGlobalType(ptr, nil)
}
// Get gets the value of this global
func (g *Global) Get(store Storelike) Val {
ret := C.wasmtime_val_t{}
C.wasmtime_global_get(store.Context(), &g.val, &ret)
runtime.KeepAlive(store)
return takeVal(store, &ret)
}
// Set sets the value of this global
func (g *Global) Set(store Storelike, val Val) error {
var raw_val C.wasmtime_val_t
val.initialize(store, &raw_val)
err := C.wasmtime_global_set(store.Context(), &g.val, &raw_val)
C.wasmtime_val_unroot(store.Context(), &raw_val)
runtime.KeepAlive(store)
if err == nil {
return nil
}
return mkError(err)
}
func (g *Global) AsExtern() C.wasmtime_extern_t {
ret := C.wasmtime_extern_t{kind: C.WASMTIME_EXTERN_GLOBAL}
C.go_wasmtime_extern_global_set(&ret, g.val)
return ret
}
package wasmtime
// #include <wasm.h>
import "C"
import "runtime"
// GlobalType is a ValType, which classify global variables and hold a value and can either be mutable or immutable.
type GlobalType struct {
_ptr *C.wasm_globaltype_t
_owner interface{}
}
// NewGlobalType creates a new `GlobalType` with the `kind` provided and whether it's
// `mutable` or not
func NewGlobalType(content *ValType, mutable bool) *GlobalType {
mutability := C.WASM_CONST
if mutable {
mutability = C.WASM_VAR
}
contentPtr := C.wasm_valtype_new(C.wasm_valtype_kind(content.ptr()))
runtime.KeepAlive(content)
ptr := C.wasm_globaltype_new(contentPtr, C.wasm_mutability_t(mutability))
return mkGlobalType(ptr, nil)
}
func mkGlobalType(ptr *C.wasm_globaltype_t, owner interface{}) *GlobalType {
globaltype := &GlobalType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(globaltype, func(globaltype *GlobalType) {
globaltype.Close()
})
}
return globaltype
}
func (ty *GlobalType) ptr() *C.wasm_globaltype_t {
ret := ty._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (ty *GlobalType) owner() interface{} {
if ty._owner != nil {
return ty._owner
}
return ty
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (ty *GlobalType) Close() {
if ty._ptr == nil || ty._owner != nil {
return
}
runtime.SetFinalizer(ty, nil)
C.wasm_globaltype_delete(ty._ptr)
ty._ptr = nil
}
// Content returns the type of value stored in this global
func (ty *GlobalType) Content() *ValType {
ptr := C.wasm_globaltype_content(ty.ptr())
return mkValType(ptr, ty.owner())
}
// Mutable returns whether this global type is mutable or not
func (ty *GlobalType) Mutable() bool {
ret := C.wasm_globaltype_mutability(ty.ptr()) == C.WASM_VAR
runtime.KeepAlive(ty)
return ret
}
// AsExternType converts this type to an instance of `ExternType`
func (ty *GlobalType) AsExternType() *ExternType {
ptr := C.wasm_globaltype_as_externtype_const(ty.ptr())
return mkExternType(ptr, ty.owner())
}
package wasmtime
// #include <wasm.h>
import "C"
import "runtime"
// ImportType is one of the imports component
// A module defines a set of imports that are required for instantiation.
type ImportType struct {
_ptr *C.wasm_importtype_t
_owner interface{}
}
// NewImportType creates a new `ImportType` with the given `module` and `name` and the type
// provided.
func NewImportType(module, name string, ty AsExternType) *ImportType {
moduleVec := stringToByteVec(module)
nameVec := stringToByteVec(name)
// Creating an import type requires taking ownership, so create a copy
// so we don't have to invalidate pointers here. Shouldn't be too
// costly in theory anyway.
extern := ty.AsExternType()
ptr := C.wasm_externtype_copy(extern.ptr())
runtime.KeepAlive(extern)
// And once we've got all that create the import type!
importPtr := C.wasm_importtype_new(&moduleVec, &nameVec, ptr)
return mkImportType(importPtr, nil)
}
func mkImportType(ptr *C.wasm_importtype_t, owner interface{}) *ImportType {
importtype := &ImportType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(importtype, func(importtype *ImportType) {
importtype.Close()
})
}
return importtype
}
func (ty *ImportType) ptr() *C.wasm_importtype_t {
ret := ty._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (ty *ImportType) owner() interface{} {
if ty._owner != nil {
return ty._owner
}
return ty
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (ty *ImportType) Close() {
if ty._ptr == nil || ty._owner != nil {
return
}
runtime.SetFinalizer(ty, nil)
C.wasm_importtype_delete(ty._ptr)
ty._ptr = nil
}
// Module returns the name in the module this import type is importing
func (ty *ImportType) Module() string {
ptr := C.wasm_importtype_module(ty.ptr())
ret := C.GoStringN(ptr.data, C.int(ptr.size))
runtime.KeepAlive(ty)
return ret
}
// Name returns the name in the module this import type is importing.
//
// Note that the returned string may be `nil` with the module linking proposal
// where this field is optional in the import type.
func (ty *ImportType) Name() *string {
ptr := C.wasm_importtype_name(ty.ptr())
if ptr == nil {
return nil
}
ret := C.GoStringN(ptr.data, C.int(ptr.size))
runtime.KeepAlive(ty)
return &ret
}
// Type returns the type of item this import type expects
func (ty *ImportType) Type() *ExternType {
ptr := C.wasm_importtype_type(ty.ptr())
return mkExternType(ptr, ty.owner())
}
package wasmtime
// #include "shims.h"
import "C"
import (
"runtime"
"unsafe"
)
// Instance is an instantiated module instance.
// Once a module has been instantiated as an Instance, any exported function can be invoked externally via its function address funcaddr in the store S and an appropriate list val∗ of argument values.
type Instance struct {
val C.wasmtime_instance_t
}
// NewInstance instantiates a WebAssembly `module` with the `imports` provided.
//
// This function will attempt to create a new wasm instance given the provided
// imports. This can fail if the wrong number of imports are specified, the
// imports aren't of the right type, or for other resource-related issues.
//
// This will also run the `start` function of the instance, returning an error
// if it traps.
func NewInstance(store Storelike, module *Module, imports []AsExtern) (*Instance, error) {
importsRaw := make([]C.wasmtime_extern_t, len(imports))
for i, imp := range imports {
importsRaw[i] = imp.AsExtern()
}
var val C.wasmtime_instance_t
err := enterWasm(store, func(trap **C.wasm_trap_t) *C.wasmtime_error_t {
var imports *C.wasmtime_extern_t
if len(importsRaw) > 0 {
imports = (*C.wasmtime_extern_t)(unsafe.Pointer(&importsRaw[0]))
}
return C.wasmtime_instance_new(
store.Context(),
module.ptr(),
imports,
C.size_t(len(importsRaw)),
&val,
trap,
)
})
runtime.KeepAlive(store)
runtime.KeepAlive(module)
runtime.KeepAlive(imports)
runtime.KeepAlive(importsRaw)
if err != nil {
return nil, err
}
return mkInstance(val), nil
}
func mkInstance(val C.wasmtime_instance_t) *Instance {
return &Instance{val}
}
type externList struct {
vec C.wasm_extern_vec_t
}
// Exports returns a list of exports from this instance.
//
// Each export is returned as a `*Extern` and lines up with the exports list of
// the associated `Module`.
func (instance *Instance) Exports(store Storelike) []*Extern {
ret := make([]*Extern, 0)
var name *C.char
var name_len C.size_t
for i := 0; ; i++ {
var item C.wasmtime_extern_t
ok := C.wasmtime_instance_export_nth(
store.Context(),
&instance.val,
C.size_t(i),
&name,
&name_len,
&item,
)
if !ok {
break
}
ret = append(ret, mkExtern(&item))
}
runtime.KeepAlive(store)
return ret
}
// GetExport attempts to find an export on this instance by `name`
//
// May return `nil` if this instance has no export named `name`
func (i *Instance) GetExport(store Storelike, name string) *Extern {
var item C.wasmtime_extern_t
ok := C.wasmtime_instance_export_get(
store.Context(),
&i.val,
C._GoStringPtr(name),
C._GoStringLen(name),
&item,
)
runtime.KeepAlive(store)
runtime.KeepAlive(name)
if ok {
return mkExtern(&item)
}
return nil
}
// GetFunc attempts to find a function on this instance by `name`.
//
// May return `nil` if this instance has no function named `name`,
// it is not a function, etc.
func (i *Instance) GetFunc(store Storelike, name string) *Func {
f := i.GetExport(store, name)
if f == nil {
return nil
}
return f.Func()
}
package wasmtime
// #include <wasmtime.h>
// #include "shims.h"
import "C"
import (
"reflect"
"runtime"
)
// Linker implements a wasmtime Linking module, which can link instantiated modules together.
// More details you can see [examples for C](https://bytecodealliance.github.io/wasmtime/examples-c-linking.html) or
// [examples for Rust](https://bytecodealliance.github.io/wasmtime/examples-rust-linking.html)
type Linker struct {
_ptr *C.wasmtime_linker_t
Engine *Engine
}
func NewLinker(engine *Engine) *Linker {
ptr := C.wasmtime_linker_new(engine.ptr())
linker := &Linker{_ptr: ptr, Engine: engine}
runtime.SetFinalizer(linker, func(linker *Linker) {
linker.Close()
})
return linker
}
func (l *Linker) ptr() *C.wasmtime_linker_t {
ret := l._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
// Close will deallocate this linker's state explicitly.
//
// For more information see the documentation for engine.Close()
func (l *Linker) Close() {
if l._ptr == nil {
return
}
runtime.SetFinalizer(l, nil)
C.wasmtime_linker_delete(l._ptr)
l._ptr = nil
}
// AllowShadowing configures whether names can be redefined after they've already been defined
// in this linker.
func (l *Linker) AllowShadowing(allow bool) {
C.wasmtime_linker_allow_shadowing(l.ptr(), C.bool(allow))
runtime.KeepAlive(l)
}
// Define defines a new item in this linker with the given module/name pair. Returns
// an error if shadowing is disallowed and the module/name is already defined.
func (l *Linker) Define(store Storelike, module, name string, item AsExtern) error {
extern := item.AsExtern()
err := C.wasmtime_linker_define(
l.ptr(),
store.Context(),
C._GoStringPtr(module),
C._GoStringLen(module),
C._GoStringPtr(name),
C._GoStringLen(name),
&extern,
)
runtime.KeepAlive(l)
runtime.KeepAlive(module)
runtime.KeepAlive(name)
runtime.KeepAlive(item)
runtime.KeepAlive(store)
if err == nil {
return nil
}
return mkError(err)
}
// DefineFunc acts as a convenience wrapper to calling Define and WrapFunc.
//
// Returns an error if shadowing is disabled and the name is already defined.
func (l *Linker) DefineFunc(store Storelike, module, name string, f interface{}) error {
return l.Define(store, module, name, WrapFunc(store, f))
}
// FuncNew defines a function in this linker in the same style as `NewFunc`
//
// Note that this function does not require a `Storelike`, which is
// intentional. This function can be used to insert store-independent functions
// into this linker which allows this linker to be used for instantiating
// modules in multiple different stores.
//
// Returns an error if shadowing is disabled and the name is already defined.
func (l *Linker) FuncNew(module, name string, ty *FuncType, f func(*Caller, []Val) ([]Val, *Trap)) error {
idx := insertFuncNew(nil, ty, f)
err := C.go_linker_define_func(
l.ptr(),
C._GoStringPtr(module),
C._GoStringLen(module),
C._GoStringPtr(name),
C._GoStringLen(name),
ty.ptr(),
0, // this is "new"
C.size_t(idx),
)
runtime.KeepAlive(l)
runtime.KeepAlive(module)
runtime.KeepAlive(name)
runtime.KeepAlive(ty)
if err == nil {
return nil
}
return mkError(err)
}
// FuncWrap defines a function in this linker in the same style as `WrapFunc`
//
// Note that this function does not require a `Storelike`, which is
// intentional. This function can be used to insert store-independent functions
// into this linker which allows this linker to be used for instantiating
// modules in multiple different stores.
//
// Returns an error if shadowing is disabled and the name is already defined.
func (l *Linker) FuncWrap(module, name string, f interface{}) error {
val := reflect.ValueOf(f)
ty := inferFuncType(val)
idx := insertFuncWrap(nil, val)
err := C.go_linker_define_func(
l.ptr(),
C._GoStringPtr(module),
C._GoStringLen(module),
C._GoStringPtr(name),
C._GoStringLen(name),
ty.ptr(),
1, // this is "wrap"
C.size_t(idx),
)
runtime.KeepAlive(l)
runtime.KeepAlive(module)
runtime.KeepAlive(name)
runtime.KeepAlive(ty)
if err == nil {
return nil
}
return mkError(err)
}
// DefineInstance defines all exports of an instance provided under the module name provided.
//
// Returns an error if shadowing is disabled and names are already defined.
func (l *Linker) DefineInstance(store Storelike, module string, instance *Instance) error {
err := C.wasmtime_linker_define_instance(
l.ptr(),
store.Context(),
C._GoStringPtr(module),
C._GoStringLen(module),
&instance.val,
)
runtime.KeepAlive(l)
runtime.KeepAlive(module)
runtime.KeepAlive(store)
if err == nil {
return nil
}
return mkError(err)
}
// DefineModule defines automatic instantiations of the module in this linker.
//
// The `name` of the module is the name within the linker, and the `module` is
// the one that's being instantiated. This function automatically handles
// WASI Commands and Reactors for instantiation and initialization. For more
// information see the Rust documentation --
// https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.module.
func (l *Linker) DefineModule(store Storelike, name string, module *Module) error {
err := C.wasmtime_linker_module(
l.ptr(),
store.Context(),
C._GoStringPtr(name),
C._GoStringLen(name),
module.ptr(),
)
runtime.KeepAlive(l)
runtime.KeepAlive(name)
runtime.KeepAlive(module)
runtime.KeepAlive(store)
if err == nil {
return nil
}
return mkError(err)
}
// DefineWasi links a WASI module into this linker, ensuring that all exported functions
// are available for linking.
//
// Returns an error if shadowing is disabled and names are already defined.
func (l *Linker) DefineWasi() error {
err := C.wasmtime_linker_define_wasi(l.ptr())
runtime.KeepAlive(l)
if err == nil {
return nil
}
return mkError(err)
}
// Instantiate instantiates a module with all imports defined in this linker.
//
// Returns an error if the instance's imports couldn't be satisfied, had the
// wrong types, or if a trap happened executing the start function.
func (l *Linker) Instantiate(store Storelike, module *Module) (*Instance, error) {
var ret C.wasmtime_instance_t
err := enterWasm(store, func(trap **C.wasm_trap_t) *C.wasmtime_error_t {
return C.wasmtime_linker_instantiate(l.ptr(), store.Context(), module.ptr(), &ret, trap)
})
runtime.KeepAlive(l)
runtime.KeepAlive(module)
runtime.KeepAlive(store)
if err != nil {
return nil, err
}
return mkInstance(ret), nil
}
// GetDefault acquires the "default export" of the named module in this linker.
//
// If there is no default item then an error is returned, otherwise the default
// function is returned.
//
// For more information see the Rust documentation --
// https://docs.wasmtime.dev/api/wasmtime/struct.Linker.html#method.get_default.
func (l *Linker) GetDefault(store Storelike, name string) (*Func, error) {
var ret C.wasmtime_func_t
err := C.wasmtime_linker_get_default(
l.ptr(),
store.Context(),
C._GoStringPtr(name),
C._GoStringLen(name),
&ret,
)
runtime.KeepAlive(l)
runtime.KeepAlive(name)
runtime.KeepAlive(store)
if err != nil {
return nil, mkError(err)
}
return mkFunc(ret), nil
}
// GetOneByName loads an item by name from this linker.
//
// If the item isn't defined then nil is returned, otherwise the item is
// returned.
func (l *Linker) Get(store Storelike, module, name string) *Extern {
var ret C.wasmtime_extern_t
ok := C.wasmtime_linker_get(
l.ptr(),
store.Context(),
C._GoStringPtr(module),
C._GoStringLen(module),
C._GoStringPtr(name),
C._GoStringLen(name),
&ret,
)
runtime.KeepAlive(l)
runtime.KeepAlive(name)
runtime.KeepAlive(module)
runtime.KeepAlive(store)
if ok {
return mkExtern(&ret)
}
return nil
}
//go:build !debug
// +build !debug
package wasmtime
// See `ffi.go` documentation about `ptr()` for what's going on here.
func maybeGC() {
}
package wasmtime
// #include "shims.h"
import "C"
import (
"runtime"
"unsafe"
)
// Memory instance is the runtime representation of a linear memory.
// It holds a vector of bytes and an optional maximum size, if one was specified at the definition site of the memory.
// Read more in [spec](https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances)
// In wasmtime-go, you can get the vector of bytes by the unsafe pointer of memory from `Memory.Data()`, or go style byte slice from `Memory.UnsafeData()`
type Memory struct {
val C.wasmtime_memory_t
}
// NewMemory creates a new `Memory` in the given `Store` with the specified `ty`.
func NewMemory(store Storelike, ty *MemoryType) (*Memory, error) {
var ret C.wasmtime_memory_t
err := C.wasmtime_memory_new(store.Context(), ty.ptr(), &ret)
runtime.KeepAlive(store)
runtime.KeepAlive(ty)
if err != nil {
return nil, mkError(err)
}
return mkMemory(ret), nil
}
func mkMemory(val C.wasmtime_memory_t) *Memory {
return &Memory{val}
}
// Type returns the type of this memory
func (mem *Memory) Type(store Storelike) *MemoryType {
ptr := C.wasmtime_memory_type(store.Context(), &mem.val)
runtime.KeepAlive(store)
return mkMemoryType(ptr, nil)
}
// Data returns the raw pointer in memory of where this memory starts
func (mem *Memory) Data(store Storelike) unsafe.Pointer {
ret := unsafe.Pointer(C.wasmtime_memory_data(store.Context(), &mem.val))
runtime.KeepAlive(store)
return ret
}
// UnsafeData returns the raw memory backed by this `Memory` as a byte slice (`[]byte`).
//
// This is not a safe method to call, hence the "unsafe" in the name. The byte
// slice returned from this function is not managed by the Go garbage collector.
// You need to ensure that `m`, the original `Memory`, lives longer than the
// `[]byte` returned.
//
// Note that you may need to use `runtime.KeepAlive` to keep the original memory
// `m` alive for long enough while you're using the `[]byte` slice. If the
// `[]byte` slice is used after `m` is GC'd then that is undefined behavior.
func (mem *Memory) UnsafeData(store Storelike) []byte {
length := mem.DataSize(store)
return unsafe.Slice((*byte)(mem.Data(store)), length)
}
// DataSize returns the size, in bytes, that `Data()` is valid for
func (mem *Memory) DataSize(store Storelike) uintptr {
ret := uintptr(C.wasmtime_memory_data_size(store.Context(), &mem.val))
runtime.KeepAlive(store)
return ret
}
// Size returns the size, in wasm pages, of this memory
func (mem *Memory) Size(store Storelike) uint64 {
ret := uint64(C.wasmtime_memory_size(store.Context(), &mem.val))
runtime.KeepAlive(store)
return ret
}
// Grow grows this memory by `delta` pages
func (mem *Memory) Grow(store Storelike, delta uint64) (uint64, error) {
prev := C.uint64_t(0)
err := C.wasmtime_memory_grow(store.Context(), &mem.val, C.uint64_t(delta), &prev)
runtime.KeepAlive(store)
if err != nil {
return 0, mkError(err)
}
return uint64(prev), nil
}
func (mem *Memory) AsExtern() C.wasmtime_extern_t {
ret := C.wasmtime_extern_t{kind: C.WASMTIME_EXTERN_MEMORY}
C.go_wasmtime_extern_memory_set(&ret, mem.val)
return ret
}
package wasmtime
// #include <wasmtime.h>
import "C"
import "runtime"
// MemoryType is one of Memory types which classify linear memories and their size range.
// The limits constrain the minimum and optionally the maximum size of a memory. The limits are given in units of page size.
type MemoryType struct {
_ptr *C.wasm_memorytype_t
_owner interface{}
}
// NewMemoryType creates a new `MemoryType` with the limits on size provided
//
// The `min` value is the minimum size, in WebAssembly pages, of this memory.
// The `has_max` boolean indicates whether a maximum size is present, and if so
// `max` is used as the maximum size of memory, in wasm pages.
//
// Note that this will create a 32-bit memory type, the default outside of the
// memory64 proposal.
func NewMemoryType(min uint32, has_max bool, max uint32, shared bool) *MemoryType {
if min > (1<<16) || max > (1<<16) {
panic("provided sizes are too large")
}
ptr := C.wasmtime_memorytype_new(C.uint64_t(min), C._Bool(has_max), C.uint64_t(max), false, C._Bool(shared))
return mkMemoryType(ptr, nil)
}
// NewMemoryType64 creates a new 64-bit `MemoryType` with the provided limits
//
// The `min` value is the minimum size, in WebAssembly pages, of this memory.
// The `has_max` boolean indicates whether a maximum size is present, and if so
// `max` is used as the maximum size of memory, in wasm pages.
//
// Note that 64-bit memories are part of the memory64 WebAssembly proposal.
func NewMemoryType64(min uint64, has_max bool, max uint64, shared bool) *MemoryType {
if min > (1<<48) || max > (1<<48) {
panic("provided sizes are too large")
}
ptr := C.wasmtime_memorytype_new(C.uint64_t(min), C._Bool(has_max), C.uint64_t(max), true, C._Bool(shared))
return mkMemoryType(ptr, nil)
}
func mkMemoryType(ptr *C.wasm_memorytype_t, owner interface{}) *MemoryType {
memorytype := &MemoryType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(memorytype, func(memorytype *MemoryType) {
memorytype.Close()
})
}
return memorytype
}
func (ty *MemoryType) ptr() *C.wasm_memorytype_t {
ret := ty._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (ty *MemoryType) owner() interface{} {
if ty._owner != nil {
return ty._owner
}
return ty
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (ty *MemoryType) Close() {
if ty._ptr == nil || ty._owner != nil {
return
}
runtime.SetFinalizer(ty, nil)
C.wasm_memorytype_delete(ty._ptr)
ty._ptr = nil
}
// Minimum returns the minimum size of this memory, in WebAssembly pages
func (ty *MemoryType) Minimum() uint64 {
ret := C.wasmtime_memorytype_minimum(ty.ptr())
runtime.KeepAlive(ty)
return uint64(ret)
}
// Maximum returns the maximum size of this memory, in WebAssembly pages, if
// specified.
//
// If the maximum size is not specified then `(false, 0)` is returned, otherwise
// `(true, N)` is returned where `N` is the listed maximum size of this memory.
func (ty *MemoryType) Maximum() (bool, uint64) {
size := C.uint64_t(0)
present := C.wasmtime_memorytype_maximum(ty.ptr(), &size)
runtime.KeepAlive(ty)
return bool(present), uint64(size)
}
// Is64 returns whether this is a 64-bit memory or not.
func (ty *MemoryType) Is64() bool {
ok := C.wasmtime_memorytype_is64(ty.ptr())
runtime.KeepAlive(ty)
return bool(ok)
}
// IsShared returns whether this is a shared memory or not.
func (ty *MemoryType) IsShared() bool {
ok := C.wasmtime_memorytype_isshared(ty.ptr())
runtime.KeepAlive(ty)
return bool(ok)
}
// AsExternType converts this type to an instance of `ExternType`
func (ty *MemoryType) AsExternType() *ExternType {
ptr := C.wasm_memorytype_as_externtype_const(ty.ptr())
return mkExternType(ptr, ty.owner())
}
package wasmtime
// #include "shims.h"
// #include <stdlib.h>
import "C"
import (
"os"
"runtime"
"unsafe"
)
// Module is a module which collects definitions for types, functions, tables, memories, and globals.
// In addition, it can declare imports and exports and provide initialization logic in the form of data and element segments or a start function.
// Modules organized WebAssembly programs as the unit of deployment, loading, and compilation.
type Module struct {
_ptr *C.wasmtime_module_t
}
// NewModule compiles a new `Module` from the `wasm` provided with the given configuration
// in `engine`.
func NewModule(engine *Engine, wasm []byte) (*Module, error) {
// We can't create the `wasm_byte_vec_t` here and pass it in because
// that runs into the error of "passed a pointer to a pointer" because
// the vec itself is passed by pointer and it contains a pointer to
// `wasm`. To work around this we insert some C shims above and call
// them.
var wasmPtr *C.uint8_t
if len(wasm) > 0 {
wasmPtr = (*C.uint8_t)(unsafe.Pointer(&wasm[0]))
}
var ptr *C.wasmtime_module_t
err := C.wasmtime_module_new(engine.ptr(), wasmPtr, C.size_t(len(wasm)), &ptr)
runtime.KeepAlive(engine)
runtime.KeepAlive(wasm)
if err != nil {
return nil, mkError(err)
}
return mkModule(ptr), nil
}
// NewModuleFromFile reads the contents of the `file` provided and interprets them as either the
// text format or the binary format for WebAssembly.
//
// Afterwards delegates to the `NewModule` constructor with the contents read.
func NewModuleFromFile(engine *Engine, file string) (*Module, error) {
wasm, err := os.ReadFile(file)
if err != nil {
return nil, err
}
// If this wasm isn't actually wasm, treat it as the text format and
// parse it as such.
if len(wasm) > 0 && wasm[0] != 0 {
wasm, err = Wat2Wasm(string(wasm))
if err != nil {
return nil, err
}
}
return NewModule(engine, wasm)
}
// ModuleValidate validates whether `wasm` would be a valid wasm module according to the
// configuration in `store`
func ModuleValidate(engine *Engine, wasm []byte) error {
var wasmPtr *C.uint8_t
if len(wasm) > 0 {
wasmPtr = (*C.uint8_t)(unsafe.Pointer(&wasm[0]))
}
err := C.wasmtime_module_validate(engine.ptr(), wasmPtr, C.size_t(len(wasm)))
runtime.KeepAlive(engine)
runtime.KeepAlive(wasm)
if err == nil {
return nil
}
return mkError(err)
}
func mkModule(ptr *C.wasmtime_module_t) *Module {
module := &Module{_ptr: ptr}
runtime.SetFinalizer(module, func(module *Module) {
module.Close()
})
return module
}
func (m *Module) ptr() *C.wasmtime_module_t {
ret := m._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
// Close will deallocate this module's state explicitly.
//
// For more information see the documentation for engine.Close()
func (m *Module) Close() {
if m._ptr == nil {
return
}
runtime.SetFinalizer(m, nil)
C.wasmtime_module_delete(m._ptr)
m._ptr = nil
}
// Imports returns a list of `ImportType` which are the items imported by
// this module and are required for instantiation
func (m *Module) Imports() []*ImportType {
imports := &importTypeList{}
C.wasmtime_module_imports(m.ptr(), &imports.vec)
runtime.KeepAlive(m)
return imports.mkGoList()
}
// Exports returns a list of `ExportType` which are the items that will be
// exported by this module after instantiation.
func (m *Module) Exports() []*ExportType {
exports := &exportTypeList{}
C.wasmtime_module_exports(m.ptr(), &exports.vec)
runtime.KeepAlive(m)
return exports.mkGoList()
}
type importTypeList struct {
vec C.wasm_importtype_vec_t
}
func (list *importTypeList) mkGoList() []*ImportType {
runtime.SetFinalizer(list, func(imports *importTypeList) {
C.wasm_importtype_vec_delete(&imports.vec)
})
ret := make([]*ImportType, int(list.vec.size))
base := unsafe.Pointer(list.vec.data)
var ptr *C.wasm_importtype_t
for i := 0; i < int(list.vec.size); i++ {
ptr := *(**C.wasm_importtype_t)(unsafe.Pointer(uintptr(base) + unsafe.Sizeof(ptr)*uintptr(i)))
ty := mkImportType(ptr, list)
ret[i] = ty
}
return ret
}
type exportTypeList struct {
vec C.wasm_exporttype_vec_t
}
func (list *exportTypeList) mkGoList() []*ExportType {
runtime.SetFinalizer(list, func(exports *exportTypeList) {
C.wasm_exporttype_vec_delete(&exports.vec)
})
ret := make([]*ExportType, int(list.vec.size))
base := unsafe.Pointer(list.vec.data)
var ptr *C.wasm_exporttype_t
for i := 0; i < int(list.vec.size); i++ {
ptr := *(**C.wasm_exporttype_t)(unsafe.Pointer(uintptr(base) + unsafe.Sizeof(ptr)*uintptr(i)))
ty := mkExportType(ptr, list)
ret[i] = ty
}
return ret
}
// NewModuleDeserialize decodes and deserializes in-memory bytes previously
// produced by `module.Serialize()`.
//
// This function does not take a WebAssembly binary as input. It takes
// as input the results of a previous call to `Serialize()`, and only takes
// that as input.
//
// If deserialization is successful then a compiled module is returned,
// otherwise nil and an error are returned.
//
// Note that to deserialize successfully the bytes provided must have been
// produced with an `Engine` that has the same compilation options as the
// provided engine, and from the same version of this library.
func NewModuleDeserialize(engine *Engine, encoded []byte) (*Module, error) {
var encodedPtr *C.uint8_t
var ptr *C.wasmtime_module_t
if len(encoded) > 0 {
encodedPtr = (*C.uint8_t)(unsafe.Pointer(&encoded[0]))
}
err := C.wasmtime_module_deserialize(
engine.ptr(),
encodedPtr,
C.size_t(len(encoded)),
&ptr,
)
runtime.KeepAlive(engine)
runtime.KeepAlive(encoded)
if err != nil {
return nil, mkError(err)
}
return mkModule(ptr), nil
}
// NewModuleDeserializeFile is the same as `NewModuleDeserialize` except that
// the bytes are read from a file instead of provided as an argument.
func NewModuleDeserializeFile(engine *Engine, path string) (*Module, error) {
cs := C.CString(path)
var ptr *C.wasmtime_module_t
err := C.wasmtime_module_deserialize_file(engine.ptr(), cs, &ptr)
runtime.KeepAlive(engine)
C.free(unsafe.Pointer(cs))
if err != nil {
return nil, mkError(err)
}
return mkModule(ptr), nil
}
// Serialize will convert this in-memory compiled module into a list of bytes.
//
// The purpose of this method is to extract an artifact which can be stored
// elsewhere from this `Module`. The returned bytes can, for example, be stored
// on disk or in an object store. The `NewModuleDeserialize` function can be
// used to deserialize the returned bytes at a later date to get the module
// back.
func (m *Module) Serialize() ([]byte, error) {
retVec := C.wasm_byte_vec_t{}
err := C.wasmtime_module_serialize(m.ptr(), &retVec)
runtime.KeepAlive(m)
if err != nil {
return nil, mkError(err)
}
ret := C.GoBytes(unsafe.Pointer(retVec.data), C.int(retVec.size))
C.wasm_byte_vec_delete(&retVec)
return ret, nil
}
package wasmtime
type slab struct {
list []int
next int
}
func (s *slab) allocate() int {
if s.next == len(s.list) {
s.list = append(s.list, s.next+1)
}
ret := s.next
s.next = s.list[ret]
return ret
}
func (s *slab) deallocate(slot int) {
s.list[slot] = s.next
s.next = slot
}
package wasmtime
// #include <wasmtime.h>
// #include "shims.h"
import "C"
import (
"reflect"
"runtime"
"sync"
"unsafe"
)
// Store is a general group of wasm instances, and many objects
// must all be created with and reference the same `Store`
type Store struct {
_ptr *C.wasmtime_store_t
// The `Engine` that this store uses for compilation and environment
// settings.
Engine *Engine
}
// Storelike represents types that can be used to contextually reference a
// `Store`.
//
// This interface is implemented by `*Store` and `*Caller` and is pervasively
// used throughout this library. You'll want to pass one of those two objects
// into functions that take a `Storelike`.
type Storelike interface {
// Returns the wasmtime context pointer this store is attached to.
Context() *C.wasmtime_context_t
}
var gStoreLock sync.Mutex
var gStoreMap = make(map[int]*storeData)
var gStoreSlab slab
// State associated with a `Store`, currently used to propagate panic
// information through invocations as well as store Go closures that have been
// added to the store.
type storeData struct {
engine *Engine
funcNew []funcNewEntry
funcWrap []funcWrapEntry
lastPanic interface{}
}
type funcNewEntry struct {
callback func(*Caller, []Val) ([]Val, *Trap)
results []*ValType
}
type funcWrapEntry struct {
callback reflect.Value
}
// NewStore creates a new `Store` from the configuration provided in `engine`
func NewStore(engine *Engine) *Store {
// Allocate an index for this store and allocate some internal data to go with
// the store.
gStoreLock.Lock()
idx := gStoreSlab.allocate()
gStoreMap[idx] = &storeData{engine: engine}
gStoreLock.Unlock()
ptr := C.go_store_new(engine.ptr(), C.size_t(idx))
store := &Store{
_ptr: ptr,
Engine: engine,
}
runtime.SetFinalizer(store, func(store *Store) {
store.Close()
})
return store
}
//export goFinalizeStore
func goFinalizeStore(env unsafe.Pointer) {
// When a store is finalized this is used as the finalization callback for the
// custom data within the store, and our finalization here will delete the
// store's data from the global map and deallocate its index to get reused by
// a future store.
idx := int(uintptr(env))
gStoreLock.Lock()
defer gStoreLock.Unlock()
delete(gStoreMap, idx)
gStoreSlab.deallocate(idx)
}
func (store *Store) ptr() *C.wasmtime_store_t {
ret := store._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
// Close will deallocate this store's state explicitly.
//
// For more information see the documentation for engine.Close()
func (store *Store) Close() {
if store._ptr == nil {
return
}
runtime.SetFinalizer(store, nil)
C.wasmtime_store_delete(store._ptr)
store._ptr = nil
}
// GC will clean up any `externref` values that are no longer actually
// referenced.
//
// This function is not required to be called for correctness, it's only an
// optimization if desired to clean out any extra `externref` values.
func (store *Store) GC() {
C.wasmtime_context_gc(store.Context())
runtime.KeepAlive(store)
}
// SetWasi will configure the WASI state to use for instances within this
// `Store`.
//
// The `wasi` argument cannot be reused for another `Store`, it's consumed by
// this function.
func (store *Store) SetWasi(wasi *WasiConfig) {
runtime.SetFinalizer(wasi, nil)
ptr := wasi.ptr()
wasi._ptr = nil
C.wasmtime_context_set_wasi(store.Context(), ptr)
runtime.KeepAlive(store)
}
// Implementation of the `Storelike` interface
func (store *Store) Context() *C.wasmtime_context_t {
ret := C.wasmtime_store_context(store.ptr())
maybeGC()
runtime.KeepAlive(store)
return ret
}
// SetEpochDeadline will configure the relative deadline, from the current
// engine's epoch number, after which wasm code will be interrupted.
func (store *Store) SetEpochDeadline(deadline uint64) {
C.wasmtime_context_set_epoch_deadline(store.Context(), C.uint64_t(deadline))
runtime.KeepAlive(store)
}
// Returns the underlying `*storeData` that this store references in Go, used
// for inserting functions or storing panic data.
func getDataInStore(store Storelike) *storeData {
data := uintptr(C.wasmtime_context_get_data(store.Context()))
gStoreLock.Lock()
defer gStoreLock.Unlock()
return gStoreMap[int(data)]
}
var gEngineFuncLock sync.Mutex
var gEngineFuncNew = make(map[int]*funcNewEntry)
var gEngineFuncNewSlab slab
var gEngineFuncWrap = make(map[int]*funcWrapEntry)
var gEngineFuncWrapSlab slab
func insertFuncNew(data *storeData, ty *FuncType, callback func(*Caller, []Val) ([]Val, *Trap)) int {
var idx int
entry := funcNewEntry{
callback: callback,
results: ty.Results(),
}
if data == nil {
gEngineFuncLock.Lock()
defer gEngineFuncLock.Unlock()
idx = gEngineFuncNewSlab.allocate()
gEngineFuncNew[idx] = &entry
idx = (idx << 1)
} else {
idx = len(data.funcNew)
data.funcNew = append(data.funcNew, entry)
idx = (idx << 1) | 1
}
return idx
}
func (data *storeData) getFuncNew(idx int) *funcNewEntry {
if idx&1 == 0 {
gEngineFuncLock.Lock()
defer gEngineFuncLock.Unlock()
return gEngineFuncNew[idx>>1]
} else {
return &data.funcNew[idx>>1]
}
}
func insertFuncWrap(data *storeData, callback reflect.Value) int {
var idx int
entry := funcWrapEntry{callback}
if data == nil {
gEngineFuncLock.Lock()
defer gEngineFuncLock.Unlock()
idx = gEngineFuncWrapSlab.allocate()
gEngineFuncWrap[idx] = &entry
idx = (idx << 1)
} else {
idx = len(data.funcWrap)
data.funcWrap = append(data.funcWrap, entry)
idx = (idx << 1) | 1
}
return idx
}
func (data *storeData) getFuncWrap(idx int) *funcWrapEntry {
if idx&1 == 0 {
gEngineFuncLock.Lock()
defer gEngineFuncLock.Unlock()
return gEngineFuncWrap[idx>>1]
} else {
return &data.funcWrap[idx>>1]
}
}
//export goFinalizeFuncNew
func goFinalizeFuncNew(env unsafe.Pointer) {
idx := int(uintptr(env))
if idx&1 != 0 {
panic("shouldn't finalize a store-local index")
}
idx = idx >> 1
gEngineFuncLock.Lock()
defer gEngineFuncLock.Unlock()
delete(gEngineFuncNew, idx)
gEngineFuncNewSlab.deallocate(idx)
}
//export goFinalizeFuncWrap
func goFinalizeFuncWrap(env unsafe.Pointer) {
idx := int(uintptr(env))
if idx&1 != 0 {
panic("shouldn't finalize a store-local index")
}
idx = idx >> 1
gEngineFuncLock.Lock()
defer gEngineFuncLock.Unlock()
delete(gEngineFuncWrap, idx)
gEngineFuncWrapSlab.deallocate(idx)
}
// GetFuel returns the amount of fuel remaining in this store.
//
// If fuel consumption is not enabled via `Config.SetConsumeFuel` then
// this function will return an error. Otherwise this will retrieve the fuel
// remaining and return it.
//
// Also note that fuel, if enabled, must be originally configured via
// `Store.SetFuel`.
func (store *Store) GetFuel() (uint64, error) {
var remaining uint64
c_remaining := C.uint64_t(remaining)
err := C.wasmtime_context_get_fuel(store.Context(), &c_remaining)
runtime.KeepAlive(store)
if err != nil {
return 0, mkError(err)
}
return uint64(c_remaining), nil
}
// SetFuel sets this store's fuel to the specified value.
//
// For this method to work fuel consumption must be enabled via
// `Config.SetConsumeFuel`. By default a store starts with 0 fuel
// for wasm to execute with (meaning it will immediately trap).
// This function must be called for the store to have
// some fuel to allow WebAssembly to execute.
//
// Note that at this time when fuel is entirely consumed it will cause
// wasm to trap. More usages of fuel are planned for the future.
//
// If fuel is not enabled within this store then an error is returned.
func (store *Store) SetFuel(fuel uint64) error {
err := C.wasmtime_context_set_fuel(store.Context(), C.uint64_t(fuel))
runtime.KeepAlive(store)
if err != nil {
return mkError(err)
}
return nil
}
// Limiter provides limits for a store. Used by hosts to limit resource
// consumption of instances. Use negative value to keep the default value
// for the limit.
func (store *Store) Limiter(
memorySize int64,
tableElements int64,
instances int64,
tables int64,
memories int64,
) {
C.wasmtime_store_limiter(
store.ptr(),
C.int64_t(memorySize),
C.int64_t(tableElements),
C.int64_t(instances),
C.int64_t(tables),
C.int64_t(memories),
)
runtime.KeepAlive(store)
}
package wasmtime
// #include "shims.h"
import "C"
import (
"errors"
"runtime"
)
// Table is a table instance, which is the runtime representation of a table.
//
// It holds a vector of reference types and an optional maximum size, if one was
// specified in the table type at the table’s definition site.
// Read more in [spec](https://webassembly.github.io/spec/core/exec/runtime.html#table-instances)
type Table struct {
val C.wasmtime_table_t
}
// NewTable creates a new `Table` in the given `Store` with the specified `ty`.
//
// The `ty` must be a reference type (`funref` or `externref`) and `init`
// is the initial value for all table slots and must have the type specified by
// `ty`.
func NewTable(store Storelike, ty *TableType, init Val) (*Table, error) {
var ret C.wasmtime_table_t
var raw_val C.wasmtime_val_t
init.initialize(store, &raw_val)
err := C.wasmtime_table_new(store.Context(), ty.ptr(), &raw_val, &ret)
C.wasmtime_val_unroot(store.Context(), &raw_val)
runtime.KeepAlive(store)
runtime.KeepAlive(ty)
if err != nil {
return nil, mkError(err)
}
return mkTable(ret), nil
}
func mkTable(val C.wasmtime_table_t) *Table {
return &Table{val}
}
// Size returns the size of this table in units of elements.
func (t *Table) Size(store Storelike) uint64 {
ret := C.wasmtime_table_size(store.Context(), &t.val)
runtime.KeepAlive(store)
return uint64(ret)
}
// Grow grows this table by the number of units specified, using the
// specified initializer value for new slots.
//
// Returns an error if the table failed to grow, or the previous size of the
// table if growth was successful.
func (t *Table) Grow(store Storelike, delta uint64, init Val) (uint64, error) {
var prev C.uint64_t
var raw_val C.wasmtime_val_t
init.initialize(store, &raw_val)
err := C.wasmtime_table_grow(store.Context(), &t.val, C.uint64_t(delta), &raw_val, &prev)
C.wasmtime_val_unroot(store.Context(), &raw_val)
runtime.KeepAlive(store)
if err != nil {
return 0, mkError(err)
}
return uint64(prev), nil
}
// Get gets an item from this table from the specified index.
//
// Returns an error if the index is out of bounds, or returns a value (which
// may be internally null) if the index is in bounds corresponding to the entry
// at the specified index.
func (t *Table) Get(store Storelike, idx uint64) (Val, error) {
var val C.wasmtime_val_t
ok := C.wasmtime_table_get(store.Context(), &t.val, C.uint64_t(idx), &val)
runtime.KeepAlive(store)
if !ok {
return Val{}, errors.New("index out of bounds")
}
return takeVal(store, &val), nil
}
// Set sets an item in this table at the specified index.
//
// Returns an error if the index is out of bounds.
func (t *Table) Set(store Storelike, idx uint64, val Val) error {
var raw_val C.wasmtime_val_t
val.initialize(store, &raw_val)
err := C.wasmtime_table_set(store.Context(), &t.val, C.uint64_t(idx), &raw_val)
C.wasmtime_val_unroot(store.Context(), &raw_val)
runtime.KeepAlive(store)
if err != nil {
return mkError(err)
}
return nil
}
// Type returns the underlying type of this table
func (t *Table) Type(store Storelike) *TableType {
ptr := C.wasmtime_table_type(store.Context(), &t.val)
runtime.KeepAlive(store)
return mkTableType(ptr, nil)
}
func (t *Table) AsExtern() C.wasmtime_extern_t {
ret := C.wasmtime_extern_t{kind: C.WASMTIME_EXTERN_TABLE}
C.go_wasmtime_extern_table_set(&ret, t.val)
return ret
}
package wasmtime
// #include <wasm.h>
import "C"
import "runtime"
// TableType is one of table types which classify tables over elements of element types within a size range.
type TableType struct {
_ptr *C.wasm_tabletype_t
_owner interface{}
}
// NewTableType creates a new `TableType` with the `element` type provided as
// well as limits on its size.
//
// The `min` value is the minimum size, in elements, of this table. The
// `has_max` boolean indicates whether a maximum size is present, and if so
// `max` is used as the maximum size of the table, in elements.
func NewTableType(element *ValType, min uint32, has_max bool, max uint32) *TableType {
valptr := C.wasm_valtype_new(C.wasm_valtype_kind(element.ptr()))
runtime.KeepAlive(element)
if !has_max {
max = 0xffffffff
}
limitsFFI := C.wasm_limits_t{
min: C.uint32_t(min),
max: C.uint32_t(max),
}
ptr := C.wasm_tabletype_new(valptr, &limitsFFI)
return mkTableType(ptr, nil)
}
func mkTableType(ptr *C.wasm_tabletype_t, owner interface{}) *TableType {
tabletype := &TableType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(tabletype, func(tabletype *TableType) {
tabletype.Close()
})
}
return tabletype
}
func (ty *TableType) ptr() *C.wasm_tabletype_t {
ret := ty._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (ty *TableType) owner() interface{} {
if ty._owner != nil {
return ty._owner
}
return ty
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (ty *TableType) Close() {
if ty._ptr == nil || ty._owner != nil {
return
}
runtime.SetFinalizer(ty, nil)
C.wasm_tabletype_delete(ty._ptr)
ty._ptr = nil
}
// Element returns the type of value stored in this table
func (ty *TableType) Element() *ValType {
ptr := C.wasm_tabletype_element(ty.ptr())
return mkValType(ptr, ty.owner())
}
// Minimum returns the minimum size, in elements, of this table.
func (ty *TableType) Minimum() uint32 {
ptr := C.wasm_tabletype_limits(ty.ptr())
ret := uint32(ptr.min)
runtime.KeepAlive(ty)
return ret
}
// Maximum returns the maximum size, in elements, of this table.
//
// If no maximum size is listed then `(false, 0)` is returned, otherwise
// `(true, N)` is returned where `N` is the maximum size.
func (ty *TableType) Maximum() (bool, uint32) {
ptr := C.wasm_tabletype_limits(ty.ptr())
ret := uint32(ptr.max)
runtime.KeepAlive(ty)
if ret == 0xffffffff {
return false, 0
} else {
return true, ret
}
}
// AsExternType converts this type to an instance of `ExternType`
func (ty *TableType) AsExternType() *ExternType {
ptr := C.wasm_tabletype_as_externtype_const(ty.ptr())
return mkExternType(ptr, ty.owner())
}
package wasmtime
// #include <stdlib.h>
// #include <wasm.h>
// #include <wasmtime.h>
import "C"
import (
"runtime"
"unsafe"
)
// Trap is the trap instruction which represents the occurrence of a trap.
// Traps are bubbled up through nested instruction sequences, ultimately reducing the entire program to a single trap instruction, signalling abrupt termination.
type Trap struct {
_ptr *C.wasm_trap_t
}
// Frame is one of activation frames which carry the return arity n of the respective function,
// hold the values of its locals (including arguments) in the order corresponding to their static local indices,
// and a reference to the function’s own module instance
type Frame struct {
_ptr *C.wasm_frame_t
_owner interface{}
}
// TrapCode is the code of an instruction trap.
type TrapCode uint8
const (
// StackOverflow: the current stack space was exhausted.
StackOverflow TrapCode = iota
// MemoryOutOfBounds: out-of-bounds memory access.
MemoryOutOfBounds
// HeapMisaligned: a wasm atomic operation was presented with a not-naturally-aligned linear-memory address.
HeapMisaligned
// TableOutOfBounds: out-of-bounds access to a table.
TableOutOfBounds
// IndirectCallToNull: indirect call to a null table entry.
IndirectCallToNull
// BadSignature: signature mismatch on indirect call.
BadSignature
// IntegerOverflow: an integer arithmetic operation caused an overflow.
IntegerOverflow
// IntegerDivisionByZero: integer division by zero.
IntegerDivisionByZero
// BadConversionToInteger: failed float-to-int conversion.
BadConversionToInteger
// UnreachableCodeReached: code that was supposed to have been unreachable was reached.
UnreachableCodeReached
// Interrupt: execution has been interrupted.
Interrupt
// OutOfFuel: Execution has run out of the configured fuel amount.
OutOfFuel
)
// NewTrap creates a new `Trap` with the `name` and the type provided.
func NewTrap(message string) *Trap {
ptr := C.wasmtime_trap_new(C._GoStringPtr(message), C._GoStringLen(message))
runtime.KeepAlive(message)
return mkTrap(ptr)
}
func mkTrap(ptr *C.wasm_trap_t) *Trap {
trap := &Trap{_ptr: ptr}
runtime.SetFinalizer(trap, func(trap *Trap) {
C.wasm_trap_delete(trap._ptr)
})
return trap
}
func (t *Trap) ptr() *C.wasm_trap_t {
ret := t._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (t *Trap) Close() {
if t._ptr == nil {
return
}
runtime.SetFinalizer(t, nil)
C.wasm_trap_delete(t._ptr)
t._ptr = nil
}
// Message returns the message of the `Trap`
func (t *Trap) Message() string {
message := C.wasm_byte_vec_t{}
C.wasm_trap_message(t.ptr(), &message)
ret := C.GoStringN(message.data, C.int(message.size-1))
runtime.KeepAlive(t)
C.wasm_byte_vec_delete(&message)
return ret
}
// Code returns the code of the `Trap` if it exists, nil otherwise.
func (t *Trap) Code() *TrapCode {
var code C.uint8_t
var ret *TrapCode
ok := C.wasmtime_trap_code(t.ptr(), &code)
if ok == C._Bool(true) {
ret = (*TrapCode)(&code)
}
runtime.KeepAlive(t)
return ret
}
func (t *Trap) Error() string {
return t.Message()
}
func unwrapStrOr(s *string, other string) string {
if s == nil {
return other
}
return *s
}
type frameList struct {
vec C.wasm_frame_vec_t
owner interface{}
}
// Frames returns the wasm function frames that make up this trap
func (t *Trap) Frames() []*Frame {
frames := &frameList{owner: t}
C.wasm_trap_trace(t.ptr(), &frames.vec)
runtime.KeepAlive(t)
runtime.SetFinalizer(frames, func(frames *frameList) {
C.wasm_frame_vec_delete(&frames.vec)
})
ret := make([]*Frame, int(frames.vec.size))
base := unsafe.Pointer(frames.vec.data)
var ptr *C.wasm_frame_t
for i := 0; i < int(frames.vec.size); i++ {
ptr := *(**C.wasm_frame_t)(unsafe.Pointer(uintptr(base) + unsafe.Sizeof(ptr)*uintptr(i)))
ret[i] = &Frame{
_ptr: ptr,
_owner: frames,
}
}
return ret
}
func (f *Frame) ptr() *C.wasm_frame_t {
ret := f._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
// FuncIndex returns the function index in the wasm module that this frame represents
func (f *Frame) FuncIndex() uint32 {
ret := C.wasm_frame_func_index(f.ptr())
runtime.KeepAlive(f)
return uint32(ret)
}
// FuncName returns the name, if available, for this frame's function
func (f *Frame) FuncName() *string {
ret := C.wasmtime_frame_func_name(f.ptr())
if ret == nil {
runtime.KeepAlive(f)
return nil
}
str := C.GoStringN(ret.data, C.int(ret.size))
runtime.KeepAlive(f)
return &str
}
// ModuleName returns the name, if available, for this frame's module
func (f *Frame) ModuleName() *string {
ret := C.wasmtime_frame_module_name(f.ptr())
if ret == nil {
runtime.KeepAlive(f)
return nil
}
str := C.GoStringN(ret.data, C.int(ret.size))
runtime.KeepAlive(f)
return &str
}
// ModuleOffset returns offset of this frame's instruction into the original module
func (f *Frame) ModuleOffset() uint {
ret := uint(C.wasm_frame_module_offset(f.ptr()))
runtime.KeepAlive(f)
return ret
}
// FuncOffset returns offset of this frame's instruction into the original function
func (f *Frame) FuncOffset() uint {
ret := uint(C.wasm_frame_func_offset(f.ptr()))
runtime.KeepAlive(f)
return ret
}
package wasmtime
// #include <wasm.h>
// #include "shims.h"
import "C"
import (
"runtime"
"sync"
"unsafe"
)
var gExternrefLock sync.Mutex
var gExternrefMap = make(map[int]interface{})
var gExternrefSlab slab
// Val is a primitive numeric value.
// Moreover, in the definition of programs, immutable sequences of values occur to represent more complex data, such as text strings or other vectors.
type Val struct {
kind C.wasmtime_valkind_t
val interface{}
}
// ValI32 converts a go int32 to a i32 Val
func ValI32(val int32) Val {
return Val{kind: C.WASMTIME_I32, val: val}
}
// ValI64 converts a go int64 to a i64 Val
func ValI64(val int64) Val {
return Val{kind: C.WASMTIME_I64, val: val}
}
// ValF32 converts a go float32 to a f32 Val
func ValF32(val float32) Val {
return Val{kind: C.WASMTIME_F32, val: val}
}
// ValF64 converts a go float64 to a f64 Val
func ValF64(val float64) Val {
return Val{kind: C.WASMTIME_F64, val: val}
}
// ValFuncref converts a Func to a funcref Val
//
// Note that `f` can be `nil` to represent a null `funcref`.
func ValFuncref(f *Func) Val {
return Val{kind: C.WASMTIME_FUNCREF, val: f}
}
// ValExternref converts a go value to a externref Val
//
// Using `externref` is a way to pass arbitrary Go data into a WebAssembly
// module for it to store. Later, when you get a `Val`, you can extract the type
// with the `Externref()` method.
func ValExternref(val interface{}) Val {
return Val{kind: C.WASMTIME_EXTERNREF, val: val}
}
//export goFinalizeExternref
func goFinalizeExternref(env unsafe.Pointer) {
idx := int(uintptr(env)) - 1
gExternrefLock.Lock()
defer gExternrefLock.Unlock()
delete(gExternrefMap, idx)
gExternrefSlab.deallocate(idx)
}
func mkVal(store Storelike, src *C.wasmtime_val_t) Val {
switch src.kind {
case C.WASMTIME_I32:
return ValI32(int32(C.go_wasmtime_val_i32_get(src)))
case C.WASMTIME_I64:
return ValI64(int64(C.go_wasmtime_val_i64_get(src)))
case C.WASMTIME_F32:
return ValF32(float32(C.go_wasmtime_val_f32_get(src)))
case C.WASMTIME_F64:
return ValF64(float64(C.go_wasmtime_val_f64_get(src)))
case C.WASMTIME_FUNCREF:
val := C.go_wasmtime_val_funcref_get(src)
if val.store_id == 0 {
return ValFuncref(nil)
} else {
return ValFuncref(mkFunc(val))
}
case C.WASMTIME_EXTERNREF:
val := C.go_wasmtime_val_externref_get(src)
if val.store_id == 0 {
return ValExternref(nil)
}
data := C.wasmtime_externref_data(store.Context(), &val)
runtime.KeepAlive(store)
gExternrefLock.Lock()
defer gExternrefLock.Unlock()
return ValExternref(gExternrefMap[int(uintptr(data))-1])
}
panic("failed to get kind of `Val`")
}
func takeVal(store Storelike, src *C.wasmtime_val_t) Val {
ret := mkVal(store, src)
C.wasmtime_val_unroot(store.Context(), src)
runtime.KeepAlive(store)
return ret
}
// Kind returns the kind of value that this `Val` contains.
func (v Val) Kind() ValKind {
switch v.kind {
case C.WASMTIME_I32:
return KindI32
case C.WASMTIME_I64:
return KindI64
case C.WASMTIME_F32:
return KindF32
case C.WASMTIME_F64:
return KindF64
case C.WASMTIME_FUNCREF:
return KindFuncref
case C.WASMTIME_EXTERNREF:
return KindExternref
}
panic("failed to get kind of `Val`")
}
// I32 returns the underlying 32-bit integer if this is an `i32`, or panics.
func (v Val) I32() int32 {
if v.Kind() != KindI32 {
panic("not an i32")
}
return v.val.(int32)
}
// I64 returns the underlying 64-bit integer if this is an `i64`, or panics.
func (v Val) I64() int64 {
if v.Kind() != KindI64 {
panic("not an i64")
}
return v.val.(int64)
}
// F32 returns the underlying 32-bit float if this is an `f32`, or panics.
func (v Val) F32() float32 {
if v.Kind() != KindF32 {
panic("not an f32")
}
return v.val.(float32)
}
// F64 returns the underlying 64-bit float if this is an `f64`, or panics.
func (v Val) F64() float64 {
if v.Kind() != KindF64 {
panic("not an f64")
}
return v.val.(float64)
}
// Funcref returns the underlying function if this is a `funcref`, or panics.
//
// Note that a null `funcref` is returned as `nil`.
func (v Val) Funcref() *Func {
if v.Kind() != KindFuncref {
panic("not a funcref")
}
return v.val.(*Func)
}
// Externref returns the underlying value if this is an `externref`, or panics.
//
// Note that a null `externref` is returned as `nil`.
func (v Val) Externref() interface{} {
if v.Kind() != KindExternref {
panic("not an externref")
}
return v.val
}
// Get returns the underlying 64-bit float if this is an `f64`, or panics.
func (v Val) Get() interface{} {
return v.val
}
func (v Val) initialize(store Storelike, ptr *C.wasmtime_val_t) {
ptr.kind = v.kind
switch v.kind {
case C.WASMTIME_I32:
C.go_wasmtime_val_i32_set(ptr, C.int32_t(v.val.(int32)))
case C.WASMTIME_I64:
C.go_wasmtime_val_i64_set(ptr, C.int64_t(v.val.(int64)))
case C.WASMTIME_F32:
C.go_wasmtime_val_f32_set(ptr, C.float(v.val.(float32)))
case C.WASMTIME_F64:
C.go_wasmtime_val_f64_set(ptr, C.double(v.val.(float64)))
case C.WASMTIME_FUNCREF:
val := v.val.(*Func)
if val != nil {
C.go_wasmtime_val_funcref_set(ptr, val.val)
} else {
empty := C.wasmtime_func_t{}
C.go_wasmtime_val_funcref_set(ptr, empty)
}
case C.WASMTIME_EXTERNREF:
// If we have a non-nil value then store it in our global map
// of all externref values. Otherwise there's nothing for us to
// do since the `ref` field will already be a nil pointer.
//
// Note that we add 1 so all non-null externref values are
// created with non-null pointers.
if v.val == nil {
C.go_wasmtime_val_externref_set(ptr, C.wasmtime_externref_t{})
} else {
gExternrefLock.Lock()
defer gExternrefLock.Unlock()
index := gExternrefSlab.allocate()
gExternrefMap[index] = v.val
var ref C.wasmtime_externref_t
ok := C.go_externref_new(store.Context(), C.size_t(index+1), &ref)
runtime.KeepAlive(store)
if ok {
C.go_wasmtime_val_externref_set(ptr, ref)
} else {
panic("failed to create an externref")
}
}
default:
panic("failed to get kind of `Val`")
}
}
package wasmtime
// #include <wasm.h>
import "C"
import "runtime"
// ValKind enumeration of different kinds of value types
type ValKind C.wasm_valkind_t
const (
// KindI32 is the types i32 classify 32 bit integers. Integers are not inherently signed or unsigned, their interpretation is determined by individual operations.
KindI32 ValKind = C.WASM_I32
// KindI64 is the types i64 classify 64 bit integers. Integers are not inherently signed or unsigned, their interpretation is determined by individual operations.
KindI64 ValKind = C.WASM_I64
// KindF32 is the types f32 classify 32 bit floating-point data. They correspond to the respective binary floating-point representations, also known as single and double precision, as defined by the IEEE 754-2019 standard.
KindF32 ValKind = C.WASM_F32
// KindF64 is the types f64 classify 64 bit floating-point data. They correspond to the respective binary floating-point representations, also known as single and double precision, as defined by the IEEE 754-2019 standard.
KindF64 ValKind = C.WASM_F64
// TODO: Unknown
KindExternref ValKind = C.WASM_EXTERNREF
// KindFuncref is the infinite union of all function types.
KindFuncref ValKind = C.WASM_FUNCREF
)
// String renders this kind as a string, similar to the `*.wat` format
func (ty ValKind) String() string {
switch ty {
case KindI32:
return "i32"
case KindI64:
return "i64"
case KindF32:
return "f32"
case KindF64:
return "f64"
case KindExternref:
return "externref"
case KindFuncref:
return "funcref"
}
panic("unknown kind")
}
// ValType means one of the value types, which classify the individual values that WebAssembly code can compute with and the values that a variable accepts.
type ValType struct {
_ptr *C.wasm_valtype_t
_owner interface{}
}
// NewValType creates a new `ValType` with the `kind` provided
func NewValType(kind ValKind) *ValType {
ptr := C.wasm_valtype_new(C.wasm_valkind_t(kind))
return mkValType(ptr, nil)
}
func mkValType(ptr *C.wasm_valtype_t, owner interface{}) *ValType {
valtype := &ValType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(valtype, func(valtype *ValType) {
valtype.Close()
})
}
return valtype
}
// Kind returns the corresponding `ValKind` for this `ValType`
func (t *ValType) Kind() ValKind {
ret := ValKind(C.wasm_valtype_kind(t.ptr()))
runtime.KeepAlive(t)
return ret
}
// Converts this `ValType` into a string according to the string representation
// of `ValKind`.
func (t *ValType) String() string {
return t.Kind().String()
}
func (t *ValType) ptr() *C.wasm_valtype_t {
ret := t._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
func (t *ValType) owner() interface{} {
if t._owner != nil {
return t._owner
}
return t
}
// Close will deallocate this type's state explicitly.
//
// For more information see the documentation for engine.Close()
func (ty *ValType) Close() {
if ty._ptr == nil || ty._owner != nil {
return
}
runtime.SetFinalizer(ty, nil)
C.wasm_valtype_delete(ty._ptr)
ty._ptr = nil
}
package wasmtime
// #include <wasi.h>
// #include <stdlib.h>
import "C"
import (
"errors"
"runtime"
"unsafe"
)
type WasiConfig struct {
_ptr *C.wasi_config_t
}
func NewWasiConfig() *WasiConfig {
ptr := C.wasi_config_new()
config := &WasiConfig{_ptr: ptr}
runtime.SetFinalizer(config, func(config *WasiConfig) {
config.Close()
})
return config
}
func (c *WasiConfig) ptr() *C.wasi_config_t {
ret := c._ptr
if ret == nil {
panic("object has been closed already")
}
maybeGC()
return ret
}
// Close will deallocate this WASI configuration's state explicitly.
//
// For more information see the documentation for engine.Close()
func (c *WasiConfig) Close() {
if c._ptr == nil {
return
}
runtime.SetFinalizer(c, nil)
C.wasi_config_delete(c._ptr)
c._ptr = nil
}
// SetArgv will explicitly configure the argv for this WASI configuration.
// Note that this field can only be set, it cannot be read
func (c *WasiConfig) SetArgv(argv []string) {
ptrs := make([]*C.char, len(argv))
for i, arg := range argv {
ptrs[i] = C.CString(arg)
}
var argvRaw **C.char
if len(ptrs) > 0 {
argvRaw = &ptrs[0]
}
C.wasi_config_set_argv(c.ptr(), C.size_t(len(argv)), argvRaw)
runtime.KeepAlive(c)
for _, ptr := range ptrs {
C.free(unsafe.Pointer(ptr))
}
}
func (c *WasiConfig) InheritArgv() {
C.wasi_config_inherit_argv(c.ptr())
runtime.KeepAlive(c)
}
// SetEnv configures environment variables to be returned for this WASI configuration.
// The pairs provided must be an iterable list of key/value pairs of environment variables.
// Note that this field can only be set, it cannot be read
func (c *WasiConfig) SetEnv(keys, values []string) {
if len(keys) != len(values) {
panic("mismatched numbers of keys and values")
}
namePtrs := make([]*C.char, len(values))
valuePtrs := make([]*C.char, len(values))
for i, key := range keys {
namePtrs[i] = C.CString(key)
}
for i, value := range values {
valuePtrs[i] = C.CString(value)
}
var namesRaw, valuesRaw **C.char
if len(keys) > 0 {
namesRaw = &namePtrs[0]
valuesRaw = &valuePtrs[0]
}
C.wasi_config_set_env(c.ptr(), C.size_t(len(keys)), namesRaw, valuesRaw)
runtime.KeepAlive(c)
for i, ptr := range namePtrs {
C.free(unsafe.Pointer(ptr))
C.free(unsafe.Pointer(valuePtrs[i]))
}
}
func (c *WasiConfig) InheritEnv() {
C.wasi_config_inherit_env(c.ptr())
runtime.KeepAlive(c)
}
func (c *WasiConfig) SetStdinFile(path string) error {
pathC := C.CString(path)
ok := C.wasi_config_set_stdin_file(c.ptr(), pathC)
runtime.KeepAlive(c)
C.free(unsafe.Pointer(pathC))
if ok {
return nil
}
return errors.New("failed to open file")
}
func (c *WasiConfig) InheritStdin() {
C.wasi_config_inherit_stdin(c.ptr())
runtime.KeepAlive(c)
}
func (c *WasiConfig) SetStdoutFile(path string) error {
pathC := C.CString(path)
ok := C.wasi_config_set_stdout_file(c.ptr(), pathC)
runtime.KeepAlive(c)
C.free(unsafe.Pointer(pathC))
if ok {
return nil
}
return errors.New("failed to open file")
}
func (c *WasiConfig) InheritStdout() {
C.wasi_config_inherit_stdout(c.ptr())
runtime.KeepAlive(c)
}
func (c *WasiConfig) SetStderrFile(path string) error {
pathC := C.CString(path)
ok := C.wasi_config_set_stderr_file(c.ptr(), pathC)
runtime.KeepAlive(c)
C.free(unsafe.Pointer(pathC))
if ok {
return nil
}
return errors.New("failed to open file")
}
func (c *WasiConfig) InheritStderr() {
C.wasi_config_inherit_stderr(c.ptr())
runtime.KeepAlive(c)
}
type WasiDirPerms uint8
type WasiFilePerms uint8
const (
DIR_READ WasiDirPerms = C.WASMTIME_WASI_DIR_PERMS_READ
DIR_WRITE WasiDirPerms = C.WASMTIME_WASI_DIR_PERMS_WRITE
FILE_READ WasiFilePerms = C.WASMTIME_WASI_FILE_PERMS_READ
FILE_WRITE WasiFilePerms = C.WASMTIME_WASI_FILE_PERMS_WRITE
)
func (c *WasiConfig) PreopenDir(path, guestPath string, dirPerms WasiDirPerms, filePerms WasiFilePerms) error {
pathC := C.CString(path)
guestPathC := C.CString(guestPath)
ok := C.wasi_config_preopen_dir(c.ptr(), pathC, guestPathC,
C.wasi_dir_perms(dirPerms), C.wasi_file_perms(filePerms))
runtime.KeepAlive(c)
C.free(unsafe.Pointer(pathC))
C.free(unsafe.Pointer(guestPathC))
if ok {
return nil
}
return errors.New("failed to preopen directory")
}
package wasmtime
// #include <wasmtime.h>
import "C"
import (
"runtime"
"unsafe"
)
// Wat2Wasm converts the text format of WebAssembly to the binary format.
//
// Takes the text format in-memory as input, and returns either the binary
// encoding of the text format or an error if parsing fails.
func Wat2Wasm(wat string) ([]byte, error) {
retVec := C.wasm_byte_vec_t{}
err := C.wasmtime_wat2wasm(
C._GoStringPtr(wat),
C._GoStringLen(wat),
&retVec,
)
runtime.KeepAlive(wat)
if err == nil {
ret := C.GoBytes(unsafe.Pointer(retVec.data), C.int(retVec.size))
C.wasm_byte_vec_delete(&retVec)
return ret, nil
}
return nil, mkError(err)
}