Skip to content

Commit bafe466

Browse files
neelancebradfitz
authored andcommitted
syscall/js: add TypedArrayOf
The new function js.TypedArrayOf returns a JavaScript typed array for a given slice. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays This change also changes js.ValueOf to not accept a []byte any more. Fixes #25532. Change-Id: I8c7bc98ca4e21c3514d19eee7a1f92388d74ab2a Reviewed-on: https://go-review.googlesource.com/121215 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 63a4acb commit bafe466

File tree

6 files changed

+160
-16
lines changed

6 files changed

+160
-16
lines changed

src/crypto/rand/rand_js.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ var jsCrypto = js.Global().Get("crypto")
2020
type reader struct{}
2121

2222
func (r *reader) Read(b []byte) (int, error) {
23-
jsCrypto.Call("getRandomValues", b)
23+
a := js.TypedArrayOf(b)
24+
jsCrypto.Call("getRandomValues", a)
25+
a.Release()
2426
return len(b), nil
2527
}

src/net/http/roundtrip_js.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ func (r *streamReader) Read(p []byte) (n int, err error) {
166166
return
167167
}
168168
value := make([]byte, result.Get("value").Get("byteLength").Int())
169-
js.ValueOf(value).Call("set", result.Get("value"))
169+
a := js.TypedArrayOf(value)
170+
a.Call("set", result.Get("value"))
171+
a.Release()
170172
bCh <- value
171173
})
172174
defer success.Close()
@@ -227,7 +229,9 @@ func (r *arrayReader) Read(p []byte) (n int, err error) {
227229
// Wrap the input ArrayBuffer with a Uint8Array
228230
uint8arrayWrapper := js.Global().Get("Uint8Array").New(args[0])
229231
value := make([]byte, uint8arrayWrapper.Get("byteLength").Int())
230-
js.ValueOf(value).Call("set", uint8arrayWrapper)
232+
a := js.TypedArrayOf(value)
233+
a.Call("set", uint8arrayWrapper)
234+
a.Release()
231235
bCh <- value
232236
})
233237
defer success.Close()

src/syscall/fs_js.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,9 @@ func Read(fd int, b []byte) (int, error) {
374374
return n, err
375375
}
376376

377-
n, err := fsCall("readSync", fd, b, 0, len(b))
377+
a := js.TypedArrayOf(b)
378+
n, err := fsCall("readSync", fd, a, 0, len(b))
379+
a.Release()
378380
if err != nil {
379381
return 0, err
380382
}
@@ -395,7 +397,9 @@ func Write(fd int, b []byte) (int, error) {
395397
return n, err
396398
}
397399

398-
n, err := fsCall("writeSync", fd, b, 0, len(b))
400+
a := js.TypedArrayOf(b)
401+
n, err := fsCall("writeSync", fd, a, 0, len(b))
402+
a.Release()
399403
if err != nil {
400404
return 0, err
401405
}
@@ -405,15 +409,19 @@ func Write(fd int, b []byte) (int, error) {
405409
}
406410

407411
func Pread(fd int, b []byte, offset int64) (int, error) {
408-
n, err := fsCall("readSync", fd, b, 0, len(b), offset)
412+
a := js.TypedArrayOf(b)
413+
n, err := fsCall("readSync", fd, a, 0, len(b), offset)
414+
a.Release()
409415
if err != nil {
410416
return 0, err
411417
}
412418
return n.Int(), nil
413419
}
414420

415421
func Pwrite(fd int, b []byte, offset int64) (int, error) {
416-
n, err := fsCall("writeSync", fd, b, 0, len(b), offset)
422+
a := js.TypedArrayOf(b)
423+
n, err := fsCall("writeSync", fd, a, 0, len(b), offset)
424+
a.Release()
417425
if err != nil {
418426
return 0, err
419427
}

src/syscall/js/js.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,23 @@ func Global() Value {
8181
return valueGlobal
8282
}
8383

84-
var uint8Array = valueGlobal.Get("Uint8Array")
85-
86-
// ValueOf returns x as a JavaScript value.
84+
// ValueOf returns x as a JavaScript value:
85+
//
86+
// | Go | JavaScript |
87+
// | --------------------- | --------------------- |
88+
// | js.Value | [its value] |
89+
// | js.TypedArray | [typed array] |
90+
// | js.Callback | function |
91+
// | nil | null |
92+
// | bool | boolean |
93+
// | integers and floats | number |
94+
// | string | string |
8795
func ValueOf(x interface{}) Value {
8896
switch x := x.(type) {
8997
case Value:
9098
return x
99+
case TypedArray:
100+
return x.Value
91101
case Callback:
92102
return x.enqueueFn
93103
case nil:
@@ -128,13 +138,8 @@ func ValueOf(x interface{}) Value {
128138
return floatValue(x)
129139
case string:
130140
return makeValue(stringVal(x))
131-
case []byte:
132-
if len(x) == 0 {
133-
return uint8Array.New(memory.Get("buffer"), 0, 0)
134-
}
135-
return uint8Array.New(memory.Get("buffer"), unsafe.Pointer(&x[0]), len(x))
136141
default:
137-
panic("invalid value")
142+
panic("ValueOf: invalid value")
138143
}
139144
}
140145

src/syscall/js/js_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,28 @@ func TestObject(t *testing.T) {
109109
}
110110
}
111111

112+
func TestTypedArrayOf(t *testing.T) {
113+
testTypedArrayOf(t, "[]int8", []int8{0, -42, 0}, -42)
114+
testTypedArrayOf(t, "[]int16", []int16{0, -42, 0}, -42)
115+
testTypedArrayOf(t, "[]int32", []int32{0, -42, 0}, -42)
116+
testTypedArrayOf(t, "[]uint8", []uint8{0, 42, 0}, 42)
117+
testTypedArrayOf(t, "[]uint16", []uint16{0, 42, 0}, 42)
118+
testTypedArrayOf(t, "[]uint32", []uint32{0, 42, 0}, 42)
119+
testTypedArrayOf(t, "[]float32", []float32{0, -42.5, 0}, -42.5)
120+
testTypedArrayOf(t, "[]float64", []float64{0, -42.5, 0}, -42.5)
121+
}
122+
123+
func testTypedArrayOf(t *testing.T, name string, slice interface{}, want float64) {
124+
t.Run(name, func(t *testing.T) {
125+
a := js.TypedArrayOf(slice)
126+
got := a.Index(1).Float()
127+
a.Release()
128+
if got != want {
129+
t.Errorf("got %#v, want %#v", got, want)
130+
}
131+
})
132+
}
133+
112134
func TestNaN(t *testing.T) {
113135
want := js.ValueOf(math.NaN())
114136
got := dummys.Get("NaN")

src/syscall/js/typedarray.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build js,wasm
6+
7+
package js
8+
9+
import (
10+
"sync"
11+
"unsafe"
12+
)
13+
14+
var (
15+
int8Array = Global().Get("Int8Array")
16+
int16Array = Global().Get("Int16Array")
17+
int32Array = Global().Get("Int32Array")
18+
uint8Array = Global().Get("Uint8Array")
19+
uint16Array = Global().Get("Uint16Array")
20+
uint32Array = Global().Get("Uint32Array")
21+
float32Array = Global().Get("Float32Array")
22+
float64Array = Global().Get("Float64Array")
23+
)
24+
25+
// TypedArray represents a JavaScript typed array.
26+
type TypedArray struct {
27+
Value
28+
}
29+
30+
// Release frees up resources allocated for the typed array.
31+
// The typed array and its buffer must not be accessed after calling Release.
32+
func (a TypedArray) Release() {
33+
openTypedArraysMutex.Lock()
34+
delete(openTypedArrays, a)
35+
openTypedArraysMutex.Unlock()
36+
}
37+
38+
var (
39+
openTypedArraysMutex sync.Mutex
40+
openTypedArrays = make(map[TypedArray]interface{})
41+
)
42+
43+
// TypedArrayOf returns a JavaScript typed array backed by the slice's underlying array.
44+
// It can be passed to functions of this package that accept interface{}, for example Value.Set and Value.Call.
45+
//
46+
// The supported types are []int8, []int16, []int32, []uint8, []uint16, []uint32, []float32 and []float64.
47+
// Passing an unsupported value causes a panic.
48+
//
49+
// TypedArray.Release must be called to free up resources when the typed array will not be used any more.
50+
func TypedArrayOf(slice interface{}) TypedArray {
51+
a := TypedArray{typedArrayOf(slice)}
52+
openTypedArraysMutex.Lock()
53+
openTypedArrays[a] = slice
54+
openTypedArraysMutex.Unlock()
55+
return a
56+
}
57+
58+
func typedArrayOf(slice interface{}) Value {
59+
switch slice := slice.(type) {
60+
case []int8:
61+
if len(slice) == 0 {
62+
return int8Array.New(memory.Get("buffer"), 0, 0)
63+
}
64+
return int8Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
65+
case []int16:
66+
if len(slice) == 0 {
67+
return int16Array.New(memory.Get("buffer"), 0, 0)
68+
}
69+
return int16Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
70+
case []int32:
71+
if len(slice) == 0 {
72+
return int32Array.New(memory.Get("buffer"), 0, 0)
73+
}
74+
return int32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
75+
case []uint8:
76+
if len(slice) == 0 {
77+
return uint8Array.New(memory.Get("buffer"), 0, 0)
78+
}
79+
return uint8Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
80+
case []uint16:
81+
if len(slice) == 0 {
82+
return uint16Array.New(memory.Get("buffer"), 0, 0)
83+
}
84+
return uint16Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
85+
case []uint32:
86+
if len(slice) == 0 {
87+
return uint32Array.New(memory.Get("buffer"), 0, 0)
88+
}
89+
return uint32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
90+
case []float32:
91+
if len(slice) == 0 {
92+
return float32Array.New(memory.Get("buffer"), 0, 0)
93+
}
94+
return float32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
95+
case []float64:
96+
if len(slice) == 0 {
97+
return float64Array.New(memory.Get("buffer"), 0, 0)
98+
}
99+
return float64Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
100+
default:
101+
panic("TypedArrayOf: not a supported slice")
102+
}
103+
}

0 commit comments

Comments
 (0)