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 }