-
Notifications
You must be signed in to change notification settings - Fork 243
/
goos.go
307 lines (249 loc) · 8.41 KB
/
goos.go
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
// Package goos isolates code from runtime.GOOS=js in a way that avoids cyclic
// dependencies when re-used from other packages.
package goos
import (
"context"
"encoding/binary"
"fmt"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/gojs/goarch"
"github.com/tetratelabs/wazero/internal/gojs/util"
"github.com/tetratelabs/wazero/internal/wasm"
)
// Ref is used to identify a JavaScript value, since the value itself cannot
// be passed to WebAssembly.
//
// The JavaScript value "undefined" is represented by the value 0.
//
// A JavaScript number (64-bit float, except 0 and NaN) is represented by its
// IEEE 754 binary representation.
//
// All other values are represented as an IEEE 754 binary representation of NaN
// with bits 0-31 used as an ID and bits 32-34 used to differentiate between
// string, symbol, function and object.
type Ref uint64
const (
// predefined
IdValueNaN uint32 = iota
IdValueZero
IdValueNull
IdValueTrue
IdValueFalse
IdValueGlobal
IdJsGo
// The below are derived from analyzing `*_js.go` source.
IdObjectConstructor
IdArrayConstructor
IdJsProcess
IdJsfs
IdJsfsConstants
IdUint8ArrayConstructor
IdJsCrypto
IdJsDateConstructor
IdJsDate
NextID
)
const (
RefValueUndefined = Ref(0)
RefValueNaN = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueNaN)
RefValueZero = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueZero)
RefValueNull = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueNull)
RefValueTrue = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueTrue)
RefValueFalse = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueFalse)
RefValueGlobal = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdValueGlobal)
RefJsGo = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsGo)
RefObjectConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdObjectConstructor)
RefArrayConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdArrayConstructor)
RefJsProcess = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsProcess)
RefJsfs = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsfs)
RefJsfsConstants = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsfsConstants)
RefUint8ArrayConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdUint8ArrayConstructor)
RefJsCrypto = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdJsCrypto)
RefJsDateConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdJsDateConstructor)
RefJsDate = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsDate)
)
type TypeFlag byte
// the type flags need to be in sync with gojs.js
const (
TypeFlagNone TypeFlag = iota
TypeFlagObject
TypeFlagString
TypeFlagSymbol //nolint
TypeFlagFunction
)
func ValueRef(id uint32, typeFlag TypeFlag) Ref {
return (NanHead|Ref(typeFlag))<<32 | Ref(id)
}
var le = binary.LittleEndian
// NanHead are the upper 32 bits of a Ref which are set if the value is not encoded as an IEEE 754 number (see above).
const NanHead = 0x7FF80000
func (ref Ref) ParseFloat() (v float64, ok bool) {
if (ref>>32)&NanHead != NanHead {
v = api.DecodeF64(uint64(ref))
ok = true
}
return
}
// GetLastEventArgs returns the arguments to the last event created by
// custom.NameSyscallValueCall.
type GetLastEventArgs func(context.Context) []interface{}
type ValLoader func(context.Context, Ref) interface{}
type Stack interface {
goarch.Stack
ParamRef(i int) Ref
ParamRefs(mem api.Memory, i int) []Ref
ParamVal(ctx context.Context, i int, loader ValLoader) interface{}
// ParamVals is used by functions whose final parameter is an arg array.
ParamVals(ctx context.Context, mem api.Memory, i int, loader ValLoader) []interface{}
SetResultRef(i int, v Ref)
}
type stack struct {
s goarch.Stack
}
// Name implements the same method as documented on goarch.Stack
func (s *stack) Name() string {
return s.s.Name()
}
// Param implements the same method as documented on goarch.Stack
func (s *stack) Param(i int) uint64 {
return s.s.Param(i)
}
// ParamBytes implements the same method as documented on goarch.Stack
func (s *stack) ParamBytes(mem api.Memory, i int) []byte {
return s.s.ParamBytes(mem, i)
}
// ParamRef implements Stack.ParamRef
func (s *stack) ParamRef(i int) Ref {
return Ref(s.s.Param(i))
}
// ParamRefs implements Stack.ParamRefs
func (s *stack) ParamRefs(mem api.Memory, i int) []Ref {
offset := s.s.ParamUint32(i)
size := s.s.ParamUint32(i + 1)
byteCount := size << 3 // size * 8
result := make([]Ref, 0, size)
buf := util.MustRead(mem, s.Name(), i, offset, byteCount)
for pos := uint32(0); pos < byteCount; pos += 8 {
ref := Ref(le.Uint64(buf[pos:]))
result = append(result, ref)
}
return result
}
// ParamString implements the same method as documented on goarch.Stack
func (s *stack) ParamString(mem api.Memory, i int) string {
return s.s.ParamString(mem, i)
}
// ParamInt32 implements the same method as documented on goarch.Stack
func (s *stack) ParamInt32(i int) int32 {
return s.s.ParamInt32(i)
}
// ParamUint32 implements the same method as documented on goarch.Stack
func (s *stack) ParamUint32(i int) uint32 {
return s.s.ParamUint32(i)
}
// ParamVal implements Stack.ParamVal
func (s *stack) ParamVal(ctx context.Context, i int, loader ValLoader) interface{} {
ref := s.ParamRef(i)
return loader(ctx, ref)
}
// ParamVals implements Stack.ParamVals
func (s *stack) ParamVals(ctx context.Context, mem api.Memory, i int, loader ValLoader) []interface{} {
offset := s.s.ParamUint32(i)
size := s.s.ParamUint32(i + 1)
byteCount := size << 3 // size * 8
result := make([]interface{}, 0, size)
buf := util.MustRead(mem, s.Name(), i, offset, byteCount)
for pos := uint32(0); pos < byteCount; pos += 8 {
ref := Ref(le.Uint64(buf[pos:]))
result = append(result, loader(ctx, ref))
}
return result
}
// Refresh implements the same method as documented on goarch.Stack
func (s *stack) Refresh(mod api.Module) {
s.s.Refresh(mod)
}
// SetResult implements the same method as documented on goarch.Stack
func (s *stack) SetResult(i int, v uint64) {
s.s.SetResult(i, v)
}
// SetResultBool implements the same method as documented on goarch.Stack
func (s *stack) SetResultBool(i int, v bool) {
s.s.SetResultBool(i, v)
}
// SetResultI32 implements the same method as documented on goarch.Stack
func (s *stack) SetResultI32(i int, v int32) {
s.s.SetResultI32(i, v)
}
// SetResultI64 implements the same method as documented on goarch.Stack
func (s *stack) SetResultI64(i int, v int64) {
s.s.SetResultI64(i, v)
}
// SetResultRef implements Stack.SetResultRef
func (s *stack) SetResultRef(i int, v Ref) {
s.s.SetResult(i, uint64(v))
}
// SetResultUint32 implements the same method as documented on goarch.Stack
func (s *stack) SetResultUint32(i int, v uint32) {
s.s.SetResultUint32(i, v)
}
func NewFunc(name string, goFunc Func) *wasm.HostFunc {
sf := &stackFunc{name: name, f: goFunc}
return util.NewFunc(name, sf.Call)
}
type Func func(context.Context, api.Module, Stack)
type stackFunc struct {
name string
f Func
}
// Call implements the same method as defined on api.GoModuleFunction.
func (f *stackFunc) Call(ctx context.Context, mod api.Module, wasmStack []uint64) {
s := NewStack(f.name, mod.Memory(), uint32(wasmStack[0]))
f.f(ctx, mod, s)
}
func NewStack(name string, mem api.Memory, sp uint32) *stack {
return &stack{goarch.NewStack(name, mem, sp)}
}
var Undefined = struct{ name string }{name: "undefined"}
func ValueToUint32(arg interface{}) uint32 {
if arg == RefValueZero || arg == Undefined {
return 0
} else if u, ok := arg.(uint32); ok {
return u
}
return uint32(arg.(float64))
}
func ValueToInt32(arg interface{}) int32 {
if arg == RefValueZero || arg == Undefined {
return 0
} else if u, ok := arg.(int); ok {
return int32(u)
}
return int32(uint32(arg.(float64)))
}
// GetFunction allows getting a JavaScript property by name.
type GetFunction interface {
Get(propertyKey string) interface{}
}
// ByteArray is a result of uint8ArrayConstructor which temporarily stores
// binary data outside linear memory.
//
// Note: This is a wrapper because a slice is not hashable.
type ByteArray struct {
slice []byte
}
func WrapByteArray(buf []byte) *ByteArray {
return &ByteArray{buf}
}
// Unwrap returns the underlying byte slice
func (a *ByteArray) Unwrap() []byte {
return a.slice
}
// Get implements GetFunction
func (a *ByteArray) Get(propertyKey string) interface{} {
switch propertyKey {
case "byteLength":
return uint32(len(a.slice))
}
panic(fmt.Sprintf("TODO: get byteArray.%s", propertyKey))
}