github.com/tetratelabs/wazero@v1.2.1/experimental/gojs/gojs.go (about) 1 // Package gojs allows you to run wasm binaries compiled by Go when 2 // `GOARCH=wasm GOOS=js`. See https://wazero.io/languages/go/ for more. 3 // 4 // # Experimental 5 // 6 // Go defines js "EXPERIMENTAL... exempt from the Go compatibility promise." 7 // Accordingly, wazero cannot guarantee this will work from release to release, 8 // or that usage will be relatively free of bugs. Moreover, `GOOS=wasi` will 9 // happen, and once that's available in two releases wazero will remove this 10 // package. 11 // 12 // Due to these concerns and the relatively high implementation overhead, most 13 // will choose TinyGo instead of gojs. 14 package gojs 15 16 import ( 17 "context" 18 "net/http" 19 20 "github.com/tetratelabs/wazero" 21 "github.com/tetratelabs/wazero/api" 22 . "github.com/tetratelabs/wazero/internal/gojs" 23 internalconfig "github.com/tetratelabs/wazero/internal/gojs/config" 24 . "github.com/tetratelabs/wazero/internal/gojs/run" 25 "github.com/tetratelabs/wazero/internal/wasm" 26 ) 27 28 // MustInstantiate calls Instantiate or panics on error. 29 // 30 // This is a simpler function for those who know the module "go" is not 31 // already instantiated, and don't need to unload it. 32 func MustInstantiate(ctx context.Context, r wazero.Runtime) { 33 if _, err := Instantiate(ctx, r); err != nil { 34 panic(err) 35 } 36 } 37 38 // Instantiate instantiates the "go" module, used by `GOARCH=wasm GOOS=js`, 39 // into the runtime. 40 // 41 // # Notes 42 // 43 // - Failure cases are documented on wazero.Runtime InstantiateModule. 44 // - Closing the wazero.Runtime has the same effect as closing the result. 45 // - To add more functions to the "env" module, use FunctionExporter. 46 func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) { 47 builder := r.NewHostModuleBuilder("go") 48 NewFunctionExporter().ExportFunctions(builder) 49 return builder.Instantiate(ctx) 50 } 51 52 // FunctionExporter configures the functions in the "go" module used by 53 // `GOARCH=wasm GOOS=js`. 54 type FunctionExporter interface { 55 // ExportFunctions builds functions to export with a 56 // wazero.HostModuleBuilder named "go". 57 ExportFunctions(wazero.HostModuleBuilder) 58 } 59 60 // NewFunctionExporter returns a FunctionExporter object. 61 func NewFunctionExporter() FunctionExporter { 62 return &functionExporter{} 63 } 64 65 type functionExporter struct{} 66 67 // ExportFunctions implements FunctionExporter.ExportFunctions 68 func (e *functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) { 69 hfExporter := builder.(wasm.HostFuncExporter) 70 71 hfExporter.ExportHostFunc(GetRandomData) 72 hfExporter.ExportHostFunc(Nanotime1) 73 hfExporter.ExportHostFunc(WasmExit) 74 hfExporter.ExportHostFunc(CopyBytesToJS) 75 hfExporter.ExportHostFunc(ValueCall) 76 hfExporter.ExportHostFunc(ValueGet) 77 hfExporter.ExportHostFunc(ValueIndex) 78 hfExporter.ExportHostFunc(ValueLength) 79 hfExporter.ExportHostFunc(ValueNew) 80 hfExporter.ExportHostFunc(ValueSet) 81 hfExporter.ExportHostFunc(WasmWrite) 82 hfExporter.ExportHostFunc(ResetMemoryDataView) 83 hfExporter.ExportHostFunc(Walltime) 84 hfExporter.ExportHostFunc(ScheduleTimeoutEvent) 85 hfExporter.ExportHostFunc(ClearTimeoutEvent) 86 hfExporter.ExportHostFunc(FinalizeRef) 87 hfExporter.ExportHostFunc(StringVal) 88 hfExporter.ExportHostFunc(ValueDelete) 89 hfExporter.ExportHostFunc(ValueSetIndex) 90 hfExporter.ExportHostFunc(ValueInvoke) 91 hfExporter.ExportHostFunc(ValuePrepareString) 92 hfExporter.ExportHostFunc(ValueInstanceOf) 93 hfExporter.ExportHostFunc(ValueLoadString) 94 hfExporter.ExportHostFunc(CopyBytesToGo) 95 hfExporter.ExportHostFunc(Debug) 96 } 97 98 // Config extends wazero.ModuleConfig with GOOS=js specific extensions. 99 // Use NewConfig to create an instance. 100 type Config interface { 101 // WithOSWorkdir sets the initial working directory used to Run Wasm to 102 // the value of os.Getwd instead of the default of root "/". 103 // 104 // Here's an example that overrides this to the current directory: 105 // 106 // err = gojs.Run(ctx, r, compiled, gojs.NewConfig(moduleConfig). 107 // WithOSWorkdir()) 108 // 109 // Note: To use this feature requires mounting the real root directory via 110 // wazero.FSConfig `WithDirMount`. On windows, this root must be the same drive 111 // as the value of os.Getwd. For example, it would be an error to mount `C:\` 112 // as the guest path "", while the current directory is inside `D:\`. 113 WithOSWorkdir() Config 114 115 // WithOSUser allows the guest to see the current user's uid, gid, euid and 116 // groups, instead of zero for each value. 117 // 118 // Here's an example that uses the real user's IDs: 119 // 120 // err = gojs.Run(ctx, r, compiled, gojs.NewConfig(moduleConfig). 121 // WithOSUser()) 122 // 123 // Note: This has no effect on windows. 124 WithOSUser() Config 125 126 // WithRoundTripper sets the http.RoundTripper used to Run Wasm. 127 // 128 // For example, if the code compiled via `GOARCH=wasm GOOS=js` uses 129 // http.RoundTripper, you can avoid failures by assigning an implementation 130 // like so: 131 // 132 // err = gojs.Run(ctx, r, compiled, gojs.NewConfig(moduleConfig). 133 // WithRoundTripper(ctx, http.DefaultTransport)) 134 WithRoundTripper(http.RoundTripper) Config 135 } 136 137 // NewConfig returns a Config that can be used for configuring module instantiation. 138 func NewConfig(moduleConfig wazero.ModuleConfig) Config { 139 return &cfg{moduleConfig: moduleConfig, internal: internalconfig.NewConfig()} 140 } 141 142 type cfg struct { 143 moduleConfig wazero.ModuleConfig 144 internal *internalconfig.Config 145 } 146 147 func (c *cfg) clone() *cfg { 148 return &cfg{moduleConfig: c.moduleConfig, internal: c.internal.Clone()} 149 } 150 151 // WithOSWorkdir implements Config.WithOSWorkdir 152 func (c *cfg) WithOSWorkdir() Config { 153 ret := c.clone() 154 ret.internal.OsWorkdir = true 155 return ret 156 } 157 158 // WithOSUser implements Config.WithOSUser 159 func (c *cfg) WithOSUser() Config { 160 ret := c.clone() 161 ret.internal.OsUser = true 162 return ret 163 } 164 165 // WithRoundTripper implements Config.WithRoundTripper 166 func (c *cfg) WithRoundTripper(rt http.RoundTripper) Config { 167 ret := c.clone() 168 ret.internal.Rt = rt 169 return ret 170 } 171 172 // Run instantiates a new module and calls "run" with the given config. 173 // 174 // # Parameters 175 // 176 // - ctx: context to use when instantiating the module and calling "run". 177 // - r: runtime to instantiate both the host and guest (compiled) module in. 178 // - compiled: guest binary compiled with `GOARCH=wasm GOOS=js` 179 // - config: the Config to use including wazero.ModuleConfig or extensions of 180 // it. 181 // 182 // # Example 183 // 184 // After compiling your Wasm binary with wazero.Runtime's `CompileModule`, run 185 // it like below: 186 // 187 // // Instantiate host functions needed by gojs 188 // gojs.MustInstantiate(ctx, r) 189 // 190 // // Assign any configuration relevant for your compiled wasm. 191 // config := gojs.NewConfig(wazero.NewConfig()) 192 // 193 // // Run your wasm, notably handing any ExitError 194 // err = gojs.Run(ctx, r, compiled, config) 195 // if exitErr, ok := err.(*sys.ExitError); ok && exitErr.ExitCode() != 0 { 196 // log.Panicln(err) 197 // } else if !ok { 198 // log.Panicln(err) 199 // } 200 // 201 // # Notes 202 // 203 // - Wasm generated by `GOARCH=wasm GOOS=js` is very slow to compile: Use 204 // wazero.RuntimeConfig with wazero.CompilationCache when re-running the 205 // same binary. 206 // - The guest module is closed after being run. 207 func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule, moduleConfig Config) error { 208 c := moduleConfig.(*cfg) 209 _, err := RunAndReturnState(ctx, r, compiled, c.moduleConfig, c.internal) 210 return err 211 }