github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/imports/emscripten/emscripten.go (about) 1 // Package emscripten contains Go-defined special functions imported by 2 // Emscripten under the module name "env". 3 // 4 // Emscripten has many imports which are triggered on build flags. Use 5 // FunctionExporter, instead of Instantiate, to define more "env" functions. 6 // 7 // # Relationship to WASI 8 // 9 // Emscripten typically requires wasi_snapshot_preview1 to implement exit. 10 // 11 // See wasi_snapshot_preview1.Instantiate and 12 // https://github.com/emscripten-core/emscripten/wiki/WebAssembly-Standalone 13 package emscripten 14 15 import ( 16 "context" 17 "strings" 18 19 wazero "github.com/wasilibs/wazerox" 20 "github.com/wasilibs/wazerox/api" 21 internal "github.com/wasilibs/wazerox/internal/emscripten" 22 "github.com/wasilibs/wazerox/internal/wasm" 23 ) 24 25 const i32 = wasm.ValueTypeI32 26 27 // MustInstantiate calls Instantiate or panics on error. 28 // 29 // This is a simpler function for those who know the module "env" is not 30 // already instantiated, and don't need to unload it. 31 // 32 // Deprecated: Due to Emscripten dynamic import generation, InstantiateForModule should be used instead. 33 func MustInstantiate(ctx context.Context, r wazero.Runtime) { 34 if _, err := Instantiate(ctx, r); err != nil { 35 panic(err) 36 } 37 } 38 39 // Instantiate instantiates the "env" module used by Emscripten into the 40 // runtime. 41 // 42 // # Notes 43 // 44 // - Failure cases are documented on wazero.Runtime InstantiateModule. 45 // - Closing the wazero.Runtime has the same effect as closing the result. 46 // - To add more functions to the "env" module, use FunctionExporter. 47 // 48 // Deprecated: Due to Emscripten dynamic import generation, InstantiateForModule should be used instead. 49 func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) { 50 builder := r.NewHostModuleBuilder("env") 51 NewFunctionExporter().ExportFunctions(builder) 52 return builder.Instantiate(ctx) 53 } 54 55 // FunctionExporter configures the functions in the "env" module used by 56 // Emscripten. 57 // 58 // # Notes 59 // 60 // - This is an interface for decoupling, not third-party implementations. 61 // All implementations are in wazero. 62 type FunctionExporter interface { 63 // ExportFunctions builds functions to export with a wazero.HostModuleBuilder 64 // named "env". 65 ExportFunctions(wazero.HostModuleBuilder) 66 } 67 68 // NewFunctionExporter returns a FunctionExporter object with trace disabled. 69 // Deprecated: Due to Emscripten dynamic import generation, NewFunctionExporterForModule should be used instead. 70 func NewFunctionExporter() FunctionExporter { 71 return &functionExporter{} 72 } 73 74 type functionExporter struct{} 75 76 // ExportFunctions implements FunctionExporter.ExportFunctions 77 func (functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) { 78 exporter := builder.(wasm.HostFuncExporter) 79 exporter.ExportHostFunc(internal.NotifyMemoryGrowth) 80 } 81 82 type emscriptenFns []*wasm.HostFunc 83 84 // InstantiateForModule instantiates a module named "env" populated with any 85 // known functions used in emscripten. 86 func InstantiateForModule(ctx context.Context, r wazero.Runtime, guest wazero.CompiledModule) (api.Closer, error) { 87 // Create the exporter for the supplied wasm 88 exporter, err := NewFunctionExporterForModule(guest) 89 if err != nil { 90 return nil, err 91 } 92 93 // Instantiate it! 94 env := r.NewHostModuleBuilder("env") 95 exporter.ExportFunctions(env) 96 return env.Instantiate(ctx) 97 } 98 99 // NewFunctionExporterForModule returns a guest-specific FunctionExporter, 100 // populated with any known functions used in emscripten. 101 func NewFunctionExporterForModule(guest wazero.CompiledModule) (FunctionExporter, error) { 102 ret := emscriptenFns{} 103 for _, fn := range guest.ImportedFunctions() { 104 importModule, importName, isImport := fn.Import() 105 if !isImport || importModule != "env" { 106 continue // not emscripten 107 } 108 if importName == internal.FunctionNotifyMemoryGrowth { 109 ret = append(ret, internal.NotifyMemoryGrowth) 110 continue 111 } 112 if importName == internal.FunctionThrowLongjmp { 113 ret = append(ret, internal.ThrowLongjmp) 114 continue 115 } 116 if !strings.HasPrefix(importName, internal.InvokePrefix) { 117 continue // not invoke, and maybe not emscripten 118 } 119 120 hf := internal.NewInvokeFunc(importName, fn.ParamTypes(), fn.ResultTypes()) 121 ret = append(ret, hf) 122 } 123 return ret, nil 124 } 125 126 // ExportFunctions implements FunctionExporter.ExportFunctions 127 func (i emscriptenFns) ExportFunctions(builder wazero.HostModuleBuilder) { 128 exporter := builder.(wasm.HostFuncExporter) 129 for _, fn := range i { 130 exporter.ExportHostFunc(fn) 131 } 132 }