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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
|
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js,wasm
// Package js gives access to the WebAssembly host environment when using the js/wasm architecture.
// Its API is based on JavaScript semantics.
//
// This package is EXPERIMENTAL. Its current scope is only to allow tests to run, but not yet to provide a
// comprehensive API for users. It is exempt from the Go compatibility promise.
package js
import (
"unsafe"
)
// ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly.
// A JavaScript number (64-bit float, except 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-33 used to differentiate between string, symbol, function and object.
type ref uint64
// nanHead are the upper 32 bits of a ref which are set if the value is not a JavaScript number or NaN itself.
const nanHead = 0x7FF80000
// Value represents a JavaScript value.
type Value struct {
ref ref
}
func makeValue(v ref) Value {
return Value{ref: v}
}
func predefValue(id uint32) Value {
return Value{ref: nanHead<<32 | ref(id)}
}
func floatValue(f float64) Value {
if f != f {
return valueNaN
}
return Value{ref: *(*ref)(unsafe.Pointer(&f))}
}
// Error wraps a JavaScript error.
type Error struct {
// Value is the underlying JavaScript error value.
Value
}
// Error implements the error interface.
func (e Error) Error() string {
return "JavaScript error: " + e.Get("message").String()
}
var (
valueNaN = predefValue(0)
valueUndefined = predefValue(1)
valueNull = predefValue(2)
valueTrue = predefValue(3)
valueFalse = predefValue(4)
valueGlobal = predefValue(5)
memory = predefValue(6) // WebAssembly linear memory
jsGo = predefValue(7) // instance of the Go class in JavaScript
objectConstructor = valueGlobal.Get("Object")
arrayConstructor = valueGlobal.Get("Array")
)
// Undefined returns the JavaScript value "undefined".
func Undefined() Value {
return valueUndefined
}
// Null returns the JavaScript value "null".
func Null() Value {
return valueNull
}
// Global returns the JavaScript global object, usually "window" or "global".
func Global() Value {
return valueGlobal
}
// ValueOf returns x as a JavaScript value:
//
// | Go | JavaScript |
// | ---------------------- | ---------------------- |
// | js.Value | [its value] |
// | js.TypedArray | typed array |
// | js.Callback | function |
// | nil | null |
// | bool | boolean |
// | integers and floats | number |
// | string | string |
// | []interface{} | new array |
// | map[string]interface{} | new object |
func ValueOf(x interface{}) Value {
switch x := x.(type) {
case Value:
return x
case TypedArray:
return x.Value
case Callback:
return x.Value
case nil:
return valueNull
case bool:
if x {
return valueTrue
} else {
return valueFalse
}
case int:
return floatValue(float64(x))
case int8:
return floatValue(float64(x))
case int16:
return floatValue(float64(x))
case int32:
return floatValue(float64(x))
case int64:
return floatValue(float64(x))
case uint:
return floatValue(float64(x))
case uint8:
return floatValue(float64(x))
case uint16:
return floatValue(float64(x))
case uint32:
return floatValue(float64(x))
case uint64:
return floatValue(float64(x))
case uintptr:
return floatValue(float64(x))
case unsafe.Pointer:
return floatValue(float64(uintptr(x)))
case float32:
return floatValue(float64(x))
case float64:
return floatValue(x)
case string:
return makeValue(stringVal(x))
case []interface{}:
a := arrayConstructor.New(len(x))
for i, s := range x {
a.SetIndex(i, s)
}
return a
case map[string]interface{}:
o := objectConstructor.New()
for k, v := range x {
o.Set(k, v)
}
return o
default:
panic("ValueOf: invalid value")
}
}
func stringVal(x string) ref
// Type represents the JavaScript type of a Value.
type Type int
const (
TypeUndefined Type = iota
TypeNull
TypeBoolean
TypeNumber
TypeString
TypeSymbol
TypeObject
TypeFunction
)
func (t Type) String() string {
switch t {
case TypeUndefined:
return "undefined"
case TypeNull:
return "null"
case TypeBoolean:
return "boolean"
case TypeNumber:
return "number"
case TypeString:
return "string"
case TypeSymbol:
return "symbol"
case TypeObject:
return "object"
case TypeFunction:
return "function"
default:
panic("bad type")
}
}
// Type returns the JavaScript type of the value v. It is similar to JavaScript's typeof operator,
// except that it returns TypeNull instead of TypeObject for null.
func (v Value) Type() Type {
switch v.ref {
case valueUndefined.ref:
return TypeUndefined
case valueNull.ref:
return TypeNull
case valueTrue.ref, valueFalse.ref:
return TypeBoolean
}
if v.isNumber() {
return TypeNumber
}
typeFlag := v.ref >> 32 & 3
switch typeFlag {
case 1:
return TypeString
case 2:
return TypeSymbol
case 3:
return TypeFunction
default:
return TypeObject
}
}
// Get returns the JavaScript property p of value v.
func (v Value) Get(p string) Value {
return makeValue(valueGet(v.ref, p))
}
func valueGet(v ref, p string) ref
// Set sets the JavaScript property p of value v to ValueOf(x).
func (v Value) Set(p string, x interface{}) {
valueSet(v.ref, p, ValueOf(x).ref)
}
func valueSet(v ref, p string, x ref)
// Index returns JavaScript index i of value v.
func (v Value) Index(i int) Value {
return makeValue(valueIndex(v.ref, i))
}
func valueIndex(v ref, i int) ref
// SetIndex sets the JavaScript index i of value v to ValueOf(x).
func (v Value) SetIndex(i int, x interface{}) {
valueSetIndex(v.ref, i, ValueOf(x).ref)
}
func valueSetIndex(v ref, i int, x ref)
func makeArgs(args []interface{}) []ref {
argVals := make([]ref, len(args))
for i, arg := range args {
argVals[i] = ValueOf(arg).ref
}
return argVals
}
// Length returns the JavaScript property "length" of v.
func (v Value) Length() int {
return valueLength(v.ref)
}
func valueLength(v ref) int
// Call does a JavaScript call to the method m of value v with the given arguments.
// It panics if v has no method m.
// The arguments get mapped to JavaScript values according to the ValueOf function.
func (v Value) Call(m string, args ...interface{}) Value {
res, ok := valueCall(v.ref, m, makeArgs(args))
if !ok {
if vType := v.Type(); vType != TypeObject && vType != TypeFunction { // check here to avoid overhead in success case
panic(&ValueError{"Value.Call", vType})
}
if propType := v.Get(m).Type(); propType != TypeFunction {
panic("syscall/js: Value.Call: property " + m + " is not a function, got " + propType.String())
}
panic(Error{makeValue(res)})
}
return makeValue(res)
}
func valueCall(v ref, m string, args []ref) (ref, bool)
// Invoke does a JavaScript call of the value v with the given arguments.
// It panics if v is not a function.
// The arguments get mapped to JavaScript values according to the ValueOf function.
func (v Value) Invoke(args ...interface{}) Value {
res, ok := valueInvoke(v.ref, makeArgs(args))
if !ok {
if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
panic(&ValueError{"Value.Invoke", vType})
}
panic(Error{makeValue(res)})
}
return makeValue(res)
}
func valueInvoke(v ref, args []ref) (ref, bool)
// New uses JavaScript's "new" operator with value v as constructor and the given arguments.
// It panics if v is not a function.
// The arguments get mapped to JavaScript values according to the ValueOf function.
func (v Value) New(args ...interface{}) Value {
res, ok := valueNew(v.ref, makeArgs(args))
if !ok {
panic(Error{makeValue(res)})
}
return makeValue(res)
}
func valueNew(v ref, args []ref) (ref, bool)
func (v Value) isNumber() bool {
return v.ref>>32&nanHead != nanHead || v.ref == valueNaN.ref
}
func (v Value) float(method string) float64 {
if !v.isNumber() {
panic(&ValueError{method, v.Type()})
}
return *(*float64)(unsafe.Pointer(&v.ref))
}
// Float returns the value v as a float64. It panics if v is not a JavaScript number.
func (v Value) Float() float64 {
return v.float("Value.Float")
}
// Int returns the value v truncated to an int. It panics if v is not a JavaScript number.
func (v Value) Int() int {
return int(v.float("Value.Int"))
}
// Bool returns the value v as a bool. It panics if v is not a JavaScript boolean.
func (v Value) Bool() bool {
switch v.ref {
case valueTrue.ref:
return true
case valueFalse.ref:
return false
default:
panic(&ValueError{"Value.Bool", v.Type()})
}
}
// String returns the value v converted to string according to JavaScript type conversions.
func (v Value) String() string {
str, length := valuePrepareString(v.ref)
b := make([]byte, length)
valueLoadString(str, b)
return string(b)
}
func valuePrepareString(v ref) (ref, int)
func valueLoadString(v ref, b []byte)
// InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator.
func (v Value) InstanceOf(t Value) bool {
return valueInstanceOf(v.ref, t.ref)
}
func valueInstanceOf(v ref, t ref) bool
// A ValueError occurs when a Value method is invoked on
// a Value that does not support it. Such cases are documented
// in the description of each method.
type ValueError struct {
Method string
Type Type
}
func (e *ValueError) Error() string {
return "syscall/js: call of " + e.Method + " on " + e.Type.String()
}
|