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) } // 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) } // IsPulley will return whether the current engine's execution is backed by // the Pulley interpreter inside of Wasmtime. If this returns false then // native execution is used instead. func (engine *Engine) IsPulley() bool { ret := C.wasmtime_engine_is_pulley(engine.ptr()) runtime.KeepAlive(engine) return bool(ret) }
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) }