wa-lang.org/wazero@v1.0.2/internal/gojs/runtime.go (about)

     1  package gojs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  
     8  	"wa-lang.org/wazero/api"
     9  	"wa-lang.org/wazero/internal/gojs/spfunc"
    10  	"wa-lang.org/wazero/internal/wasm"
    11  )
    12  
    13  const (
    14  	i32, i64 = api.ValueTypeI32, api.ValueTypeI64
    15  
    16  	functionWasmExit             = "runtime.wasmExit"
    17  	functionWasmWrite            = "runtime.wasmWrite"
    18  	functionResetMemoryDataView  = "runtime.resetMemoryDataView"
    19  	functionNanotime1            = "runtime.nanotime1"
    20  	functionWalltime             = "runtime.walltime"
    21  	functionScheduleTimeoutEvent = "runtime.scheduleTimeoutEvent" // TODO: trigger usage
    22  	functionClearTimeoutEvent    = "runtime.clearTimeoutEvent"    // TODO: trigger usage
    23  	functionGetRandomData        = "runtime.getRandomData"
    24  )
    25  
    26  // WasmExit implements runtime.wasmExit which supports runtime.exit.
    27  //
    28  // See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.go#L28
    29  var WasmExit = spfunc.MustCallFromSP(false, &wasm.HostFunc{
    30  	ExportNames: []string{functionWasmExit},
    31  	Name:        functionWasmExit,
    32  	ParamTypes:  []api.ValueType{i32},
    33  	ParamNames:  []string{"code"},
    34  	Code: &wasm.Code{
    35  		IsHostFunction: true,
    36  		GoFunc:         api.GoModuleFunc(wasmExit),
    37  	},
    38  })
    39  
    40  func wasmExit(ctx context.Context, mod api.Module, stack []uint64) {
    41  	code := uint32(stack[0])
    42  
    43  	getState(ctx).clear()
    44  	_ = mod.CloseWithExitCode(ctx, code) // TODO: should ours be signed bit (like -1 == 255)?
    45  }
    46  
    47  // WasmWrite implements runtime.wasmWrite which supports runtime.write and
    48  // runtime.writeErr. This implements `println`.
    49  //
    50  // See https://github.com/golang/go/blob/go1.19/src/runtime/os_js.go#L29
    51  var WasmWrite = spfunc.MustCallFromSP(false, &wasm.HostFunc{
    52  	ExportNames: []string{functionWasmWrite},
    53  	Name:        functionWasmWrite,
    54  	ParamTypes:  []api.ValueType{i32, i32, i32},
    55  	ParamNames:  []string{"fd", "p", "n"},
    56  	Code: &wasm.Code{
    57  		IsHostFunction: true,
    58  		GoFunc:         api.GoModuleFunc(wasmWrite),
    59  	},
    60  })
    61  
    62  func wasmWrite(ctx context.Context, mod api.Module, stack []uint64) {
    63  	fd, p, n := uint32(stack[0]), uint32(stack[1]), uint32(stack[2])
    64  
    65  	var writer io.Writer
    66  
    67  	switch fd {
    68  	case 1:
    69  		writer = mod.(*wasm.CallContext).Sys.Stdout()
    70  	case 2:
    71  		writer = mod.(*wasm.CallContext).Sys.Stderr()
    72  	default:
    73  		// Keep things simple by expecting nothing past 2
    74  		panic(fmt.Errorf("unexpected fd %d", fd))
    75  	}
    76  
    77  	if _, err := writer.Write(mustRead(ctx, mod.Memory(), "p", p, n)); err != nil {
    78  		panic(fmt.Errorf("error writing p: %w", err))
    79  	}
    80  }
    81  
    82  // ResetMemoryDataView signals wasm.OpcodeMemoryGrow happened, indicating any
    83  // cached view of memory should be reset.
    84  //
    85  // See https://github.com/golang/go/blob/go1.19/src/runtime/mem_js.go#L82
    86  var ResetMemoryDataView = &wasm.HostFunc{
    87  	ExportNames: []string{functionResetMemoryDataView},
    88  	Name:        functionResetMemoryDataView,
    89  	ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32},
    90  	ParamNames:  []string{parameterSp},
    91  	// TODO: Compiler-based memory.grow callbacks are ignored until we have a generic solution #601
    92  	Code: &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeEnd}},
    93  }
    94  
    95  // Nanotime1 implements runtime.nanotime which supports time.Since.
    96  //
    97  // See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L184
    98  var Nanotime1 = spfunc.MustCallFromSP(false, &wasm.HostFunc{
    99  	ExportNames: []string{functionNanotime1},
   100  	Name:        functionNanotime1,
   101  	ResultTypes: []api.ValueType{i64},
   102  	Code: &wasm.Code{
   103  		IsHostFunction: true,
   104  		GoFunc:         api.GoModuleFunc(nanotime1),
   105  	},
   106  })
   107  
   108  func nanotime1(ctx context.Context, mod api.Module, stack []uint64) {
   109  	time := mod.(*wasm.CallContext).Sys.Nanotime(ctx)
   110  	stack[0] = api.EncodeI64(time)
   111  }
   112  
   113  // Walltime implements runtime.walltime which supports time.Now.
   114  //
   115  // See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L188
   116  var Walltime = spfunc.MustCallFromSP(false, &wasm.HostFunc{
   117  	ExportNames: []string{functionWalltime},
   118  	Name:        functionWalltime,
   119  	ResultTypes: []api.ValueType{i64, i32},
   120  	Code: &wasm.Code{
   121  		IsHostFunction: true,
   122  		GoFunc:         api.GoModuleFunc(walltime),
   123  	},
   124  })
   125  
   126  func walltime(ctx context.Context, mod api.Module, stack []uint64) {
   127  	sec, nsec := mod.(*wasm.CallContext).Sys.Walltime(ctx)
   128  	stack[0] = api.EncodeI64(sec)
   129  	stack[1] = api.EncodeI32(nsec)
   130  }
   131  
   132  // ScheduleTimeoutEvent implements runtime.scheduleTimeoutEvent which supports
   133  // runtime.notetsleepg used by runtime.signal_recv.
   134  //
   135  // Unlike other most functions prefixed by "runtime.", this both launches a
   136  // goroutine and invokes code compiled into wasm "resume".
   137  //
   138  // See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L192
   139  var ScheduleTimeoutEvent = stubFunction(functionScheduleTimeoutEvent)
   140  
   141  // ^^ stubbed because signal handling is not implemented in GOOS=js
   142  
   143  // ClearTimeoutEvent implements runtime.clearTimeoutEvent which supports
   144  // runtime.notetsleepg used by runtime.signal_recv.
   145  //
   146  // See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L196
   147  var ClearTimeoutEvent = stubFunction(functionClearTimeoutEvent)
   148  
   149  // ^^ stubbed because signal handling is not implemented in GOOS=js
   150  
   151  // GetRandomData implements runtime.getRandomData, which initializes the seed
   152  // for runtime.fastrand.
   153  //
   154  // See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L200
   155  var GetRandomData = spfunc.MustCallFromSP(false, &wasm.HostFunc{
   156  	ExportNames: []string{functionGetRandomData},
   157  	Name:        functionGetRandomData,
   158  	ParamTypes:  []api.ValueType{i32, i32},
   159  	ParamNames:  []string{"buf", "bufLen"},
   160  	Code: &wasm.Code{
   161  		IsHostFunction: true,
   162  		GoFunc:         api.GoModuleFunc(getRandomData),
   163  	},
   164  })
   165  
   166  func getRandomData(ctx context.Context, mod api.Module, stack []uint64) {
   167  	randSource := mod.(*wasm.CallContext).Sys.RandSource()
   168  	buf, bufLen := uint32(stack[0]), uint32(stack[1])
   169  
   170  	r := mustRead(ctx, mod.Memory(), "r", buf, bufLen)
   171  
   172  	if n, err := randSource.Read(r); err != nil {
   173  		panic(fmt.Errorf("RandSource.Read(r /* len=%d */) failed: %w", bufLen, err))
   174  	} else if uint32(n) != bufLen {
   175  		panic(fmt.Errorf("RandSource.Read(r /* len=%d */) read %d bytes", bufLen, n))
   176  	}
   177  }