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