1
1
//go:build js && wasm
2
+ // +build js,wasm
2
3
3
4
// Copyright 2021 VMware, Inc.
4
5
// SPDX-License-Identifier: BSD-2-Clause
5
6
6
7
package wasm
7
8
8
9
import (
10
+ "reflect"
9
11
"syscall/js"
10
12
11
13
"github.com/google/uuid"
@@ -24,10 +26,8 @@ func NewTransportWasmBridge(busRef bus.EventBus) *TransportWasmBridge {
24
26
}
25
27
26
28
func (t * TransportWasmBridge ) sendRequestMessageWrapper () js.Func {
27
- jsFunc := js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
28
- // release the resources allocated for js.Func once this closure goes out of scope
29
- defer jsFunc .Release ()
30
-
29
+ var jsFunc js.Func
30
+ jsFunc = js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
31
31
var destId * uuid.UUID
32
32
channel := args [0 ].String ()
33
33
payload := args [1 ].JSValue ()
@@ -47,10 +47,8 @@ func (t *TransportWasmBridge) sendRequestMessageWrapper() js.Func {
47
47
}
48
48
49
49
func (t * TransportWasmBridge ) sendResponseMessageWrapper () js.Func {
50
- jsFunc := js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
51
- // release the resources allocated for js.Func once this closure goes out of scope
52
- defer jsFunc .Release ()
53
-
50
+ var jsFunc js.Func
51
+ jsFunc = js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
54
52
var destId * uuid.UUID
55
53
channel := args [0 ].String ()
56
54
payload := args [1 ].JSValue ()
@@ -70,13 +68,15 @@ func (t *TransportWasmBridge) sendResponseMessageWrapper() js.Func {
70
68
}
71
69
72
70
func (t * TransportWasmBridge ) listenStreamWrapper () js.Func {
73
- jsFunc := js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
71
+ var jsFunc js.Func
72
+ jsFunc = js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
74
73
handler , err := t .busRef .ListenStream (args [0 ].String ())
75
74
if err != nil {
76
75
errCtor := js .Global ().Get ("Error" )
77
76
return errCtor .New (err .Error ())
78
77
}
79
78
79
+ jsFuncRefs := make ([]js.Func , 0 )
80
80
closerFuncRef := js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
81
81
handler .Close ()
82
82
@@ -89,18 +89,19 @@ func (t *TransportWasmBridge) listenStreamWrapper() js.Func {
89
89
})
90
90
91
91
// store js.Func handles for resource cleanup later
92
- jsFuncRefs := []js. Func { jsFunc , getResponseHandlerJsCallbackFunc (handler ), closerFuncRef }
92
+ jsFuncRefs = append ( jsFuncRefs , getResponseHandlerJsCallbackFunc (handler ), closerFuncRef )
93
93
94
94
return map [string ]interface {}{
95
- "handle" : jsFuncRefs [1 ],
96
- "close" : jsFuncRefs [2 ],
95
+ "handle" : jsFuncRefs [0 ],
96
+ "close" : jsFuncRefs [1 ],
97
97
}
98
98
})
99
99
return jsFunc
100
100
}
101
101
102
102
func (t * TransportWasmBridge ) listenStreamWithIdWrapper () js.Func {
103
- jsFunc := js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
103
+ var jsFunc js.Func
104
+ jsFunc = js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
104
105
if len (args ) <= 1 {
105
106
errCtor := js .Global ().Get ("Error" )
106
107
js .Global ().Get ("console" ).Call ("error" , errCtor .New ("cannot listen to channel. missing destination ID" ))
@@ -119,6 +120,7 @@ func (t *TransportWasmBridge) listenStreamWithIdWrapper() js.Func {
119
120
return errCtor .New (err .Error ())
120
121
}
121
122
123
+ jsFuncRefs := make ([]js.Func , 0 )
122
124
closerFuncRef := js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
123
125
handler .Close ()
124
126
@@ -131,13 +133,14 @@ func (t *TransportWasmBridge) listenStreamWithIdWrapper() js.Func {
131
133
})
132
134
133
135
// store js.Func handles for resource cleanup later
134
- jsFuncRefs := []js. Func { jsFunc , getResponseHandlerJsCallbackFunc (handler ), closerFuncRef }
136
+ jsFuncRefs = append ( jsFuncRefs , getResponseHandlerJsCallbackFunc (handler ), closerFuncRef )
135
137
136
138
return map [string ]interface {}{
137
- "handle" : jsFuncRefs [1 ],
138
- "close" : jsFuncRefs [2 ],
139
+ "handle" : jsFuncRefs [0 ],
140
+ "close" : jsFuncRefs [1 ],
139
141
}
140
142
})
143
+ return jsFunc
141
144
}
142
145
143
146
func (t * TransportWasmBridge ) browserBusWrapper () js.Func {
@@ -167,20 +170,54 @@ func (t *TransportWasmBridge) SetUpTransportWasmAPI() {
167
170
168
171
func getResponseHandlerJsCallbackFunc (handler bus.MessageHandler ) js.Func {
169
172
return js .FuncOf (func (this js.Value , args []js.Value ) interface {} {
173
+ successCallback := args [0 ]
174
+ hasFailureCallback := len (args ) > 1
175
+
176
+ if (successCallback .Type () != js .TypeFunction ) || (hasFailureCallback && args [1 ].Type () != js .TypeFunction ) {
177
+ errCtor := js .Global ().Get ("Error" )
178
+ js .Global ().Get ("console" ).Call ("error" , errCtor .New ("invalid argument. not a function." ))
179
+ handler .Close ()
180
+ return nil
181
+ }
182
+
170
183
handler .Handle (func (m * model.Message ) {
171
- jsCallback := args [0 ]
172
- if jsCallback .Type () != js .TypeFunction {
173
- errCtor := js .Global ().Get ("Error" )
174
- js .Global ().Get ("console" ).Call ("error" , errCtor .New ("invalid argument. not a function." ))
175
- handler .Close ()
176
- return
184
+ // workaround for js.ValueOf() panicking for slices of primitive types such as []int, []string, []bool, or
185
+ // a map containing such containers for their keys. this is quite dirty but hopefully when generics arrive
186
+ // we should be able to handle this situation more elegantly & hope go team improves js package.
187
+ var payload = m .Payload
188
+ switch payload .(type ) {
189
+ case []string , []int , []bool , []float64 , map [string ]interface {}:
190
+ payload = toWasmSafePayload (payload )
177
191
}
178
- jsCallback .Invoke (m . Payload )
192
+ successCallback .Invoke (payload )
179
193
}, func (e error ) {
180
- errCtor := js .Global ().Get ("Error" )
181
- js .Global ().Get ("console" ).Call ("error" , errCtor .New (e .Error ()))
182
- handler .Close ()
194
+ if hasFailureCallback {
195
+ args [1 ].Invoke (e .Error ())
196
+ } else {
197
+ errCtor := js .Global ().Get ("Error" )
198
+ js .Global ().Get ("console" ).Call ("error" , errCtor .New (e .Error ()))
199
+ }
183
200
})
184
201
return nil
185
202
})
186
203
}
204
+
205
+ func toWasmSafePayload (payload interface {}) interface {} {
206
+ refl := reflect .ValueOf (payload )
207
+ if refl .Kind () == reflect .Map {
208
+ for _ , keyV := range refl .MapKeys () {
209
+ valV := refl .MapIndex (keyV )
210
+ refl .SetMapIndex (keyV , reflect .ValueOf (toWasmSafePayload (valV .Interface ())))
211
+ }
212
+ return payload
213
+ } else if refl .Kind () == reflect .Slice || refl .Kind () == reflect .Array {
214
+ var converted = make ([]interface {}, 0 )
215
+ for i := 0 ; i < refl .Len (); i ++ {
216
+ converted = append (converted , refl .Index (i ).Interface ())
217
+ }
218
+ return converted
219
+ }
220
+
221
+ // let js.ValueOf() handle the rest
222
+ return payload
223
+ }
0 commit comments