github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/experimental/wazerotest/wazerotest.go (about)

     1  package wazerotest
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"math"
     9  	"reflect"
    10  	"strconv"
    11  	"sync"
    12  	"sync/atomic"
    13  
    14  	"github.com/wasilibs/wazerox/api"
    15  	"github.com/wasilibs/wazerox/internal/internalapi"
    16  	"github.com/wasilibs/wazerox/sys"
    17  )
    18  
    19  const (
    20  	exitStatusMarker = 1 << 63
    21  )
    22  
    23  // Module is an implementation of the api.Module interface, it represents a
    24  // WebAssembly module.
    25  type Module struct {
    26  	internalapi.WazeroOnlyType
    27  
    28  	// The module name that will be returned by calling the Name method.
    29  	ModuleName string
    30  
    31  	// The list of functions of the module. Functions with a non-empty export
    32  	// names will be exported by the module.
    33  	Functions []*Function
    34  
    35  	// The list of globals of the module. Global
    36  	Globals []*Global
    37  
    38  	// The program memory. If non-nil, the memory is automatically exported as
    39  	// "memory".
    40  	ExportMemory *Memory
    41  
    42  	exitStatus atomic.Uint64
    43  
    44  	once                        sync.Once
    45  	exportedFunctions           map[string]api.Function
    46  	exportedFunctionDefinitions map[string]api.FunctionDefinition
    47  	exportedGlobals             map[string]api.Global
    48  	exportedMemoryDefinitions   map[string]api.MemoryDefinition
    49  }
    50  
    51  // NewModule constructs a Module object with the given memory and function list.
    52  func NewModule(memory *Memory, functions ...*Function) *Module {
    53  	return &Module{Functions: functions, ExportMemory: memory}
    54  }
    55  
    56  // String implements fmt.Stringer.
    57  func (m *Module) String() string {
    58  	return "module[" + m.ModuleName + "]"
    59  }
    60  
    61  // Name implements the same method as documented on api.Module.
    62  func (m *Module) Name() string {
    63  	return m.ModuleName
    64  }
    65  
    66  // Memory implements the same method as documented on api.Module.
    67  func (m *Module) Memory() api.Memory {
    68  	if m.ExportMemory != nil {
    69  		m.once.Do(m.initialize)
    70  		return m.ExportMemory
    71  	}
    72  	return nil
    73  }
    74  
    75  // ExportedFunction implements the same method as documented on api.Module.
    76  func (m *Module) ExportedFunction(name string) api.Function {
    77  	m.once.Do(m.initialize)
    78  	return m.exportedFunctions[name]
    79  }
    80  
    81  // ExportedFunctionDefinitions implements the same method as documented on api.Module.
    82  func (m *Module) ExportedFunctionDefinitions() map[string]api.FunctionDefinition {
    83  	m.once.Do(m.initialize)
    84  	return m.exportedFunctionDefinitions
    85  }
    86  
    87  // ExportedMemory implements the same method as documented on api.Module.
    88  func (m *Module) ExportedMemory(name string) api.Memory {
    89  	if m.ExportMemory != nil && name == "memory" {
    90  		m.once.Do(m.initialize)
    91  		return m.ExportMemory
    92  	}
    93  	return nil
    94  }
    95  
    96  // ExportedMemoryDefinitions implements the same method as documented on api.Module.
    97  func (m *Module) ExportedMemoryDefinitions() map[string]api.MemoryDefinition {
    98  	m.once.Do(m.initialize)
    99  	return m.exportedMemoryDefinitions
   100  }
   101  
   102  // ExportedGlobal implements the same method as documented on api.Module.
   103  func (m *Module) ExportedGlobal(name string) api.Global {
   104  	m.once.Do(m.initialize)
   105  	return m.exportedGlobals[name]
   106  }
   107  
   108  // Close implements the same method as documented on api.Closer.
   109  func (m *Module) Close(ctx context.Context) error {
   110  	return m.CloseWithExitCode(ctx, 0)
   111  }
   112  
   113  // CloseWithExitCode implements the same method as documented on api.Closer.
   114  func (m *Module) CloseWithExitCode(ctx context.Context, exitCode uint32) error {
   115  	m.exitStatus.CompareAndSwap(0, exitStatusMarker|uint64(exitCode))
   116  	return nil
   117  }
   118  
   119  // IsClosed implements the same method as documented on api.Module.
   120  func (m *Module) IsClosed() bool {
   121  	_, exited := m.ExitStatus()
   122  	return exited
   123  }
   124  
   125  // NumGlobal implements the same method as documented on experimental.InternalModule.
   126  func (m *Module) NumGlobal() int {
   127  	return len(m.Globals)
   128  }
   129  
   130  // Global implements the same method as documented on experimental.InternalModule.
   131  func (m *Module) Global(i int) api.Global {
   132  	m.once.Do(m.initialize)
   133  	return m.Globals[i]
   134  }
   135  
   136  // Below are undocumented extensions
   137  
   138  func (m *Module) NumFunction() int {
   139  	return len(m.Functions)
   140  }
   141  
   142  func (m *Module) Function(i int) api.Function {
   143  	m.once.Do(m.initialize)
   144  	return m.Functions[i]
   145  }
   146  
   147  func (m *Module) ExitStatus() (exitCode uint32, exited bool) {
   148  	exitStatus := m.exitStatus.Load()
   149  	return uint32(exitStatus), exitStatus != 0
   150  }
   151  
   152  func (m *Module) initialize() {
   153  	m.exportedFunctions = make(map[string]api.Function)
   154  	m.exportedFunctionDefinitions = make(map[string]api.FunctionDefinition)
   155  	m.exportedGlobals = make(map[string]api.Global)
   156  	m.exportedMemoryDefinitions = make(map[string]api.MemoryDefinition)
   157  
   158  	for index, function := range m.Functions {
   159  		for _, exportName := range function.ExportNames {
   160  			m.exportedFunctions[exportName] = function
   161  			m.exportedFunctionDefinitions[exportName] = function.Definition()
   162  		}
   163  		function.module = m
   164  		function.index = index
   165  	}
   166  
   167  	for _, global := range m.Globals {
   168  		for _, exportName := range global.ExportNames {
   169  			m.exportedGlobals[exportName] = global
   170  		}
   171  	}
   172  
   173  	if m.ExportMemory != nil {
   174  		m.ExportMemory.module = m
   175  		m.exportedMemoryDefinitions["memory"] = m.ExportMemory.Definition()
   176  	}
   177  }
   178  
   179  // Global is an implementation of the api.Global interface, it represents a
   180  // global in a WebAssembly module.
   181  type Global struct {
   182  	internalapi.WazeroOnlyType
   183  
   184  	// Type of the global value, used to interpret bits of the Value field.
   185  	ValueType api.ValueType
   186  
   187  	// Value of the global packed in a 64 bits field.
   188  	Value uint64
   189  
   190  	// List of names that the globla is exported as.
   191  	ExportNames []string
   192  }
   193  
   194  func (g *Global) String() string {
   195  	switch g.ValueType {
   196  	case api.ValueTypeI32:
   197  		return strconv.FormatInt(int64(api.DecodeI32(g.Value)), 10)
   198  	case api.ValueTypeI64:
   199  		return strconv.FormatInt(int64(g.Value), 10)
   200  	case api.ValueTypeF32:
   201  		return strconv.FormatFloat(float64(api.DecodeF32(g.Value)), 'g', -1, 32)
   202  	case api.ValueTypeF64:
   203  		return strconv.FormatFloat(api.DecodeF64(g.Value), 'g', -1, 64)
   204  	default:
   205  		return "0x" + strconv.FormatUint(g.Value, 16)
   206  	}
   207  }
   208  
   209  func (g *Global) Type() api.ValueType {
   210  	return g.ValueType
   211  }
   212  
   213  func (g *Global) Get() uint64 {
   214  	return g.Value
   215  }
   216  
   217  func GlobalI32(value int32, export ...string) *Global {
   218  	return &Global{ValueType: api.ValueTypeI32, Value: api.EncodeI32(value), ExportNames: export}
   219  }
   220  
   221  func GlobalI64(value int64, export ...string) *Global {
   222  	return &Global{ValueType: api.ValueTypeI64, Value: api.EncodeI64(value), ExportNames: export}
   223  }
   224  
   225  func GlobalF32(value float32, export ...string) *Global {
   226  	return &Global{ValueType: api.ValueTypeF32, Value: api.EncodeF32(value), ExportNames: export}
   227  }
   228  
   229  func GlobalF64(value float64, export ...string) *Global {
   230  	return &Global{ValueType: api.ValueTypeF64, Value: api.EncodeF64(value), ExportNames: export}
   231  }
   232  
   233  // Function is an implementation of the api.Function interface, it represents
   234  // a function in a WebAssembly module.
   235  //
   236  // Until accessed through a Module's method, the function definition's
   237  // ModuleName method returns an empty string and its Index method returns 0.
   238  type Function struct {
   239  	internalapi.WazeroOnlyType
   240  
   241  	// GoModuleFunction may be set to a non-nil value to allow calling of the
   242  	// function via Call or CallWithStack.
   243  	//
   244  	// It is the user's responsibility to ensure that the signature of this
   245  	// implementation matches the ParamTypes and ResultTypes fields.
   246  	GoModuleFunction api.GoModuleFunction
   247  
   248  	// Type lists representing the function signature. Those fields should be
   249  	// set for the function to be properly constructed. The Function's Call
   250  	// and CallWithStack methods will error if those fields are nil.
   251  	ParamTypes  []api.ValueType
   252  	ResultTypes []api.ValueType
   253  
   254  	// Sets of names associated with the function. It is valid to leave those
   255  	// names empty, they are only used for debugging purposes.
   256  	FunctionName string
   257  	DebugName    string
   258  	ParamNames   []string
   259  	ResultNames  []string
   260  	ExportNames  []string
   261  
   262  	// Lazily initialized when accessed through the module.
   263  	module *Module
   264  	index  int
   265  }
   266  
   267  // NewFunction constructs a Function object from a Go function.
   268  //
   269  // The function fn must accept at least two arguments of type context.Context
   270  // and api.Module. Any other arguments and return values must be of type uint32,
   271  // uint64, int32, int64, float32, or float64. The call panics if fn is not a Go
   272  // functionn or has an unsupported signature.
   273  func NewFunction(fn any) *Function {
   274  	functionType := reflect.TypeOf(fn)
   275  	functionValue := reflect.ValueOf(fn)
   276  
   277  	paramTypes := make([]api.ValueType, functionType.NumIn()-2)
   278  	paramFuncs := make([]func(uint64) reflect.Value, len(paramTypes))
   279  
   280  	resultTypes := make([]api.ValueType, functionType.NumOut())
   281  	resultFuncs := make([]func(reflect.Value) uint64, len(resultTypes))
   282  
   283  	for i := range paramTypes {
   284  		var paramType api.ValueType
   285  		var paramFunc func(uint64) reflect.Value
   286  
   287  		switch functionType.In(i + 2).Kind() {
   288  		case reflect.Uint32:
   289  			paramType = api.ValueTypeI32
   290  			paramFunc = func(v uint64) reflect.Value { return reflect.ValueOf(api.DecodeU32(v)) }
   291  		case reflect.Uint64:
   292  			paramType = api.ValueTypeI64
   293  			paramFunc = func(v uint64) reflect.Value { return reflect.ValueOf(v) }
   294  		case reflect.Int32:
   295  			paramType = api.ValueTypeI32
   296  			paramFunc = func(v uint64) reflect.Value { return reflect.ValueOf(api.DecodeI32(v)) }
   297  		case reflect.Int64:
   298  			paramType = api.ValueTypeI64
   299  			paramFunc = func(v uint64) reflect.Value { return reflect.ValueOf(int64(v)) }
   300  		case reflect.Float32:
   301  			paramType = api.ValueTypeF32
   302  			paramFunc = func(v uint64) reflect.Value { return reflect.ValueOf(api.DecodeF32(v)) }
   303  		case reflect.Float64:
   304  			paramType = api.ValueTypeF64
   305  			paramFunc = func(v uint64) reflect.Value { return reflect.ValueOf(api.DecodeF64(v)) }
   306  		default:
   307  			panic("cannot construct wasm function from go function of type " + functionType.String())
   308  		}
   309  
   310  		paramTypes[i] = paramType
   311  		paramFuncs[i] = paramFunc
   312  	}
   313  
   314  	for i := range resultTypes {
   315  		var resultType api.ValueType
   316  		var resultFunc func(reflect.Value) uint64
   317  
   318  		switch functionType.Out(i).Kind() {
   319  		case reflect.Uint32:
   320  			resultType = api.ValueTypeI32
   321  			resultFunc = func(v reflect.Value) uint64 { return v.Uint() }
   322  		case reflect.Uint64:
   323  			resultType = api.ValueTypeI64
   324  			resultFunc = func(v reflect.Value) uint64 { return v.Uint() }
   325  		case reflect.Int32:
   326  			resultType = api.ValueTypeI32
   327  			resultFunc = func(v reflect.Value) uint64 { return api.EncodeI32(int32(v.Int())) }
   328  		case reflect.Int64:
   329  			resultType = api.ValueTypeI64
   330  			resultFunc = func(v reflect.Value) uint64 { return api.EncodeI64(v.Int()) }
   331  		case reflect.Float32:
   332  			resultType = api.ValueTypeF32
   333  			resultFunc = func(v reflect.Value) uint64 { return api.EncodeF32(float32(v.Float())) }
   334  		case reflect.Float64:
   335  			resultType = api.ValueTypeF64
   336  			resultFunc = func(v reflect.Value) uint64 { return api.EncodeF64(v.Float()) }
   337  		default:
   338  			panic("cannot construct wasm function from go function of type " + functionType.String())
   339  		}
   340  
   341  		resultTypes[i] = resultType
   342  		resultFuncs[i] = resultFunc
   343  	}
   344  
   345  	return &Function{
   346  		GoModuleFunction: api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) {
   347  			in := make([]reflect.Value, 2+len(paramFuncs))
   348  			in[0] = reflect.ValueOf(ctx)
   349  			in[1] = reflect.ValueOf(mod)
   350  			for i, param := range paramFuncs {
   351  				in[i+2] = param(stack[i])
   352  			}
   353  			out := functionValue.Call(in)
   354  			for i, result := range resultFuncs {
   355  				stack[i] = result(out[i])
   356  			}
   357  		}),
   358  		ParamTypes:  paramTypes,
   359  		ResultTypes: resultTypes,
   360  	}
   361  }
   362  
   363  var (
   364  	errMissingFunctionSignature      = errors.New("missing function signature")
   365  	errMissingFunctionModule         = errors.New("missing function module")
   366  	errMissingFunctionImplementation = errors.New("missing function implementation")
   367  )
   368  
   369  func (f *Function) Definition() api.FunctionDefinition {
   370  	return functionDefinition{function: f}
   371  }
   372  
   373  func (f *Function) Call(ctx context.Context, params ...uint64) ([]uint64, error) {
   374  	stackLen := len(f.ParamTypes)
   375  	if stackLen < len(f.ResultTypes) {
   376  		stackLen = len(f.ResultTypes)
   377  	}
   378  	stack := make([]uint64, stackLen)
   379  	copy(stack, params)
   380  	err := f.CallWithStack(ctx, stack)
   381  	if err != nil {
   382  		for i := range stack {
   383  			stack[i] = 0
   384  		}
   385  	}
   386  	return stack[:len(f.ResultTypes)], err
   387  }
   388  
   389  func (f *Function) CallWithStack(ctx context.Context, stack []uint64) error {
   390  	if f.ParamTypes == nil || f.ResultTypes == nil {
   391  		return errMissingFunctionSignature
   392  	}
   393  	if f.GoModuleFunction == nil {
   394  		return errMissingFunctionImplementation
   395  	}
   396  	if f.module == nil {
   397  		return errMissingFunctionModule
   398  	}
   399  	if exitCode, exited := f.module.ExitStatus(); exited {
   400  		return sys.NewExitError(exitCode)
   401  	}
   402  	f.GoModuleFunction.Call(ctx, f.module, stack)
   403  	return nil
   404  }
   405  
   406  type functionDefinition struct {
   407  	internalapi.WazeroOnlyType
   408  	function *Function
   409  }
   410  
   411  func (def functionDefinition) Name() string {
   412  	return def.function.FunctionName
   413  }
   414  
   415  func (def functionDefinition) DebugName() string {
   416  	if def.function.DebugName != "" {
   417  		return def.function.DebugName
   418  	}
   419  	return fmt.Sprintf("%s.$%d", def.ModuleName(), def.Index())
   420  }
   421  
   422  func (def functionDefinition) GoFunction() any {
   423  	return def.function.GoModuleFunction
   424  }
   425  
   426  func (def functionDefinition) ParamTypes() []api.ValueType {
   427  	return def.function.ParamTypes
   428  }
   429  
   430  func (def functionDefinition) ParamNames() []string {
   431  	return def.function.ParamNames
   432  }
   433  
   434  func (def functionDefinition) ResultTypes() []api.ValueType {
   435  	return def.function.ResultTypes
   436  }
   437  
   438  func (def functionDefinition) ResultNames() []string {
   439  	return def.function.ResultNames
   440  }
   441  
   442  func (def functionDefinition) ModuleName() string {
   443  	if def.function.module != nil {
   444  		return def.function.module.ModuleName
   445  	}
   446  	return ""
   447  }
   448  
   449  func (def functionDefinition) Index() uint32 {
   450  	return uint32(def.function.index)
   451  }
   452  
   453  func (def functionDefinition) Import() (moduleName, name string, isImport bool) {
   454  	return
   455  }
   456  
   457  func (def functionDefinition) ExportNames() []string {
   458  	return def.function.ExportNames
   459  }
   460  
   461  // Memory is an implementation of the api.Memory interface, representing the
   462  // memory of a WebAssembly module.
   463  type Memory struct {
   464  	internalapi.WazeroOnlyType
   465  
   466  	// Byte slices holding the memory pages.
   467  	//
   468  	// It is the user's repsonsibility to ensure that the length of this byte
   469  	// slice is a multiple of the page size.
   470  	Bytes []byte
   471  
   472  	// Min and max number of memory pages which may be held in this memory.
   473  	//
   474  	// Leaving Max to zero means no upper bound.
   475  	Min uint32
   476  	Max uint32
   477  
   478  	// Lazily initialized when accessed through the module.
   479  	module *Module
   480  }
   481  
   482  // NewMemory constructs a Memory object with a buffer of the given size, aligned
   483  // to the closest multiple of the page size.
   484  func NewMemory(size int) *Memory {
   485  	numPages := (size + (PageSize - 1)) / PageSize
   486  	return &Memory{
   487  		Bytes: make([]byte, numPages*PageSize),
   488  		Min:   uint32(numPages),
   489  	}
   490  }
   491  
   492  // NewFixedMemory constructs a Memory object of the given size. The returned
   493  // memory is configured with a max limit to prevent growing beyond its initial
   494  // size.
   495  func NewFixedMemory(size int) *Memory {
   496  	memory := NewMemory(size)
   497  	memory.Max = memory.Min
   498  	return memory
   499  }
   500  
   501  // The PageSize constant defines the size of WebAssembly memory pages in bytes.
   502  //
   503  // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#page-size
   504  const PageSize = 65536
   505  
   506  func (m *Memory) Definition() api.MemoryDefinition {
   507  	return memoryDefinition{memory: m}
   508  }
   509  
   510  func (m *Memory) Size() uint32 {
   511  	return uint32(len(m.Bytes))
   512  }
   513  
   514  func (m *Memory) Grow(deltaPages uint32) (previousPages uint32, ok bool) {
   515  	previousPages = uint32(len(m.Bytes) / PageSize)
   516  	numPages := previousPages + deltaPages
   517  	if m.Max != 0 && numPages > m.Max {
   518  		return previousPages, false
   519  	}
   520  	bytes := make([]byte, PageSize*numPages)
   521  	copy(bytes, m.Bytes)
   522  	m.Bytes = bytes
   523  	return previousPages, true
   524  }
   525  
   526  func (m *Memory) ReadByte(offset uint32) (byte, bool) {
   527  	if m.isOutOfRange(offset, 1) {
   528  		return 0, false
   529  	}
   530  	return m.Bytes[offset], true
   531  }
   532  
   533  func (m *Memory) ReadUint16Le(offset uint32) (uint16, bool) {
   534  	if m.isOutOfRange(offset, 2) {
   535  		return 0, false
   536  	}
   537  	return binary.LittleEndian.Uint16(m.Bytes[offset:]), true
   538  }
   539  
   540  func (m *Memory) ReadUint32Le(offset uint32) (uint32, bool) {
   541  	if m.isOutOfRange(offset, 4) {
   542  		return 0, false
   543  	}
   544  	return binary.LittleEndian.Uint32(m.Bytes[offset:]), true
   545  }
   546  
   547  func (m *Memory) ReadUint64Le(offset uint32) (uint64, bool) {
   548  	if m.isOutOfRange(offset, 8) {
   549  		return 0, false
   550  	}
   551  	return binary.LittleEndian.Uint64(m.Bytes[offset:]), true
   552  }
   553  
   554  func (m *Memory) ReadFloat32Le(offset uint32) (float32, bool) {
   555  	v, ok := m.ReadUint32Le(offset)
   556  	return math.Float32frombits(v), ok
   557  }
   558  
   559  func (m *Memory) ReadFloat64Le(offset uint32) (float64, bool) {
   560  	v, ok := m.ReadUint64Le(offset)
   561  	return math.Float64frombits(v), ok
   562  }
   563  
   564  func (m *Memory) Read(offset, length uint32) ([]byte, bool) {
   565  	if m.isOutOfRange(offset, length) {
   566  		return nil, false
   567  	}
   568  	return m.Bytes[offset : offset+length : offset+length], true
   569  }
   570  
   571  func (m *Memory) WriteByte(offset uint32, value byte) bool {
   572  	if m.isOutOfRange(offset, 1) {
   573  		return false
   574  	}
   575  	m.Bytes[offset] = value
   576  	return true
   577  }
   578  
   579  func (m *Memory) WriteUint16Le(offset uint32, value uint16) bool {
   580  	if m.isOutOfRange(offset, 2) {
   581  		return false
   582  	}
   583  	binary.LittleEndian.PutUint16(m.Bytes[offset:], value)
   584  	return true
   585  }
   586  
   587  func (m *Memory) WriteUint32Le(offset uint32, value uint32) bool {
   588  	if m.isOutOfRange(offset, 4) {
   589  		return false
   590  	}
   591  	binary.LittleEndian.PutUint32(m.Bytes[offset:], value)
   592  	return true
   593  }
   594  
   595  func (m *Memory) WriteUint64Le(offset uint32, value uint64) bool {
   596  	if m.isOutOfRange(offset, 4) {
   597  		return false
   598  	}
   599  	binary.LittleEndian.PutUint64(m.Bytes[offset:], value)
   600  	return true
   601  }
   602  
   603  func (m *Memory) WriteFloat32Le(offset uint32, value float32) bool {
   604  	return m.WriteUint32Le(offset, math.Float32bits(value))
   605  }
   606  
   607  func (m *Memory) WriteFloat64Le(offset uint32, value float64) bool {
   608  	return m.WriteUint64Le(offset, math.Float64bits(value))
   609  }
   610  
   611  func (m *Memory) Write(offset uint32, value []byte) bool {
   612  	if m.isOutOfRange(offset, uint32(len(value))) {
   613  		return false
   614  	}
   615  	copy(m.Bytes[offset:], value)
   616  	return true
   617  }
   618  
   619  func (m *Memory) WriteString(offset uint32, value string) bool {
   620  	if m.isOutOfRange(offset, uint32(len(value))) {
   621  		return false
   622  	}
   623  	copy(m.Bytes[offset:], value)
   624  	return true
   625  }
   626  
   627  func (m *Memory) isOutOfRange(offset, length uint32) bool {
   628  	size := m.Size()
   629  	return offset >= size || length > size || offset > (size-length)
   630  }
   631  
   632  type memoryDefinition struct {
   633  	internalapi.WazeroOnlyType
   634  	memory *Memory
   635  }
   636  
   637  func (def memoryDefinition) ModuleName() string {
   638  	if def.memory.module != nil {
   639  		return def.memory.module.ModuleName
   640  	}
   641  	return ""
   642  }
   643  
   644  func (def memoryDefinition) Index() uint32 {
   645  	return 0
   646  }
   647  
   648  func (def memoryDefinition) Import() (moduleName, name string, isImport bool) {
   649  	return
   650  }
   651  
   652  func (def memoryDefinition) ExportNames() []string {
   653  	if def.memory.module != nil {
   654  		return []string{"memory"}
   655  	}
   656  	return nil
   657  }
   658  
   659  func (def memoryDefinition) Min() uint32 {
   660  	return def.memory.Min
   661  }
   662  
   663  func (def memoryDefinition) Max() (uint32, bool) {
   664  	return def.memory.Max, def.memory.Max != 0
   665  }
   666  
   667  var (
   668  	_ api.Module   = (*Module)(nil)
   669  	_ api.Function = (*Function)(nil)
   670  	_ api.Global   = (*Global)(nil)
   671  )