github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/gojs/runtime.go (about)

     1  package gojs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/bananabytelabs/wazero/api"
     9  	"github.com/bananabytelabs/wazero/experimental/sys"
    10  	"github.com/bananabytelabs/wazero/internal/gojs/custom"
    11  	"github.com/bananabytelabs/wazero/internal/gojs/goarch"
    12  	"github.com/bananabytelabs/wazero/internal/wasm"
    13  )
    14  
    15  // Debug has unknown use, so stubbed.
    16  //
    17  // See https://github.com/golang/go/blob/go1.20/src/cmd/link/internal/wasm/asm.go#L131-L136
    18  var Debug = goarch.StubFunction(custom.NameDebug)
    19  
    20  // TODO: should this call runtime.Breakpoint()?
    21  
    22  // WasmExit implements runtime.wasmExit which supports runtime.exit.
    23  //
    24  // See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.go#L24
    25  var WasmExit = goarch.NewFunc(custom.NameRuntimeWasmExit, wasmExit)
    26  
    27  func wasmExit(ctx context.Context, mod api.Module, stack goarch.Stack) {
    28  	code := stack.ParamUint32(0)
    29  
    30  	getState(ctx).close()
    31  	_ = mod.CloseWithExitCode(ctx, code)
    32  }
    33  
    34  // WasmWrite implements runtime.wasmWrite which supports runtime.write and
    35  // runtime.writeErr. This implements `println`.
    36  //
    37  // See https://github.com/golang/go/blob/go1.20/src/runtime/os_js.go#L30
    38  var WasmWrite = goarch.NewFunc(custom.NameRuntimeWasmWrite, wasmWrite)
    39  
    40  func wasmWrite(_ context.Context, mod api.Module, stack goarch.Stack) {
    41  	fd := stack.ParamInt32(0)
    42  	p := stack.ParamBytes(mod.Memory(), 1 /*, 2 */)
    43  
    44  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
    45  	if f, ok := fsc.LookupFile(fd); ok {
    46  		_, errno := f.File.Write(p)
    47  		switch errno {
    48  		case 0:
    49  			return // success
    50  		case sys.ENOSYS:
    51  			return // e.g. unimplemented for write
    52  		case sys.EBADF:
    53  			return // e.g. not opened for write
    54  		default:
    55  			panic(fmt.Errorf("error writing p: %w", errno))
    56  		}
    57  	} else {
    58  		panic(fmt.Errorf("fd %d invalid", fd))
    59  	}
    60  }
    61  
    62  // ResetMemoryDataView signals wasm.OpcodeMemoryGrow happened, indicating any
    63  // cached view of memory should be reset.
    64  //
    65  // See https://github.com/golang/go/blob/go1.20/src/runtime/mem_js.go#L82
    66  var ResetMemoryDataView = goarch.NewFunc(custom.NameRuntimeResetMemoryDataView, resetMemoryDataView)
    67  
    68  func resetMemoryDataView(context.Context, api.Module, goarch.Stack) {
    69  	// context state does not cache a memory view, and all byte slices used
    70  	// are safely copied. Also, user-defined functions are not supported.
    71  	// Hence, there's currently no known reason to reset anything.
    72  }
    73  
    74  // Nanotime1 implements runtime.nanotime which supports time.Since.
    75  //
    76  // See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L117
    77  var Nanotime1 = goarch.NewFunc(custom.NameRuntimeNanotime1, nanotime1)
    78  
    79  func nanotime1(_ context.Context, mod api.Module, stack goarch.Stack) {
    80  	nsec := mod.(*wasm.ModuleInstance).Sys.Nanotime()
    81  
    82  	stack.SetResultI64(0, nsec)
    83  }
    84  
    85  // Walltime implements runtime.walltime which supports time.Now.
    86  //
    87  // See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L121
    88  var Walltime = goarch.NewFunc(custom.NameRuntimeWalltime, walltime)
    89  
    90  func walltime(_ context.Context, mod api.Module, stack goarch.Stack) {
    91  	sec, nsec := mod.(*wasm.ModuleInstance).Sys.Walltime()
    92  
    93  	stack.SetResultI64(0, sec)
    94  	stack.SetResultI32(1, nsec)
    95  }
    96  
    97  // ScheduleTimeoutEvent implements runtime.scheduleTimeoutEvent which supports
    98  // runtime.notetsleepg used by runtime.signal_recv.
    99  //
   100  // Unlike other most functions prefixed by "runtime.", this both launches a
   101  // goroutine and invokes code compiled into wasm "resume".
   102  //
   103  // See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L125
   104  var ScheduleTimeoutEvent = goarch.NewFunc(custom.NameRuntimeScheduleTimeoutEvent, scheduleTimeoutEvent)
   105  
   106  // Note: Signal handling is not implemented in GOOS=js.
   107  func scheduleTimeoutEvent(ctx context.Context, mod api.Module, stack goarch.Stack) {
   108  	ms := stack.Param(0)
   109  
   110  	s := getState(ctx)
   111  	id := s._nextCallbackTimeoutID
   112  	stack.SetResultUint32(0, id)
   113  	s._nextCallbackTimeoutID++
   114  
   115  	cleared := make(chan bool)
   116  	timeout := time.Millisecond * time.Duration(ms)
   117  	s._scheduledTimeouts[id] = cleared
   118  
   119  	// As wasm is currently not concurrent, a timeout on another goroutine may
   120  	// not make sense. However, this implements what wasm_exec.js does anyway.
   121  	go func() {
   122  		select {
   123  		case <-cleared: // do nothing
   124  		case <-time.After(timeout):
   125  			if _, err := mod.ExportedFunction("resume").Call(ctx); err != nil {
   126  				println(err)
   127  			}
   128  		}
   129  	}()
   130  }
   131  
   132  // ClearTimeoutEvent implements runtime.clearTimeoutEvent which supports
   133  // runtime.notetsleepg used by runtime.signal_recv.
   134  //
   135  // See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L129
   136  var ClearTimeoutEvent = goarch.NewFunc(custom.NameRuntimeClearTimeoutEvent, clearTimeoutEvent)
   137  
   138  // Note: Signal handling is not implemented in GOOS=js.
   139  func clearTimeoutEvent(ctx context.Context, _ api.Module, stack goarch.Stack) {
   140  	id := stack.ParamUint32(0)
   141  	s := getState(ctx)
   142  	if cancel, ok := s._scheduledTimeouts[id]; ok {
   143  		delete(s._scheduledTimeouts, id)
   144  		cancel <- true
   145  	}
   146  }
   147  
   148  // GetRandomData implements runtime.getRandomData, which initializes the seed
   149  // for runtime.fastrand.
   150  //
   151  // See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L133
   152  var GetRandomData = goarch.NewFunc(custom.NameRuntimeGetRandomData, getRandomData)
   153  
   154  func getRandomData(_ context.Context, mod api.Module, stack goarch.Stack) {
   155  	r := stack.ParamBytes(mod.Memory(), 0 /*, 1 */)
   156  
   157  	randSource := mod.(*wasm.ModuleInstance).Sys.RandSource()
   158  
   159  	bufLen := len(r)
   160  	if n, err := randSource.Read(r); err != nil {
   161  		panic(fmt.Errorf("RandSource.Read(r /* len=%d */) failed: %w", bufLen, err))
   162  	} else if n != bufLen {
   163  		panic(fmt.Errorf("RandSource.Read(r /* len=%d */) read %d bytes", bufLen, n))
   164  	}
   165  }