aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/runtime/runtime_wasm_wasi.go
blob: aec27e4c81a51b74562f076ed2e022520dd34d9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//go:build tinygo.wasm && wasi

package runtime

import (
	"unsafe"
)

type timeUnit int64

// libc constructors
//
//export __wasm_call_ctors
func __wasm_call_ctors()

//export _start
func _start() {
	// These need to be initialized early so that the heap can be initialized.
	heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
	heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
	run()
}

// Read the command line arguments from WASI.
// For example, they can be passed to a program with wasmtime like this:
//
//	wasmtime run ./program.wasm arg1 arg2
func init() {
	__wasm_call_ctors()
}

var args []string

//go:linkname os_runtime_args os.runtime_args
func os_runtime_args() []string {
	if args == nil {
		// Read the number of args (argc) and the buffer size required to store
		// all these args (argv).
		var argc, argv_buf_size uint32
		args_sizes_get(&argc, &argv_buf_size)
		if argc == 0 {
			return nil
		}

		// Obtain the command line arguments
		argsSlice := make([]unsafe.Pointer, argc)
		buf := make([]byte, argv_buf_size)
		args_get(&argsSlice[0], unsafe.Pointer(&buf[0]))

		// Convert the array of C strings to an array of Go strings.
		args = make([]string, argc)
		for i, cstr := range argsSlice {
			length := strlen(cstr)
			argString := _string{
				length: length,
				ptr:    (*byte)(cstr),
			}
			args[i] = *(*string)(unsafe.Pointer(&argString))
		}
	}
	return args
}

func ticksToNanoseconds(ticks timeUnit) int64 {
	return int64(ticks)
}

func nanosecondsToTicks(ns int64) timeUnit {
	return timeUnit(ns)
}

const timePrecisionNanoseconds = 1000 // TODO: how can we determine the appropriate `precision`?

var (
	sleepTicksSubscription = __wasi_subscription_t{
		userData: 0,
		u: __wasi_subscription_u_t{
			tag: __wasi_eventtype_t_clock,
			u: __wasi_subscription_clock_t{
				id:        0,
				timeout:   0,
				precision: timePrecisionNanoseconds,
				flags:     0,
			},
		},
	}
	sleepTicksResult  = __wasi_event_t{}
	sleepTicksNEvents uint32
)

func sleepTicks(d timeUnit) {
	sleepTicksSubscription.u.u.timeout = uint64(d)
	poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents)
}

func ticks() timeUnit {
	var nano uint64
	clock_time_get(0, timePrecisionNanoseconds, &nano)
	return timeUnit(nano)
}

// Implementations of WASI APIs

//go:wasmimport wasi_snapshot_preview1 args_get
func args_get(argv *unsafe.Pointer, argv_buf unsafe.Pointer) (errno uint16)

//go:wasmimport wasi_snapshot_preview1 args_sizes_get
func args_sizes_get(argc *uint32, argv_buf_size *uint32) (errno uint16)

//go:wasmimport wasi_snapshot_preview1 clock_time_get
func clock_time_get(clockid uint32, precision uint64, time *uint64) (errno uint16)

//go:wasmimport wasi_snapshot_preview1 poll_oneoff
func poll_oneoff(in *__wasi_subscription_t, out *__wasi_event_t, nsubscriptions uint32, nevents *uint32) (errno uint16)

type __wasi_eventtype_t = uint8

const (
	__wasi_eventtype_t_clock __wasi_eventtype_t = 0
	// TODO: __wasi_eventtype_t_fd_read  __wasi_eventtype_t = 1
	// TODO: __wasi_eventtype_t_fd_write __wasi_eventtype_t = 2
)

type (
	// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-subscription-record
	__wasi_subscription_t struct {
		userData uint64
		u        __wasi_subscription_u_t
	}

	__wasi_subscription_u_t struct {
		tag __wasi_eventtype_t

		// TODO: support fd_read/fd_write event
		u __wasi_subscription_clock_t
	}

	// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-subscription_clock-record
	__wasi_subscription_clock_t struct {
		id        uint32
		timeout   uint64
		precision uint64
		flags     uint16
	}
)

type (
	// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-event-record
	__wasi_event_t struct {
		userData  uint64
		errno     uint16
		eventType __wasi_eventtype_t

		// only used for fd_read or fd_write events
		// TODO: support fd_read/fd_write event
		_ struct {
			nBytes uint64
			flags  uint16
		}
	}
)