Skip to content

Commit cd53093

Browse files
neelanceRichard Musiol
authored and
Richard Musiol
committed
syscall/js: improve type checks of js.Value's methods
Add more explicit checks if the given js.Value is of the correct type instead of erroring on the JavaScript layer. Change-Id: I30b18a76820fb68f6ac279bb88a57456f5bab467 Reviewed-on: https://go-review.googlesource.com/c/go/+/168886 Run-TryBot: Richard Musiol <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 0ec302c commit cd53093

File tree

2 files changed

+95
-6
lines changed

2 files changed

+95
-6
lines changed

src/syscall/js/js.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ func (t Type) String() string {
216216
}
217217
}
218218

219+
func (t Type) isObject() bool {
220+
return t == TypeObject || t == TypeFunction
221+
}
222+
219223
// Type returns the JavaScript type of the value v. It is similar to JavaScript's typeof operator,
220224
// except that it returns TypeNull instead of TypeObject for null.
221225
func (v Value) Type() Type {
@@ -244,28 +248,44 @@ func (v Value) Type() Type {
244248
}
245249

246250
// Get returns the JavaScript property p of value v.
251+
// It panics if v is not a JavaScript object.
247252
func (v Value) Get(p string) Value {
253+
if vType := v.Type(); !vType.isObject() {
254+
panic(&ValueError{"Value.Get", vType})
255+
}
248256
return makeValue(valueGet(v.ref, p))
249257
}
250258

251259
func valueGet(v ref, p string) ref
252260

253261
// Set sets the JavaScript property p of value v to ValueOf(x).
262+
// It panics if v is not a JavaScript object.
254263
func (v Value) Set(p string, x interface{}) {
264+
if vType := v.Type(); !vType.isObject() {
265+
panic(&ValueError{"Value.Set", vType})
266+
}
255267
valueSet(v.ref, p, ValueOf(x).ref)
256268
}
257269

258270
func valueSet(v ref, p string, x ref)
259271

260272
// Index returns JavaScript index i of value v.
273+
// It panics if v is not a JavaScript object.
261274
func (v Value) Index(i int) Value {
275+
if vType := v.Type(); !vType.isObject() {
276+
panic(&ValueError{"Value.Index", vType})
277+
}
262278
return makeValue(valueIndex(v.ref, i))
263279
}
264280

265281
func valueIndex(v ref, i int) ref
266282

267283
// SetIndex sets the JavaScript index i of value v to ValueOf(x).
284+
// It panics if v is not a JavaScript object.
268285
func (v Value) SetIndex(i int, x interface{}) {
286+
if vType := v.Type(); !vType.isObject() {
287+
panic(&ValueError{"Value.SetIndex", vType})
288+
}
269289
valueSetIndex(v.ref, i, ValueOf(x).ref)
270290
}
271291

@@ -280,7 +300,11 @@ func makeArgs(args []interface{}) []ref {
280300
}
281301

282302
// Length returns the JavaScript property "length" of v.
303+
// It panics if v is not a JavaScript object.
283304
func (v Value) Length() int {
305+
if vType := v.Type(); !vType.isObject() {
306+
panic(&ValueError{"Value.SetIndex", vType})
307+
}
284308
return valueLength(v.ref)
285309
}
286310

@@ -292,7 +316,7 @@ func valueLength(v ref) int
292316
func (v Value) Call(m string, args ...interface{}) Value {
293317
res, ok := valueCall(v.ref, m, makeArgs(args))
294318
if !ok {
295-
if vType := v.Type(); vType != TypeObject && vType != TypeFunction { // check here to avoid overhead in success case
319+
if vType := v.Type(); !vType.isObject() { // check here to avoid overhead in success case
296320
panic(&ValueError{"Value.Call", vType})
297321
}
298322
if propType := v.Get(m).Type(); propType != TypeFunction {
@@ -306,7 +330,7 @@ func (v Value) Call(m string, args ...interface{}) Value {
306330
func valueCall(v ref, m string, args []ref) (ref, bool)
307331

308332
// Invoke does a JavaScript call of the value v with the given arguments.
309-
// It panics if v is not a function.
333+
// It panics if v is not a JavaScript function.
310334
// The arguments get mapped to JavaScript values according to the ValueOf function.
311335
func (v Value) Invoke(args ...interface{}) Value {
312336
res, ok := valueInvoke(v.ref, makeArgs(args))
@@ -322,11 +346,14 @@ func (v Value) Invoke(args ...interface{}) Value {
322346
func valueInvoke(v ref, args []ref) (ref, bool)
323347

324348
// New uses JavaScript's "new" operator with value v as constructor and the given arguments.
325-
// It panics if v is not a function.
349+
// It panics if v is not a JavaScript function.
326350
// The arguments get mapped to JavaScript values according to the ValueOf function.
327351
func (v Value) New(args ...interface{}) Value {
328352
res, ok := valueNew(v.ref, makeArgs(args))
329353
if !ok {
354+
if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
355+
panic(&ValueError{"Value.Invoke", vType})
356+
}
330357
panic(Error{makeValue(res)})
331358
}
332359
return makeValue(res)
@@ -350,17 +377,20 @@ func (v Value) float(method string) float64 {
350377
return *(*float64)(unsafe.Pointer(&v.ref))
351378
}
352379

353-
// Float returns the value v as a float64. It panics if v is not a JavaScript number.
380+
// Float returns the value v as a float64.
381+
// It panics if v is not a JavaScript number.
354382
func (v Value) Float() float64 {
355383
return v.float("Value.Float")
356384
}
357385

358-
// Int returns the value v truncated to an int. It panics if v is not a JavaScript number.
386+
// Int returns the value v truncated to an int.
387+
// It panics if v is not a JavaScript number.
359388
func (v Value) Int() int {
360389
return int(v.float("Value.Int"))
361390
}
362391

363-
// Bool returns the value v as a bool. It panics if v is not a JavaScript boolean.
392+
// Bool returns the value v as a bool.
393+
// It panics if v is not a JavaScript boolean.
364394
func (v Value) Bool() bool {
365395
switch v.ref {
366396
case valueTrue.ref:

src/syscall/js/js_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,41 @@ func TestLength(t *testing.T) {
202202
}
203203
}
204204

205+
func TestGet(t *testing.T) {
206+
// positive cases get tested per type
207+
208+
expectValueError(t, func() {
209+
dummys.Get("zero").Get("badField")
210+
})
211+
}
212+
213+
func TestSet(t *testing.T) {
214+
// positive cases get tested per type
215+
216+
expectValueError(t, func() {
217+
dummys.Get("zero").Set("badField", 42)
218+
})
219+
}
220+
205221
func TestIndex(t *testing.T) {
206222
if got := dummys.Get("someArray").Index(1).Int(); got != 42 {
207223
t.Errorf("got %#v, want %#v", got, 42)
208224
}
225+
226+
expectValueError(t, func() {
227+
dummys.Get("zero").Index(1)
228+
})
209229
}
210230

211231
func TestSetIndex(t *testing.T) {
212232
dummys.Get("someArray").SetIndex(2, 99)
213233
if got := dummys.Get("someArray").Index(2).Int(); got != 99 {
214234
t.Errorf("got %#v, want %#v", got, 99)
215235
}
236+
237+
expectValueError(t, func() {
238+
dummys.Get("zero").SetIndex(2, 99)
239+
})
216240
}
217241

218242
func TestCall(t *testing.T) {
@@ -223,19 +247,34 @@ func TestCall(t *testing.T) {
223247
if got := dummys.Call("add", js.Global().Call("eval", "40"), 2).Int(); got != 42 {
224248
t.Errorf("got %#v, want %#v", got, 42)
225249
}
250+
251+
expectPanic(t, func() {
252+
dummys.Call("zero")
253+
})
254+
expectValueError(t, func() {
255+
dummys.Get("zero").Call("badMethod")
256+
})
226257
}
227258

228259
func TestInvoke(t *testing.T) {
229260
var i int64 = 40
230261
if got := dummys.Get("add").Invoke(i, 2).Int(); got != 42 {
231262
t.Errorf("got %#v, want %#v", got, 42)
232263
}
264+
265+
expectValueError(t, func() {
266+
dummys.Get("zero").Invoke()
267+
})
233268
}
234269

235270
func TestNew(t *testing.T) {
236271
if got := js.Global().Get("Array").New(42).Length(); got != 42 {
237272
t.Errorf("got %#v, want %#v", got, 42)
238273
}
274+
275+
expectValueError(t, func() {
276+
dummys.Get("zero").New()
277+
})
239278
}
240279

241280
func TestInstanceOf(t *testing.T) {
@@ -379,3 +418,23 @@ func TestTruthy(t *testing.T) {
379418
t.Errorf("got %#v, want %#v", got, want)
380419
}
381420
}
421+
422+
func expectValueError(t *testing.T, fn func()) {
423+
defer func() {
424+
err := recover()
425+
if _, ok := err.(*js.ValueError); !ok {
426+
t.Errorf("expected *js.ValueError, got %T", err)
427+
}
428+
}()
429+
fn()
430+
}
431+
432+
func expectPanic(t *testing.T, fn func()) {
433+
defer func() {
434+
err := recover()
435+
if err == nil {
436+
t.Errorf("expected panic")
437+
}
438+
}()
439+
fn()
440+
}

0 commit comments

Comments
 (0)