166 lines
5.4 KiB
Go
166 lines
5.4 KiB
Go
package gojs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
|
"github.com/tetratelabs/wazero/experimental/sys"
|
|
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
|
"github.com/tetratelabs/wazero/internal/gojs/goarch"
|
|
"github.com/tetratelabs/wazero/internal/wasm"
|
|
)
|
|
|
|
// Debug has unknown use, so stubbed.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/cmd/link/internal/wasm/asm.go#L131-L136
|
|
var Debug = goarch.StubFunction(custom.NameDebug)
|
|
|
|
// TODO: should this call runtime.Breakpoint()?
|
|
|
|
// WasmExit implements runtime.wasmExit which supports runtime.exit.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.go#L24
|
|
var WasmExit = goarch.NewFunc(custom.NameRuntimeWasmExit, wasmExit)
|
|
|
|
func wasmExit(ctx context.Context, mod api.Module, stack goarch.Stack) {
|
|
code := stack.ParamUint32(0)
|
|
|
|
getState(ctx).close()
|
|
_ = mod.CloseWithExitCode(ctx, code)
|
|
}
|
|
|
|
// WasmWrite implements runtime.wasmWrite which supports runtime.write and
|
|
// runtime.writeErr. This implements `println`.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/runtime/os_js.go#L30
|
|
var WasmWrite = goarch.NewFunc(custom.NameRuntimeWasmWrite, wasmWrite)
|
|
|
|
func wasmWrite(_ context.Context, mod api.Module, stack goarch.Stack) {
|
|
fd := stack.ParamInt32(0)
|
|
p := stack.ParamBytes(mod.Memory(), 1 /*, 2 */)
|
|
|
|
fsc := mod.(*wasm.ModuleInstance).Sys.FS()
|
|
if f, ok := fsc.LookupFile(fd); ok {
|
|
_, errno := f.File.Write(p)
|
|
switch errno {
|
|
case 0:
|
|
return // success
|
|
case sys.ENOSYS:
|
|
return // e.g. unimplemented for write
|
|
case sys.EBADF:
|
|
return // e.g. not opened for write
|
|
default:
|
|
panic(fmt.Errorf("error writing p: %w", errno))
|
|
}
|
|
} else {
|
|
panic(fmt.Errorf("fd %d invalid", fd))
|
|
}
|
|
}
|
|
|
|
// ResetMemoryDataView signals wasm.OpcodeMemoryGrow happened, indicating any
|
|
// cached view of memory should be reset.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/runtime/mem_js.go#L82
|
|
var ResetMemoryDataView = goarch.NewFunc(custom.NameRuntimeResetMemoryDataView, resetMemoryDataView)
|
|
|
|
func resetMemoryDataView(context.Context, api.Module, goarch.Stack) {
|
|
// context state does not cache a memory view, and all byte slices used
|
|
// are safely copied. Also, user-defined functions are not supported.
|
|
// Hence, there's currently no known reason to reset anything.
|
|
}
|
|
|
|
// Nanotime1 implements runtime.nanotime which supports time.Since.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L117
|
|
var Nanotime1 = goarch.NewFunc(custom.NameRuntimeNanotime1, nanotime1)
|
|
|
|
func nanotime1(_ context.Context, mod api.Module, stack goarch.Stack) {
|
|
nsec := mod.(*wasm.ModuleInstance).Sys.Nanotime()
|
|
|
|
stack.SetResultI64(0, nsec)
|
|
}
|
|
|
|
// Walltime implements runtime.walltime which supports time.Now.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L121
|
|
var Walltime = goarch.NewFunc(custom.NameRuntimeWalltime, walltime)
|
|
|
|
func walltime(_ context.Context, mod api.Module, stack goarch.Stack) {
|
|
sec, nsec := mod.(*wasm.ModuleInstance).Sys.Walltime()
|
|
|
|
stack.SetResultI64(0, sec)
|
|
stack.SetResultI32(1, nsec)
|
|
}
|
|
|
|
// ScheduleTimeoutEvent implements runtime.scheduleTimeoutEvent which supports
|
|
// runtime.notetsleepg used by runtime.signal_recv.
|
|
//
|
|
// Unlike other most functions prefixed by "runtime.", this both launches a
|
|
// goroutine and invokes code compiled into wasm "resume".
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L125
|
|
var ScheduleTimeoutEvent = goarch.NewFunc(custom.NameRuntimeScheduleTimeoutEvent, scheduleTimeoutEvent)
|
|
|
|
// Note: Signal handling is not implemented in GOOS=js.
|
|
func scheduleTimeoutEvent(ctx context.Context, mod api.Module, stack goarch.Stack) {
|
|
ms := stack.Param(0)
|
|
|
|
s := getState(ctx)
|
|
id := s._nextCallbackTimeoutID
|
|
stack.SetResultUint32(0, id)
|
|
s._nextCallbackTimeoutID++
|
|
|
|
cleared := make(chan bool)
|
|
timeout := time.Millisecond * time.Duration(ms)
|
|
s._scheduledTimeouts[id] = cleared
|
|
|
|
// As wasm is currently not concurrent, a timeout on another goroutine may
|
|
// not make sense. However, this implements what wasm_exec.js does anyway.
|
|
go func() {
|
|
select {
|
|
case <-cleared: // do nothing
|
|
case <-time.After(timeout):
|
|
if _, err := mod.ExportedFunction("resume").Call(ctx); err != nil {
|
|
println(err)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// ClearTimeoutEvent implements runtime.clearTimeoutEvent which supports
|
|
// runtime.notetsleepg used by runtime.signal_recv.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L129
|
|
var ClearTimeoutEvent = goarch.NewFunc(custom.NameRuntimeClearTimeoutEvent, clearTimeoutEvent)
|
|
|
|
// Note: Signal handling is not implemented in GOOS=js.
|
|
func clearTimeoutEvent(ctx context.Context, _ api.Module, stack goarch.Stack) {
|
|
id := stack.ParamUint32(0)
|
|
s := getState(ctx)
|
|
if cancel, ok := s._scheduledTimeouts[id]; ok {
|
|
delete(s._scheduledTimeouts, id)
|
|
cancel <- true
|
|
}
|
|
}
|
|
|
|
// GetRandomData implements runtime.getRandomData, which initializes the seed
|
|
// for runtime.fastrand.
|
|
//
|
|
// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L133
|
|
var GetRandomData = goarch.NewFunc(custom.NameRuntimeGetRandomData, getRandomData)
|
|
|
|
func getRandomData(_ context.Context, mod api.Module, stack goarch.Stack) {
|
|
r := stack.ParamBytes(mod.Memory(), 0 /*, 1 */)
|
|
|
|
randSource := mod.(*wasm.ModuleInstance).Sys.RandSource()
|
|
|
|
bufLen := len(r)
|
|
if n, err := randSource.Read(r); err != nil {
|
|
panic(fmt.Errorf("RandSource.Read(r /* len=%d */) failed: %w", bufLen, err))
|
|
} else if n != bufLen {
|
|
panic(fmt.Errorf("RandSource.Read(r /* len=%d */) read %d bytes", bufLen, n))
|
|
}
|
|
}
|