]> Cypherpunks.ru repositories - gostls13.git/commitdiff
syscall/js: add TypedArrayOf
authorRichard Musiol <mail@richard-musiol.de>
Wed, 27 Jun 2018 15:36:24 +0000 (17:36 +0200)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 27 Jun 2018 20:29:21 +0000 (20:29 +0000)
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 <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/crypto/rand/rand_js.go
src/net/http/roundtrip_js.go
src/syscall/fs_js.go
src/syscall/js/js.go
src/syscall/js/js_test.go
src/syscall/js/typedarray.go [new file with mode: 0644]

index 89247693a77cffedc7badda5172b85686fabac65..bb213963fd44f4727bfd681888dac726b6256d8a 100644 (file)
@@ -20,6 +20,8 @@ var jsCrypto = js.Global().Get("crypto")
 type reader struct{}
 
 func (r *reader) Read(b []byte) (int, error) {
-       jsCrypto.Call("getRandomValues", b)
+       a := js.TypedArrayOf(b)
+       jsCrypto.Call("getRandomValues", a)
+       a.Release()
        return len(b), nil
 }
index c183f87fff51c0f7a2a2b5f084df6480178bf3ad..1e6f83a666b0cd2e0b188fcfcd4b1619c13c3bc8 100644 (file)
@@ -166,7 +166,9 @@ func (r *streamReader) Read(p []byte) (n int, err error) {
                                return
                        }
                        value := make([]byte, result.Get("value").Get("byteLength").Int())
-                       js.ValueOf(value).Call("set", result.Get("value"))
+                       a := js.TypedArrayOf(value)
+                       a.Call("set", result.Get("value"))
+                       a.Release()
                        bCh <- value
                })
                defer success.Close()
@@ -227,7 +229,9 @@ func (r *arrayReader) Read(p []byte) (n int, err error) {
                        // Wrap the input ArrayBuffer with a Uint8Array
                        uint8arrayWrapper := js.Global().Get("Uint8Array").New(args[0])
                        value := make([]byte, uint8arrayWrapper.Get("byteLength").Int())
-                       js.ValueOf(value).Call("set", uint8arrayWrapper)
+                       a := js.TypedArrayOf(value)
+                       a.Call("set", uint8arrayWrapper)
+                       a.Release()
                        bCh <- value
                })
                defer success.Close()
index 64b7b8a1ad52687ecdb214513e57f0f9588f6826..36e9140759265574d57ae2f9c25862f3e600d94b 100644 (file)
@@ -374,7 +374,9 @@ func Read(fd int, b []byte) (int, error) {
                return n, err
        }
 
-       n, err := fsCall("readSync", fd, b, 0, len(b))
+       a := js.TypedArrayOf(b)
+       n, err := fsCall("readSync", fd, a, 0, len(b))
+       a.Release()
        if err != nil {
                return 0, err
        }
@@ -395,7 +397,9 @@ func Write(fd int, b []byte) (int, error) {
                return n, err
        }
 
-       n, err := fsCall("writeSync", fd, b, 0, len(b))
+       a := js.TypedArrayOf(b)
+       n, err := fsCall("writeSync", fd, a, 0, len(b))
+       a.Release()
        if err != nil {
                return 0, err
        }
@@ -405,7 +409,9 @@ func Write(fd int, b []byte) (int, error) {
 }
 
 func Pread(fd int, b []byte, offset int64) (int, error) {
-       n, err := fsCall("readSync", fd, b, 0, len(b), offset)
+       a := js.TypedArrayOf(b)
+       n, err := fsCall("readSync", fd, a, 0, len(b), offset)
+       a.Release()
        if err != nil {
                return 0, err
        }
@@ -413,7 +419,9 @@ func Pread(fd int, b []byte, offset int64) (int, error) {
 }
 
 func Pwrite(fd int, b []byte, offset int64) (int, error) {
-       n, err := fsCall("writeSync", fd, b, 0, len(b), offset)
+       a := js.TypedArrayOf(b)
+       n, err := fsCall("writeSync", fd, a, 0, len(b), offset)
+       a.Release()
        if err != nil {
                return 0, err
        }
index 8217c24c5e3f4319a6a3f81ef0c7e8aaa6deb409..a7b1ed8d29263eaef0008273d8168958115d2bd3 100644 (file)
@@ -81,13 +81,23 @@ func Global() Value {
        return valueGlobal
 }
 
-var uint8Array = valueGlobal.Get("Uint8Array")
-
-// ValueOf returns x as a JavaScript value.
+// 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                |
 func ValueOf(x interface{}) Value {
        switch x := x.(type) {
        case Value:
                return x
+       case TypedArray:
+               return x.Value
        case Callback:
                return x.enqueueFn
        case nil:
@@ -128,13 +138,8 @@ func ValueOf(x interface{}) Value {
                return floatValue(x)
        case string:
                return makeValue(stringVal(x))
-       case []byte:
-               if len(x) == 0 {
-                       return uint8Array.New(memory.Get("buffer"), 0, 0)
-               }
-               return uint8Array.New(memory.Get("buffer"), unsafe.Pointer(&x[0]), len(x))
        default:
-               panic("invalid value")
+               panic("ValueOf: invalid value")
        }
 }
 
index c96ad82850721bdadcb2760eef251d1ffa418a3b..0aaa65d054d5314b218566d59ac894e2abce91f6 100644 (file)
@@ -109,6 +109,28 @@ func TestObject(t *testing.T) {
        }
 }
 
+func TestTypedArrayOf(t *testing.T) {
+       testTypedArrayOf(t, "[]int8", []int8{0, -42, 0}, -42)
+       testTypedArrayOf(t, "[]int16", []int16{0, -42, 0}, -42)
+       testTypedArrayOf(t, "[]int32", []int32{0, -42, 0}, -42)
+       testTypedArrayOf(t, "[]uint8", []uint8{0, 42, 0}, 42)
+       testTypedArrayOf(t, "[]uint16", []uint16{0, 42, 0}, 42)
+       testTypedArrayOf(t, "[]uint32", []uint32{0, 42, 0}, 42)
+       testTypedArrayOf(t, "[]float32", []float32{0, -42.5, 0}, -42.5)
+       testTypedArrayOf(t, "[]float64", []float64{0, -42.5, 0}, -42.5)
+}
+
+func testTypedArrayOf(t *testing.T, name string, slice interface{}, want float64) {
+       t.Run(name, func(t *testing.T) {
+               a := js.TypedArrayOf(slice)
+               got := a.Index(1).Float()
+               a.Release()
+               if got != want {
+                       t.Errorf("got %#v, want %#v", got, want)
+               }
+       })
+}
+
 func TestNaN(t *testing.T) {
        want := js.ValueOf(math.NaN())
        got := dummys.Get("NaN")
diff --git a/src/syscall/js/typedarray.go b/src/syscall/js/typedarray.go
new file mode 100644 (file)
index 0000000..e824197
--- /dev/null
@@ -0,0 +1,103 @@
+// 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
+
+import (
+       "sync"
+       "unsafe"
+)
+
+var (
+       int8Array    = Global().Get("Int8Array")
+       int16Array   = Global().Get("Int16Array")
+       int32Array   = Global().Get("Int32Array")
+       uint8Array   = Global().Get("Uint8Array")
+       uint16Array  = Global().Get("Uint16Array")
+       uint32Array  = Global().Get("Uint32Array")
+       float32Array = Global().Get("Float32Array")
+       float64Array = Global().Get("Float64Array")
+)
+
+// TypedArray represents a JavaScript typed array.
+type TypedArray struct {
+       Value
+}
+
+// Release frees up resources allocated for the typed array.
+// The typed array and its buffer must not be accessed after calling Release.
+func (a TypedArray) Release() {
+       openTypedArraysMutex.Lock()
+       delete(openTypedArrays, a)
+       openTypedArraysMutex.Unlock()
+}
+
+var (
+       openTypedArraysMutex sync.Mutex
+       openTypedArrays      = make(map[TypedArray]interface{})
+)
+
+// TypedArrayOf returns a JavaScript typed array backed by the slice's underlying array.
+// It can be passed to functions of this package that accept interface{}, for example Value.Set and Value.Call.
+//
+// The supported types are []int8, []int16, []int32, []uint8, []uint16, []uint32, []float32 and []float64.
+// Passing an unsupported value causes a panic.
+//
+// TypedArray.Release must be called to free up resources when the typed array will not be used any more.
+func TypedArrayOf(slice interface{}) TypedArray {
+       a := TypedArray{typedArrayOf(slice)}
+       openTypedArraysMutex.Lock()
+       openTypedArrays[a] = slice
+       openTypedArraysMutex.Unlock()
+       return a
+}
+
+func typedArrayOf(slice interface{}) Value {
+       switch slice := slice.(type) {
+       case []int8:
+               if len(slice) == 0 {
+                       return int8Array.New(memory.Get("buffer"), 0, 0)
+               }
+               return int8Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
+       case []int16:
+               if len(slice) == 0 {
+                       return int16Array.New(memory.Get("buffer"), 0, 0)
+               }
+               return int16Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
+       case []int32:
+               if len(slice) == 0 {
+                       return int32Array.New(memory.Get("buffer"), 0, 0)
+               }
+               return int32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
+       case []uint8:
+               if len(slice) == 0 {
+                       return uint8Array.New(memory.Get("buffer"), 0, 0)
+               }
+               return uint8Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
+       case []uint16:
+               if len(slice) == 0 {
+                       return uint16Array.New(memory.Get("buffer"), 0, 0)
+               }
+               return uint16Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
+       case []uint32:
+               if len(slice) == 0 {
+                       return uint32Array.New(memory.Get("buffer"), 0, 0)
+               }
+               return uint32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
+       case []float32:
+               if len(slice) == 0 {
+                       return float32Array.New(memory.Get("buffer"), 0, 0)
+               }
+               return float32Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
+       case []float64:
+               if len(slice) == 0 {
+                       return float64Array.New(memory.Get("buffer"), 0, 0)
+               }
+               return float64Array.New(memory.Get("buffer"), unsafe.Pointer(&slice[0]), len(slice))
+       default:
+               panic("TypedArrayOf: not a supported slice")
+       }
+}