1
2
3
4
5
6
7
8
9
10
11
12 package js
13
14 import (
15 "runtime"
16 "unsafe"
17 )
18
19
20
21
22
23
24
25 type ref uint64
26
27
28 const nanHead = 0x7FF80000
29
30
31 type Wrapper interface {
32
33 JSValue() Value
34 }
35
36
37
38 type Value struct {
39 _ [0]func()
40 ref ref
41 gcPtr *ref
42 }
43
44 const (
45
46 typeFlagNone = iota
47 typeFlagObject
48 typeFlagString
49 typeFlagSymbol
50 typeFlagFunction
51 )
52
53
54 func (v Value) JSValue() Value {
55 return v
56 }
57
58 func makeValue(r ref) Value {
59 var gcPtr *ref
60 typeFlag := (r >> 32) & 7
61 if (r>>32)&nanHead == nanHead && typeFlag != typeFlagNone {
62 gcPtr = new(ref)
63 *gcPtr = r
64 runtime.SetFinalizer(gcPtr, func(p *ref) {
65 finalizeRef(*p)
66 })
67 }
68
69 return Value{ref: r, gcPtr: gcPtr}
70 }
71
72 func finalizeRef(r ref)
73
74 func predefValue(id uint32, typeFlag byte) Value {
75 return Value{ref: (nanHead|ref(typeFlag))<<32 | ref(id)}
76 }
77
78 func floatValue(f float64) Value {
79 if f == 0 {
80 return valueZero
81 }
82 if f != f {
83 return valueNaN
84 }
85 return Value{ref: *(*ref)(unsafe.Pointer(&f))}
86 }
87
88
89 type Error struct {
90
91 Value
92 }
93
94
95 func (e Error) Error() string {
96 return "JavaScript error: " + e.Get("message").String()
97 }
98
99 var (
100 valueUndefined = Value{ref: 0}
101 valueNaN = predefValue(0, typeFlagNone)
102 valueZero = predefValue(1, typeFlagNone)
103 valueNull = predefValue(2, typeFlagNone)
104 valueTrue = predefValue(3, typeFlagNone)
105 valueFalse = predefValue(4, typeFlagNone)
106 valueGlobal = predefValue(5, typeFlagObject)
107 jsGo = predefValue(6, typeFlagObject)
108
109 objectConstructor = valueGlobal.Get("Object")
110 arrayConstructor = valueGlobal.Get("Array")
111 )
112
113
114 func (v Value) Equal(w Value) bool {
115 return v.ref == w.ref && v.ref != valueNaN.ref
116 }
117
118
119 func Undefined() Value {
120 return valueUndefined
121 }
122
123
124 func (v Value) IsUndefined() bool {
125 return v.ref == valueUndefined.ref
126 }
127
128
129 func Null() Value {
130 return valueNull
131 }
132
133
134 func (v Value) IsNull() bool {
135 return v.ref == valueNull.ref
136 }
137
138
139 func (v Value) IsNaN() bool {
140 return v.ref == valueNaN.ref
141 }
142
143
144 func Global() Value {
145 return valueGlobal
146 }
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 func ValueOf(x interface{}) Value {
163 switch x := x.(type) {
164 case Value:
165 return x
166 case Wrapper:
167 return x.JSValue()
168 case nil:
169 return valueNull
170 case bool:
171 if x {
172 return valueTrue
173 } else {
174 return valueFalse
175 }
176 case int:
177 return floatValue(float64(x))
178 case int8:
179 return floatValue(float64(x))
180 case int16:
181 return floatValue(float64(x))
182 case int32:
183 return floatValue(float64(x))
184 case int64:
185 return floatValue(float64(x))
186 case uint:
187 return floatValue(float64(x))
188 case uint8:
189 return floatValue(float64(x))
190 case uint16:
191 return floatValue(float64(x))
192 case uint32:
193 return floatValue(float64(x))
194 case uint64:
195 return floatValue(float64(x))
196 case uintptr:
197 return floatValue(float64(x))
198 case unsafe.Pointer:
199 return floatValue(float64(uintptr(x)))
200 case float32:
201 return floatValue(float64(x))
202 case float64:
203 return floatValue(x)
204 case string:
205 return makeValue(stringVal(x))
206 case []interface{}:
207 a := arrayConstructor.New(len(x))
208 for i, s := range x {
209 a.SetIndex(i, s)
210 }
211 return a
212 case map[string]interface{}:
213 o := objectConstructor.New()
214 for k, v := range x {
215 o.Set(k, v)
216 }
217 return o
218 default:
219 panic("ValueOf: invalid value")
220 }
221 }
222
223 func stringVal(x string) ref
224
225
226 type Type int
227
228 const (
229 TypeUndefined Type = iota
230 TypeNull
231 TypeBoolean
232 TypeNumber
233 TypeString
234 TypeSymbol
235 TypeObject
236 TypeFunction
237 )
238
239 func (t Type) String() string {
240 switch t {
241 case TypeUndefined:
242 return "undefined"
243 case TypeNull:
244 return "null"
245 case TypeBoolean:
246 return "boolean"
247 case TypeNumber:
248 return "number"
249 case TypeString:
250 return "string"
251 case TypeSymbol:
252 return "symbol"
253 case TypeObject:
254 return "object"
255 case TypeFunction:
256 return "function"
257 default:
258 panic("bad type")
259 }
260 }
261
262 func (t Type) isObject() bool {
263 return t == TypeObject || t == TypeFunction
264 }
265
266
267
268 func (v Value) Type() Type {
269 switch v.ref {
270 case valueUndefined.ref:
271 return TypeUndefined
272 case valueNull.ref:
273 return TypeNull
274 case valueTrue.ref, valueFalse.ref:
275 return TypeBoolean
276 }
277 if v.isNumber() {
278 return TypeNumber
279 }
280 typeFlag := (v.ref >> 32) & 7
281 switch typeFlag {
282 case typeFlagObject:
283 return TypeObject
284 case typeFlagString:
285 return TypeString
286 case typeFlagSymbol:
287 return TypeSymbol
288 case typeFlagFunction:
289 return TypeFunction
290 default:
291 panic("bad type flag")
292 }
293 }
294
295
296
297 func (v Value) Get(p string) Value {
298 if vType := v.Type(); !vType.isObject() {
299 panic(&ValueError{"Value.Get", vType})
300 }
301 r := makeValue(valueGet(v.ref, p))
302 runtime.KeepAlive(v)
303 return r
304 }
305
306 func valueGet(v ref, p string) ref
307
308
309
310 func (v Value) Set(p string, x interface{}) {
311 if vType := v.Type(); !vType.isObject() {
312 panic(&ValueError{"Value.Set", vType})
313 }
314 xv := ValueOf(x)
315 valueSet(v.ref, p, xv.ref)
316 runtime.KeepAlive(v)
317 runtime.KeepAlive(xv)
318 }
319
320 func valueSet(v ref, p string, x ref)
321
322
323
324 func (v Value) Delete(p string) {
325 if vType := v.Type(); !vType.isObject() {
326 panic(&ValueError{"Value.Delete", vType})
327 }
328 valueDelete(v.ref, p)
329 runtime.KeepAlive(v)
330 }
331
332 func valueDelete(v ref, p string)
333
334
335
336 func (v Value) Index(i int) Value {
337 if vType := v.Type(); !vType.isObject() {
338 panic(&ValueError{"Value.Index", vType})
339 }
340 r := makeValue(valueIndex(v.ref, i))
341 runtime.KeepAlive(v)
342 return r
343 }
344
345 func valueIndex(v ref, i int) ref
346
347
348
349 func (v Value) SetIndex(i int, x interface{}) {
350 if vType := v.Type(); !vType.isObject() {
351 panic(&ValueError{"Value.SetIndex", vType})
352 }
353 xv := ValueOf(x)
354 valueSetIndex(v.ref, i, xv.ref)
355 runtime.KeepAlive(v)
356 runtime.KeepAlive(xv)
357 }
358
359 func valueSetIndex(v ref, i int, x ref)
360
361 func makeArgs(args []interface{}) ([]Value, []ref) {
362 argVals := make([]Value, len(args))
363 argRefs := make([]ref, len(args))
364 for i, arg := range args {
365 v := ValueOf(arg)
366 argVals[i] = v
367 argRefs[i] = v.ref
368 }
369 return argVals, argRefs
370 }
371
372
373
374 func (v Value) Length() int {
375 if vType := v.Type(); !vType.isObject() {
376 panic(&ValueError{"Value.SetIndex", vType})
377 }
378 r := valueLength(v.ref)
379 runtime.KeepAlive(v)
380 return r
381 }
382
383 func valueLength(v ref) int
384
385
386
387
388 func (v Value) Call(m string, args ...interface{}) Value {
389 argVals, argRefs := makeArgs(args)
390 res, ok := valueCall(v.ref, m, argRefs)
391 runtime.KeepAlive(v)
392 runtime.KeepAlive(argVals)
393 if !ok {
394 if vType := v.Type(); !vType.isObject() {
395 panic(&ValueError{"Value.Call", vType})
396 }
397 if propType := v.Get(m).Type(); propType != TypeFunction {
398 panic("syscall/js: Value.Call: property " + m + " is not a function, got " + propType.String())
399 }
400 panic(Error{makeValue(res)})
401 }
402 return makeValue(res)
403 }
404
405 func valueCall(v ref, m string, args []ref) (ref, bool)
406
407
408
409
410 func (v Value) Invoke(args ...interface{}) Value {
411 argVals, argRefs := makeArgs(args)
412 res, ok := valueInvoke(v.ref, argRefs)
413 runtime.KeepAlive(v)
414 runtime.KeepAlive(argVals)
415 if !ok {
416 if vType := v.Type(); vType != TypeFunction {
417 panic(&ValueError{"Value.Invoke", vType})
418 }
419 panic(Error{makeValue(res)})
420 }
421 return makeValue(res)
422 }
423
424 func valueInvoke(v ref, args []ref) (ref, bool)
425
426
427
428
429 func (v Value) New(args ...interface{}) Value {
430 argVals, argRefs := makeArgs(args)
431 res, ok := valueNew(v.ref, argRefs)
432 runtime.KeepAlive(v)
433 runtime.KeepAlive(argVals)
434 if !ok {
435 if vType := v.Type(); vType != TypeFunction {
436 panic(&ValueError{"Value.Invoke", vType})
437 }
438 panic(Error{makeValue(res)})
439 }
440 return makeValue(res)
441 }
442
443 func valueNew(v ref, args []ref) (ref, bool)
444
445 func (v Value) isNumber() bool {
446 return v.ref == valueZero.ref ||
447 v.ref == valueNaN.ref ||
448 (v.ref != valueUndefined.ref && (v.ref>>32)&nanHead != nanHead)
449 }
450
451 func (v Value) float(method string) float64 {
452 if !v.isNumber() {
453 panic(&ValueError{method, v.Type()})
454 }
455 if v.ref == valueZero.ref {
456 return 0
457 }
458 return *(*float64)(unsafe.Pointer(&v.ref))
459 }
460
461
462
463 func (v Value) Float() float64 {
464 return v.float("Value.Float")
465 }
466
467
468
469 func (v Value) Int() int {
470 return int(v.float("Value.Int"))
471 }
472
473
474
475 func (v Value) Bool() bool {
476 switch v.ref {
477 case valueTrue.ref:
478 return true
479 case valueFalse.ref:
480 return false
481 default:
482 panic(&ValueError{"Value.Bool", v.Type()})
483 }
484 }
485
486
487
488
489 func (v Value) Truthy() bool {
490 switch v.Type() {
491 case TypeUndefined, TypeNull:
492 return false
493 case TypeBoolean:
494 return v.Bool()
495 case TypeNumber:
496 return v.ref != valueNaN.ref && v.ref != valueZero.ref
497 case TypeString:
498 return v.String() != ""
499 case TypeSymbol, TypeFunction, TypeObject:
500 return true
501 default:
502 panic("bad type")
503 }
504 }
505
506
507
508
509
510 func (v Value) String() string {
511 switch v.Type() {
512 case TypeString:
513 return jsString(v)
514 case TypeUndefined:
515 return "<undefined>"
516 case TypeNull:
517 return "<null>"
518 case TypeBoolean:
519 return "<boolean: " + jsString(v) + ">"
520 case TypeNumber:
521 return "<number: " + jsString(v) + ">"
522 case TypeSymbol:
523 return "<symbol>"
524 case TypeObject:
525 return "<object>"
526 case TypeFunction:
527 return "<function>"
528 default:
529 panic("bad type")
530 }
531 }
532
533 func jsString(v Value) string {
534 str, length := valuePrepareString(v.ref)
535 runtime.KeepAlive(v)
536 b := make([]byte, length)
537 valueLoadString(str, b)
538 finalizeRef(str)
539 return string(b)
540 }
541
542 func valuePrepareString(v ref) (ref, int)
543
544 func valueLoadString(v ref, b []byte)
545
546
547 func (v Value) InstanceOf(t Value) bool {
548 r := valueInstanceOf(v.ref, t.ref)
549 runtime.KeepAlive(v)
550 runtime.KeepAlive(t)
551 return r
552 }
553
554 func valueInstanceOf(v ref, t ref) bool
555
556
557
558
559 type ValueError struct {
560 Method string
561 Type Type
562 }
563
564 func (e *ValueError) Error() string {
565 return "syscall/js: call of " + e.Method + " on " + e.Type.String()
566 }
567
568
569
570
571 func CopyBytesToGo(dst []byte, src Value) int {
572 n, ok := copyBytesToGo(dst, src.ref)
573 runtime.KeepAlive(src)
574 if !ok {
575 panic("syscall/js: CopyBytesToGo: expected src to be an Uint8Array or Uint8ClampedArray")
576 }
577 return n
578 }
579
580 func copyBytesToGo(dst []byte, src ref) (int, bool)
581
582
583
584
585 func CopyBytesToJS(dst Value, src []byte) int {
586 n, ok := copyBytesToJS(dst.ref, src)
587 runtime.KeepAlive(dst)
588 if !ok {
589 panic("syscall/js: CopyBytesToJS: expected dst to be an Uint8Array or Uint8ClampedArray")
590 }
591 return n
592 }
593
594 func copyBytesToJS(dst ref, src []byte) (int, bool)
595
View as plain text