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 }