1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build wasip1
6
7package runtime
8
9import "unsafe"
10
11// GOARCH=wasm currently has 64 bits pointers, but the WebAssembly host expects
12// pointers to be 32 bits so we use this type alias to represent pointers in
13// structs and arrays passed as arguments to WASI functions.
14//
15// Note that the use of an integer type prevents the compiler from tracking
16// pointers passed to WASI functions, so we must use KeepAlive to explicitly
17// retain the objects that could otherwise be reclaimed by the GC.
18type uintptr32 = uint32
19
20// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-size-u32
21type size = uint32
22
23// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-errno-variant
24type errno = uint32
25
26// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-filesize-u64
27type filesize = uint64
28
29// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-timestamp-u64
30type timestamp = uint64
31
32// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-clockid-variant
33type clockid = uint32
34
35const (
36	clockRealtime  clockid = 0
37	clockMonotonic clockid = 1
38)
39
40// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-iovec-record
41type iovec struct {
42	buf    uintptr32
43	bufLen size
44}
45
46//go:wasmimport wasi_snapshot_preview1 proc_exit
47func exit(code int32)
48
49//go:wasmimport wasi_snapshot_preview1 args_get
50//go:noescape
51func args_get(argv, argvBuf unsafe.Pointer) errno
52
53//go:wasmimport wasi_snapshot_preview1 args_sizes_get
54//go:noescape
55func args_sizes_get(argc, argvBufLen unsafe.Pointer) errno
56
57//go:wasmimport wasi_snapshot_preview1 clock_time_get
58//go:noescape
59func clock_time_get(clock_id clockid, precision timestamp, time unsafe.Pointer) errno
60
61//go:wasmimport wasi_snapshot_preview1 environ_get
62//go:noescape
63func environ_get(environ, environBuf unsafe.Pointer) errno
64
65//go:wasmimport wasi_snapshot_preview1 environ_sizes_get
66//go:noescape
67func environ_sizes_get(environCount, environBufLen unsafe.Pointer) errno
68
69//go:wasmimport wasi_snapshot_preview1 fd_write
70//go:noescape
71func fd_write(fd int32, iovs unsafe.Pointer, iovsLen size, nwritten unsafe.Pointer) errno
72
73//go:wasmimport wasi_snapshot_preview1 random_get
74//go:noescape
75func random_get(buf unsafe.Pointer, bufLen size) errno
76
77type eventtype = uint8
78
79const (
80	eventtypeClock eventtype = iota
81	eventtypeFdRead
82	eventtypeFdWrite
83)
84
85type eventrwflags = uint16
86
87const (
88	fdReadwriteHangup eventrwflags = 1 << iota
89)
90
91type userdata = uint64
92
93// The go:wasmimport directive currently does not accept values of type uint16
94// in arguments or returns of the function signature. Most WASI imports return
95// an errno value, which we have to define as uint32 because of that limitation.
96// However, the WASI errno type is intended to be a 16 bits integer, and in the
97// event struct the error field should be of type errno. If we used the errno
98// type for the error field it would result in a mismatching field alignment and
99// struct size because errno is declared as a 32 bits type, so we declare the
100// error field as a plain uint16.
101type event struct {
102	userdata    userdata
103	error       uint16
104	typ         eventtype
105	fdReadwrite eventFdReadwrite
106}
107
108type eventFdReadwrite struct {
109	nbytes filesize
110	flags  eventrwflags
111}
112
113type subclockflags = uint16
114
115const (
116	subscriptionClockAbstime subclockflags = 1 << iota
117)
118
119type subscriptionClock struct {
120	id        clockid
121	timeout   timestamp
122	precision timestamp
123	flags     subclockflags
124}
125
126type subscriptionFdReadwrite struct {
127	fd int32
128}
129
130type subscription struct {
131	userdata userdata
132	u        subscriptionUnion
133}
134
135type subscriptionUnion [5]uint64
136
137func (u *subscriptionUnion) eventtype() *eventtype {
138	return (*eventtype)(unsafe.Pointer(&u[0]))
139}
140
141func (u *subscriptionUnion) subscriptionClock() *subscriptionClock {
142	return (*subscriptionClock)(unsafe.Pointer(&u[1]))
143}
144
145func (u *subscriptionUnion) subscriptionFdReadwrite() *subscriptionFdReadwrite {
146	return (*subscriptionFdReadwrite)(unsafe.Pointer(&u[1]))
147}
148
149//go:wasmimport wasi_snapshot_preview1 poll_oneoff
150//go:noescape
151func poll_oneoff(in, out unsafe.Pointer, nsubscriptions size, nevents unsafe.Pointer) errno
152
153func write1(fd uintptr, p unsafe.Pointer, n int32) int32 {
154	iov := iovec{
155		buf:    uintptr32(uintptr(p)),
156		bufLen: size(n),
157	}
158	var nwritten size
159	if fd_write(int32(fd), unsafe.Pointer(&iov), 1, unsafe.Pointer(&nwritten)) != 0 {
160		throw("fd_write failed")
161	}
162	return int32(nwritten)
163}
164
165func usleep(usec uint32) {
166	var in subscription
167	var out event
168	var nevents size
169
170	eventtype := in.u.eventtype()
171	*eventtype = eventtypeClock
172
173	subscription := in.u.subscriptionClock()
174	subscription.id = clockMonotonic
175	subscription.timeout = timestamp(usec) * 1e3
176	subscription.precision = 1e3
177
178	if poll_oneoff(unsafe.Pointer(&in), unsafe.Pointer(&out), 1, unsafe.Pointer(&nevents)) != 0 {
179		throw("wasi_snapshot_preview1.poll_oneoff")
180	}
181}
182
183func readRandom(r []byte) int {
184	if random_get(unsafe.Pointer(&r[0]), size(len(r))) != 0 {
185		return 0
186	}
187	return len(r)
188}
189
190func goenvs() {
191	// arguments
192	var argc size
193	var argvBufLen size
194	if args_sizes_get(unsafe.Pointer(&argc), unsafe.Pointer(&argvBufLen)) != 0 {
195		throw("args_sizes_get failed")
196	}
197
198	argslice = make([]string, argc)
199	if argc > 0 {
200		argv := make([]uintptr32, argc)
201		argvBuf := make([]byte, argvBufLen)
202		if args_get(unsafe.Pointer(&argv[0]), unsafe.Pointer(&argvBuf[0])) != 0 {
203			throw("args_get failed")
204		}
205
206		for i := range argslice {
207			start := argv[i] - uintptr32(uintptr(unsafe.Pointer(&argvBuf[0])))
208			end := start
209			for argvBuf[end] != 0 {
210				end++
211			}
212			argslice[i] = string(argvBuf[start:end])
213		}
214	}
215
216	// environment
217	var environCount size
218	var environBufLen size
219	if environ_sizes_get(unsafe.Pointer(&environCount), unsafe.Pointer(&environBufLen)) != 0 {
220		throw("environ_sizes_get failed")
221	}
222
223	envs = make([]string, environCount)
224	if environCount > 0 {
225		environ := make([]uintptr32, environCount)
226		environBuf := make([]byte, environBufLen)
227		if environ_get(unsafe.Pointer(&environ[0]), unsafe.Pointer(&environBuf[0])) != 0 {
228			throw("environ_get failed")
229		}
230
231		for i := range envs {
232			start := environ[i] - uintptr32(uintptr(unsafe.Pointer(&environBuf[0])))
233			end := start
234			for environBuf[end] != 0 {
235				end++
236			}
237			envs[i] = string(environBuf[start:end])
238		}
239	}
240}
241
242func walltime() (sec int64, nsec int32) {
243	return walltime1()
244}
245
246func walltime1() (sec int64, nsec int32) {
247	var time timestamp
248	if clock_time_get(clockRealtime, 0, unsafe.Pointer(&time)) != 0 {
249		throw("clock_time_get failed")
250	}
251	return int64(time / 1000000000), int32(time % 1000000000)
252}
253
254func nanotime1() int64 {
255	var time timestamp
256	if clock_time_get(clockMonotonic, 0, unsafe.Pointer(&time)) != 0 {
257		throw("clock_time_get failed")
258	}
259	return int64(time)
260}
261