github.com/tetratelabs/wazero@v1.2.1/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  	"github.com/tetratelabs/wazero"
    20  	"github.com/tetratelabs/wazero/api"
    21  	internal "github.com/tetratelabs/wazero/internal/emscripten"
    22  	"github.com/tetratelabs/wazero/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  func MustInstantiate(ctx context.Context, r wazero.Runtime) {
    32  	if _, err := Instantiate(ctx, r); err != nil {
    33  		panic(err)
    34  	}
    35  }
    36  
    37  // Instantiate instantiates the "env" module used by Emscripten into the
    38  // runtime.
    39  //
    40  // # Notes
    41  //
    42  //   - Failure cases are documented on wazero.Runtime InstantiateModule.
    43  //   - Closing the wazero.Runtime has the same effect as closing the result.
    44  //   - To add more functions to the "env" module, use FunctionExporter.
    45  func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
    46  	builder := r.NewHostModuleBuilder("env")
    47  	NewFunctionExporter().ExportFunctions(builder)
    48  	return builder.Instantiate(ctx)
    49  }
    50  
    51  // FunctionExporter configures the functions in the "env" module used by
    52  // Emscripten.
    53  //
    54  // # Notes
    55  //
    56  //   - This is an interface for decoupling, not third-party implementations.
    57  //     All implementations are in wazero.
    58  type FunctionExporter interface {
    59  	// ExportFunctions builds functions to export with a wazero.HostModuleBuilder
    60  	// named "env".
    61  	ExportFunctions(wazero.HostModuleBuilder)
    62  }
    63  
    64  // NewFunctionExporter returns a FunctionExporter object with trace disabled.
    65  func NewFunctionExporter() FunctionExporter {
    66  	return &functionExporter{}
    67  }
    68  
    69  type functionExporter struct{}
    70  
    71  // ExportFunctions implements FunctionExporter.ExportFunctions
    72  func (functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) {
    73  	exporter := builder.(wasm.HostFuncExporter)
    74  	exporter.ExportHostFunc(internal.NotifyMemoryGrowth)
    75  }
    76  
    77  type emscriptenFns []*wasm.HostFunc
    78  
    79  // InstantiateForModule instantiates a module named "env" populated with any
    80  // known functions used in emscripten.
    81  func InstantiateForModule(ctx context.Context, r wazero.Runtime, guest wazero.CompiledModule) (api.Closer, error) {
    82  	// Create the exporter for the supplied wasm
    83  	exporter, err := NewFunctionExporterForModule(guest)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	// Instantiate it!
    89  	env := r.NewHostModuleBuilder("env")
    90  	exporter.ExportFunctions(env)
    91  	return env.Instantiate(ctx)
    92  }
    93  
    94  // NewFunctionExporterForModule returns a guest-specific FunctionExporter,
    95  // populated with any known functions used in emscripten.
    96  func NewFunctionExporterForModule(guest wazero.CompiledModule) (FunctionExporter, error) {
    97  	ret := emscriptenFns{}
    98  	for _, fn := range guest.ImportedFunctions() {
    99  		importModule, importName, isImport := fn.Import()
   100  		if !isImport || importModule != "env" {
   101  			continue // not emscripten
   102  		}
   103  		if importName == internal.FunctionNotifyMemoryGrowth {
   104  			ret = append(ret, internal.NotifyMemoryGrowth)
   105  			continue
   106  		}
   107  		if !strings.HasPrefix(importName, internal.InvokePrefix) {
   108  			continue // not invoke, and maybe not emscripten
   109  		}
   110  
   111  		hf := internal.NewInvokeFunc(importName, fn.ParamTypes(), fn.ResultTypes())
   112  		ret = append(ret, hf)
   113  	}
   114  	return ret, nil
   115  }
   116  
   117  // ExportFunctions implements FunctionExporter.ExportFunctions
   118  func (i emscriptenFns) ExportFunctions(builder wazero.HostModuleBuilder) {
   119  	exporter := builder.(wasm.HostFuncExporter)
   120  	for _, fn := range i {
   121  		exporter.ExportHostFunc(fn)
   122  	}
   123  }