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 func stringVal(x string) ref
215
216
217 type Type int
218
219 const (
220 TypeUndefined Type = iota
221 TypeNull
222 TypeBoolean
223 TypeNumber
224 TypeString
225 TypeSymbol
226 TypeObject
227 TypeFunction
228 )
229
230 func (t Type) String() string {
231 switch t {
232 case TypeUndefined:
233 return "undefined"
234 case TypeNull:
235 return "null"
236 case TypeBoolean:
237 return "boolean"
238 case TypeNumber:
239 return "number"
240 case TypeString:
241 return "string"
242 case TypeSymbol:
243 return "symbol"
244 case TypeObject:
245 return "object"
246 case TypeFunction:
247 return "function"
248 default:
249 panic("bad type")
250 }
251 }
252
253 func (t Type) isObject() bool {
254 return t == TypeObject || t == TypeFunction
255 }
256
257
258
259 func (v Value) Type() Type {
260 switch v.ref {
261 case valueUndefined.ref:
262 return TypeUndefined
263 case valueNull.ref:
264 return TypeNull
265 case valueTrue.ref, valueFalse.ref:
266 return TypeBoolean
267 }
268 if v.isNumber() {
269 return TypeNumber
270 }
271 typeFlag := (v.ref >> 32) & 7
272 switch typeFlag {
273 case typeFlagObject:
274 return TypeObject
275 case typeFlagString:
276 return TypeString
277 case typeFlagSymbol:
278 return TypeSymbol
279 case typeFlagFunction:
280 return TypeFunction
281 default:
282 panic("bad type flag")
283 }
284 }
285
286
287
288 func (v Value) Get(p string) Value {
289 if vType := v.Type(); !vType.isObject() {
290 panic(&ValueError{"Value.Get", vType})
291 }
292 r := makeValue(valueGet(v.ref, p))
293 runtime.KeepAlive(v)
294 return r
295 }
296
297
298 func valueGet(v ref, p string) ref
299
300
301
302 func (v Value) Set(p string, x any) {
303 if vType := v.Type(); !vType.isObject() {
304 panic(&ValueError{"Value.Set", vType})
305 }
306 xv := ValueOf(x)
307 valueSet(v.ref, p, xv.ref)
308 runtime.KeepAlive(v)
309 runtime.KeepAlive(xv)
310 }
311
312
313 func valueSet(v ref, p string, x ref)
314
315
316
317 func (v Value) Delete(p string) {
318 if vType := v.Type(); !vType.isObject() {
319 panic(&ValueError{"Value.Delete", vType})
320 }
321 valueDelete(v.ref, p)
322 runtime.KeepAlive(v)
323 }
324
325
326 func valueDelete(v ref, p string)
327
328
329
330 func (v Value) Index(i int) Value {
331 if vType := v.Type(); !vType.isObject() {
332 panic(&ValueError{"Value.Index", vType})
333 }
334 r := makeValue(valueIndex(v.ref, i))
335 runtime.KeepAlive(v)
336 return r
337 }
338
339
340 func valueIndex(v ref, i int) ref
341
342
343
344 func (v Value) SetIndex(i int, x any) {
345 if vType := v.Type(); !vType.isObject() {
346 panic(&ValueError{"Value.SetIndex", vType})
347 }
348 xv := ValueOf(x)
349 valueSetIndex(v.ref, i, xv.ref)
350 runtime.KeepAlive(v)
351 runtime.KeepAlive(xv)
352 }
353
354
355 func valueSetIndex(v ref, i int, x ref)
356
357 func makeArgs(args []any) ([]Value, []ref) {
358 argVals := make([]Value, len(args))
359 argRefs := make([]ref, len(args))
360 for i, arg := range args {
361 v := ValueOf(arg)
362 argVals[i] = v
363 argRefs[i] = v.ref
364 }
365 return argVals, argRefs
366 }
367
368
369
370 func (v Value) Length() int {
371 if vType := v.Type(); !vType.isObject() {
372 panic(&ValueError{"Value.SetIndex", vType})
373 }
374 r := valueLength(v.ref)
375 runtime.KeepAlive(v)
376 return r
377 }
378
379
380 func valueLength(v ref) int
381
382
383
384
385 func (v Value) Call(m string, args ...any) Value {
386 argVals, argRefs := makeArgs(args)
387 res, ok := valueCall(v.ref, m, argRefs)
388 runtime.KeepAlive(v)
389 runtime.KeepAlive(argVals)
390 if !ok {
391 if vType := v.Type(); !vType.isObject() {
392 panic(&ValueError{"Value.Call", vType})
393 }
394 if propType := v.Get(m).Type(); propType != TypeFunction {
395 panic("syscall/js: Value.Call: property " + m + " is not a function, got " + propType.String())
396 }
397 panic(Error{makeValue(res)})
398 }
399 return makeValue(res)
400 }
401
402
403
404 func valueCall(v ref, m string, args []ref) (ref, bool)
405
406
407
408
409 func (v Value) Invoke(args ...any) Value {
410 argVals, argRefs := makeArgs(args)
411 res, ok := valueInvoke(v.ref, argRefs)
412 runtime.KeepAlive(v)
413 runtime.KeepAlive(argVals)
414 if !ok {
415 if vType := v.Type(); vType != TypeFunction {
416 panic(&ValueError{"Value.Invoke", vType})
417 }
418 panic(Error{makeValue(res)})
419 }
420 return makeValue(res)
421 }
422
423
424 func valueInvoke(v ref, args []ref) (ref, bool)
425
426
427
428
429 func (v Value) New(args ...any) 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
444 func valueNew(v ref, args []ref) (ref, bool)
445
446 func (v Value) isNumber() bool {
447 return v.ref == valueZero.ref ||
448 v.ref == valueNaN.ref ||
449 (v.ref != valueUndefined.ref && (v.ref>>32)&nanHead != nanHead)
450 }
451
452 func (v Value) float(method string) float64 {
453 if !v.isNumber() {
454 panic(&ValueError{method, v.Type()})
455 }
456 if v.ref == valueZero.ref {
457 return 0
458 }
459 return *(*float64)(unsafe.Pointer(&v.ref))
460 }
461
462
463
464 func (v Value) Float() float64 {
465 return v.float("Value.Float")
466 }
467
468
469
470 func (v Value) Int() int {
471 return int(v.float("Value.Int"))
472 }
473
474
475
476 func (v Value) Bool() bool {
477 switch v.ref {
478 case valueTrue.ref:
479 return true
480 case valueFalse.ref:
481 return false
482 default:
483 panic(&ValueError{"Value.Bool", v.Type()})
484 }
485 }
486
487
488
489
490 func (v Value) Truthy() bool {
491 switch v.Type() {
492 case TypeUndefined, TypeNull:
493 return false
494 case TypeBoolean:
495 return v.Bool()
496 case TypeNumber:
497 return v.ref != valueNaN.ref && v.ref != valueZero.ref
498 case TypeString:
499 return v.String() != ""
500 case TypeSymbol, TypeFunction, TypeObject:
501 return true
502 default:
503 panic("bad type")
504 }
505 }
506
507
508
509
510
511 func (v Value) String() string {
512 switch v.Type() {
513 case TypeString:
514 return jsString(v)
515 case TypeUndefined:
516 return "<undefined>"
517 case TypeNull:
518 return "<null>"
519 case TypeBoolean:
520 return "<boolean: " + jsString(v) + ">"
521 case TypeNumber:
522 return "<number: " + jsString(v) + ">"
523 case TypeSymbol:
524 return "<symbol>"
525 case TypeObject:
526 return "<object>"
527 case TypeFunction:
528 return "<function>"
529 default:
530 panic("bad type")
531 }
532 }
533
534 func jsString(v Value) string {
535 str, length := valuePrepareString(v.ref)
536 runtime.KeepAlive(v)
537 b := make([]byte, length)
538 valueLoadString(str, b)
539 finalizeRef(str)
540 return string(b)
541 }
542
543
544 func valuePrepareString(v ref) (ref, int)
545
546
547 func valueLoadString(v ref, b []byte)
548
549
550 func (v Value) InstanceOf(t Value) bool {
551 r := valueInstanceOf(v.ref, t.ref)
552 runtime.KeepAlive(v)
553 runtime.KeepAlive(t)
554 return r
555 }
556
557
558 func valueInstanceOf(v ref, t ref) bool
559
560
561
562
563 type ValueError struct {
564 Method string
565 Type Type
566 }
567
568 func (e *ValueError) Error() string {
569 return "syscall/js: call of " + e.Method + " on " + e.Type.String()
570 }
571
572
573
574
575 func CopyBytesToGo(dst []byte, src Value) int {
576 n, ok := copyBytesToGo(dst, src.ref)
577 runtime.KeepAlive(src)
578 if !ok {
579 panic("syscall/js: CopyBytesToGo: expected src to be a Uint8Array or Uint8ClampedArray")
580 }
581 return n
582 }
583
584
585 func copyBytesToGo(dst []byte, src ref) (int, bool)
586
587
588
589
590 func CopyBytesToJS(dst Value, src []byte) int {
591 n, ok := copyBytesToJS(dst.ref, src)
592 runtime.KeepAlive(dst)
593 if !ok {
594 panic("syscall/js: CopyBytesToJS: expected dst to be a Uint8Array or Uint8ClampedArray")
595 }
596 return n
597 }
598
599
600 func copyBytesToJS(dst ref, src []byte) (int, bool)
601
View as plain text