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  }