wa-lang.org/wazero@v1.0.2/imports/wasi_snapshot_preview1/wasi.go (about) 1 // Package wasi_snapshot_preview1 contains Go-defined functions to access 2 // system calls, such as opening a file, similar to Go's x/sys package. These 3 // are accessible from WebAssembly-defined functions via importing ModuleName. 4 // All WASI functions return a single Errno result: ErrnoSuccess on success. 5 // 6 // e.g. Call Instantiate before instantiating any wasm binary that imports 7 // "wasi_snapshot_preview1", Otherwise, it will error due to missing imports. 8 // 9 // ctx := context.Background() 10 // r := wazero.NewRuntime(ctx) 11 // defer r.Close(ctx) // This closes everything this Runtime created. 12 // 13 // wasi_snapshot_preview1.MustInstantiate(ctx, r) 14 // mod, _ := r.InstantiateModuleFromBinary(ctx, wasm) 15 // 16 // See https://github.com/WebAssembly/WASI 17 package wasi_snapshot_preview1 18 19 import ( 20 "context" 21 22 "wa-lang.org/wazero" 23 "wa-lang.org/wazero/api" 24 "wa-lang.org/wazero/internal/wasm" 25 ) 26 27 // ModuleName is the module name WASI functions are exported into. 28 // 29 // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md 30 const ( 31 ModuleName = "wasi_snapshot_preview1" 32 i32, i64 = wasm.ValueTypeI32, wasm.ValueTypeI64 33 ) 34 35 // MustInstantiate calls Instantiate or panics on error. 36 // 37 // This is a simpler function for those who know the module ModuleName is not 38 // already instantiated, and don't need to unload it. 39 func MustInstantiate(ctx context.Context, r wazero.Runtime) { 40 if _, err := Instantiate(ctx, r); err != nil { 41 panic(err) 42 } 43 } 44 45 // Instantiate instantiates the ModuleName module into the runtime default 46 // namespace. 47 // 48 // # Notes 49 // 50 // - Failure cases are documented on wazero.Namespace InstantiateModule. 51 // - Closing the wazero.Runtime has the same effect as closing the result. 52 // - To instantiate into another wazero.Namespace, use NewBuilder instead. 53 func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) { 54 return NewBuilder(r).Instantiate(ctx, r) 55 } 56 57 // Builder configures the ModuleName module for later use via Compile or Instantiate. 58 type Builder interface { 59 // Compile compiles the ModuleName module that can instantiated in any 60 // namespace (wazero.Namespace). 61 // 62 // Note: This has the same effect as the same function on wazero.HostModuleBuilder. 63 Compile(context.Context) (wazero.CompiledModule, error) 64 65 // Instantiate instantiates the ModuleName module into the given namespace. 66 // 67 // Note: This has the same effect as the same function on wazero.HostModuleBuilder. 68 Instantiate(context.Context, wazero.Namespace) (api.Closer, error) 69 } 70 71 // NewBuilder returns a new Builder. 72 func NewBuilder(r wazero.Runtime) Builder { 73 return &builder{r} 74 } 75 76 type builder struct{ r wazero.Runtime } 77 78 // hostModuleBuilder returns a new wazero.HostModuleBuilder for ModuleName 79 func (b *builder) hostModuleBuilder() wazero.HostModuleBuilder { 80 ret := b.r.NewHostModuleBuilder(ModuleName) 81 exportFunctions(ret) 82 return ret 83 } 84 85 // Compile implements Builder.Compile 86 func (b *builder) Compile(ctx context.Context) (wazero.CompiledModule, error) { 87 return b.hostModuleBuilder().Compile(ctx) 88 } 89 90 // Instantiate implements Builder.Instantiate 91 func (b *builder) Instantiate(ctx context.Context, ns wazero.Namespace) (api.Closer, error) { 92 return b.hostModuleBuilder().Instantiate(ctx, ns) 93 } 94 95 // FunctionExporter exports functions into a wazero.HostModuleBuilder. 96 type FunctionExporter interface { 97 ExportFunctions(wazero.HostModuleBuilder) 98 } 99 100 // NewFunctionExporter returns a new FunctionExporter. This is used for the 101 // following two use cases: 102 // - Overriding a builtin function with an alternate implementation. 103 // - Exporting functions to the module "wasi_unstable" for legacy code. 104 // 105 // # Example of overriding default behavior 106 // 107 // // Export the default WASI functions. 108 // wasiBuilder := r.NewHostModuleBuilder(ModuleName) 109 // wasi_snapshot_preview1.NewFunctionExporter().ExportFunctions(wasiBuilder) 110 // 111 // // Subsequent calls to NewFunctionBuilder override built-in exports. 112 // wasiBuilder.NewFunctionBuilder(). 113 // WithFunc(func(ctx context.Context, mod api.Module, exitCode uint32) { 114 // // your custom logic 115 // }).Export("proc_exit") 116 // 117 // # Example of using the old module name for WASI 118 // 119 // // Instantiate the current WASI functions under the wasi_unstable 120 // // instead of wasi_snapshot_preview1. 121 // wasiBuilder := r.NewHostModuleBuilder("wasi_unstable") 122 // wasi_snapshot_preview1.NewFunctionExporter().ExportFunctions(wasiBuilder) 123 // _, err := wasiBuilder.Instantiate(testCtx, r) 124 func NewFunctionExporter() FunctionExporter { 125 return &functionExporter{} 126 } 127 128 type functionExporter struct{} 129 130 // ExportFunctions implements FunctionExporter.ExportFunctions 131 func (functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) { 132 exportFunctions(builder) 133 } 134 135 // ## Translation notes 136 // ### String 137 // WebAssembly 1.0 has no string type, so any string input parameter expands to two uint32 parameters: offset 138 // and length. 139 // 140 // ### iovec_array 141 // `iovec_array` is encoded as two uin32le values (i32): offset and count. 142 // 143 // ### Result 144 // Each result besides Errno is always an uint32 parameter. WebAssembly 1.0 can have up to one result, 145 // which is already used by Errno. This forces other results to be parameters. A result parameter is a memory 146 // offset to write the result to. As memory offsets are uint32, each parameter representing a result is uint32. 147 // 148 // ### Errno 149 // The WASI specification is sometimes ambiguous resulting in some runtimes interpreting the same function ways. 150 // Errno mappings are not defined in WASI, yet, so these mappings are best efforts by maintainers. When in doubt 151 // about portability, first look at /RATIONALE.md and if needed an issue on 152 // https://github.com/WebAssembly/WASI/issues 153 // 154 // ## Memory 155 // In WebAssembly 1.0 (20191205), there may be up to one Memory per store, which means api.Memory is always the 156 // wasm.Store Memories index zero: `store.Memories[0].Buffer` 157 // 158 // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md 159 // See https://github.com/WebAssembly/WASI/issues/215 160 // See https://wwa.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-instances%E2%91%A0. 161 162 // exportFunctions adds all go functions that implement wasi. 163 // These should be exported in the module named ModuleName. 164 func exportFunctions(builder wazero.HostModuleBuilder) { 165 exporter := builder.(wasm.HostFuncExporter) 166 167 // Note: these are ordered per spec for consistency even if the resulting 168 // map can't guarantee that. 169 // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#functions 170 exporter.ExportHostFunc(argsGet) 171 exporter.ExportHostFunc(argsSizesGet) 172 exporter.ExportHostFunc(environGet) 173 exporter.ExportHostFunc(environSizesGet) 174 exporter.ExportHostFunc(clockResGet) 175 exporter.ExportHostFunc(clockTimeGet) 176 exporter.ExportHostFunc(fdAdvise) 177 exporter.ExportHostFunc(fdAllocate) 178 exporter.ExportHostFunc(fdClose) 179 exporter.ExportHostFunc(fdDatasync) 180 exporter.ExportHostFunc(fdFdstatGet) 181 exporter.ExportHostFunc(fdFdstatSetFlags) 182 exporter.ExportHostFunc(fdFdstatSetRights) 183 exporter.ExportHostFunc(fdFilestatGet) 184 exporter.ExportHostFunc(fdFilestatSetSize) 185 exporter.ExportHostFunc(fdFilestatSetTimes) 186 exporter.ExportHostFunc(fdPread) 187 exporter.ExportHostFunc(fdPrestatGet) 188 exporter.ExportHostFunc(fdPrestatDirName) 189 exporter.ExportHostFunc(fdPwrite) 190 exporter.ExportHostFunc(fdRead) 191 exporter.ExportHostFunc(fdReaddir) 192 exporter.ExportHostFunc(fdRenumber) 193 exporter.ExportHostFunc(fdSeek) 194 exporter.ExportHostFunc(fdSync) 195 exporter.ExportHostFunc(fdTell) 196 exporter.ExportHostFunc(fdWrite) 197 exporter.ExportHostFunc(pathCreateDirectory) 198 exporter.ExportHostFunc(pathFilestatGet) 199 exporter.ExportHostFunc(pathFilestatSetTimes) 200 exporter.ExportHostFunc(pathLink) 201 exporter.ExportHostFunc(pathOpen) 202 exporter.ExportHostFunc(pathReadlink) 203 exporter.ExportHostFunc(pathRemoveDirectory) 204 exporter.ExportHostFunc(pathRename) 205 exporter.ExportHostFunc(pathSymlink) 206 exporter.ExportHostFunc(pathUnlinkFile) 207 exporter.ExportHostFunc(pollOneoff) 208 exporter.ExportHostFunc(procExit) 209 exporter.ExportHostFunc(procRaise) 210 exporter.ExportHostFunc(schedYield) 211 exporter.ExportHostFunc(randomGet) 212 exporter.ExportHostFunc(sockAccept) 213 exporter.ExportHostFunc(sockRecv) 214 exporter.ExportHostFunc(sockSend) 215 exporter.ExportHostFunc(sockShutdown) 216 } 217 218 func writeOffsetsAndNullTerminatedValues(ctx context.Context, mem api.Memory, values []string, offsets, bytes uint32) Errno { 219 for _, value := range values { 220 // Write current offset and advance it. 221 if !mem.WriteUint32Le(ctx, offsets, bytes) { 222 return ErrnoFault 223 } 224 offsets += 4 // size of uint32 225 226 // Write the next value to memory with a NUL terminator 227 if !mem.Write(ctx, bytes, []byte(value)) { 228 return ErrnoFault 229 } 230 bytes += uint32(len(value)) 231 if !mem.WriteByte(ctx, bytes, 0) { 232 return ErrnoFault 233 } 234 bytes++ 235 } 236 237 return ErrnoSuccess 238 } 239 240 // wasiFunc special cases that all WASI functions return a single Errno 241 // result. The returned value will be written back to the stack at index zero. 242 type wasiFunc func(ctx context.Context, mod api.Module, params []uint64) Errno 243 244 // Call implements the same method as documented on api.GoModuleFunction. 245 func (f wasiFunc) Call(ctx context.Context, mod api.Module, stack []uint64) { 246 // Write the result back onto the stack 247 stack[0] = uint64(f(ctx, mod, stack)) 248 } 249 250 // stubFunction stubs for GrainLang per #271. 251 func stubFunction(name string, paramTypes []wasm.ValueType, paramNames []string) *wasm.HostFunc { 252 return &wasm.HostFunc{ 253 Name: name, 254 ExportNames: []string{name}, 255 ParamTypes: paramTypes, 256 ParamNames: paramNames, 257 ResultTypes: []wasm.ValueType{i32}, 258 Code: &wasm.Code{ 259 IsHostFunction: true, 260 Body: []byte{wasm.OpcodeI32Const, byte(ErrnoNosys), wasm.OpcodeEnd}, 261 }, 262 } 263 }