Skip to content

Commit

Permalink
syscall/js: improve Value.String() for non-string values
Browse files Browse the repository at this point in the history
This change modifies Value.String() to use the following
representations for non-string values:
  <undefined>
  <null>
  <boolean: true>
  <number: 42>
  <symbol>
  <object>
  <function>

It avoids JavaScript conversion semantics in the Go API and lowers the
risk of hidden bugs by unexpected conversions, e.g. the conversion
of the number 42 to the string "42". See discussion in golang#29642.

This is a breaking change, which are still allowed for syscall/js.
The impact should be small since it only affects uses of
Value.String() with non-string values, which should be uncommon.

Updates golang#29642.

Change-Id: I2c27be6e24befe8cb713031fbf66f7b6041e7148
Reviewed-on: https://go-review.googlesource.com/c/go/+/169757
Run-TryBot: Richard Musiol <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Brad Fitzpatrick <[email protected]>
  • Loading branch information
neelance authored and Richard Musiol committed Mar 28, 2019
1 parent ea9eddb commit 2bd767b
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 6 deletions.
30 changes: 28 additions & 2 deletions src/syscall/js/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,35 @@ func (v Value) Truthy() bool {
}
}

// String returns the value v converted to string according to JavaScript type conversions.
// String returns the value v as a string.
// String is a special case because of Go's String method convention. Unlike the other getters,
// it does not panic if v's Type is not TypeString. Instead, it returns a string of the form "<T>"
// or "<T: V>" where T is v's type and V is a string representation of v's value.
func (v Value) String() string {
str, length := valuePrepareString(v.ref)
switch v.Type() {
case TypeString:
return jsString(v.ref)
case TypeUndefined:
return "<undefined>"
case TypeNull:
return "<null>"
case TypeBoolean:
return "<boolean: " + jsString(v.ref) + ">"
case TypeNumber:
return "<number: " + jsString(v.ref) + ">"
case TypeSymbol:
return "<symbol>"
case TypeObject:
return "<object>"
case TypeFunction:
return "<function>"
default:
panic("bad type")
}
}

func jsString(v ref) string {
str, length := valuePrepareString(v)
b := make([]byte, length)
valueLoadString(str, b)
return string(b)
Expand Down
24 changes: 20 additions & 4 deletions src/syscall/js/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,26 @@ func TestString(t *testing.T) {
t.Errorf("same value not equal")
}

wantInt := "42"
o = dummys.Get("someInt")
if got := o.String(); got != wantInt {
t.Errorf("got %#v, want %#v", got, wantInt)
if got, want := js.Undefined().String(), "<undefined>"; got != want {
t.Errorf("got %#v, want %#v", got, want)
}
if got, want := js.Null().String(), "<null>"; got != want {
t.Errorf("got %#v, want %#v", got, want)
}
if got, want := js.ValueOf(true).String(), "<boolean: true>"; got != want {
t.Errorf("got %#v, want %#v", got, want)
}
if got, want := js.ValueOf(42.5).String(), "<number: 42.5>"; got != want {
t.Errorf("got %#v, want %#v", got, want)
}
if got, want := js.Global().Call("Symbol").String(), "<symbol>"; got != want {
t.Errorf("got %#v, want %#v", got, want)
}
if got, want := js.Global().String(), "<object>"; got != want {
t.Errorf("got %#v, want %#v", got, want)
}
if got, want := js.Global().Get("setTimeout").String(), "<function>"; got != want {
t.Errorf("got %#v, want %#v", got, want)
}
}

Expand Down

0 comments on commit 2bd767b

Please sign in to comment.