]> Cypherpunks repositories - gostls13.git/commitdiff
net/rpc: Create empty maps and slices as return type
authorMichael Fraenkel <michael.fraenkel@gmail.com>
Thu, 23 Mar 2017 19:08:43 +0000 (15:08 -0400)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 23 Mar 2017 20:53:47 +0000 (20:53 +0000)
When a map or slice is used as a return type create an empty value
rather than a nil value.

Fixes #19588

Change-Id: I577fd74956172329745d614ac37d4db8f737efb8
Reviewed-on: https://go-review.googlesource.com/38474
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/rpc/jsonrpc/all_test.go
src/net/rpc/server.go
src/net/rpc/server_test.go

index b811d3c0c7c28b9364258a141774dda0c93d7e8d..bbb8eb02918d68ff9049b4536c774ec58126150a 100644 (file)
@@ -13,6 +13,7 @@ import (
        "io/ioutil"
        "net"
        "net/rpc"
+       "reflect"
        "strings"
        "testing"
 )
@@ -55,8 +56,26 @@ func (t *Arith) Error(args *Args, reply *Reply) error {
        panic("ERROR")
 }
 
+type BuiltinTypes struct{}
+
+func (BuiltinTypes) Map(i int, reply *map[int]int) error {
+       (*reply)[i] = i
+       return nil
+}
+
+func (BuiltinTypes) Slice(i int, reply *[]int) error {
+       *reply = append(*reply, i)
+       return nil
+}
+
+func (BuiltinTypes) Array(i int, reply *[1]int) error {
+       (*reply)[0] = i
+       return nil
+}
+
 func init() {
        rpc.Register(new(Arith))
+       rpc.Register(BuiltinTypes{})
 }
 
 func TestServerNoParams(t *testing.T) {
@@ -182,6 +201,45 @@ func TestClient(t *testing.T) {
        }
 }
 
+func TestBuiltinTypes(t *testing.T) {
+       cli, srv := net.Pipe()
+       go ServeConn(srv)
+
+       client := NewClient(cli)
+       defer client.Close()
+
+       // Map
+       arg := 7
+       replyMap := map[int]int{}
+       err := client.Call("BuiltinTypes.Map", arg, &replyMap)
+       if err != nil {
+               t.Errorf("Map: expected no error but got string %q", err.Error())
+       }
+       if replyMap[arg] != arg {
+               t.Errorf("Map: expected %d got %d", arg, replyMap[arg])
+       }
+
+       // Slice
+       replySlice := []int{}
+       err = client.Call("BuiltinTypes.Slice", arg, &replySlice)
+       if err != nil {
+               t.Errorf("Slice: expected no error but got string %q", err.Error())
+       }
+       if e := []int{arg}; !reflect.DeepEqual(replySlice, e) {
+               t.Errorf("Slice: expected %v got %v", e, replySlice)
+       }
+
+       // Array
+       replyArray := [1]int{}
+       err = client.Call("BuiltinTypes.Array", arg, &replyArray)
+       if err != nil {
+               t.Errorf("Array: expected no error but got string %q", err.Error())
+       }
+       if e := [1]int{arg}; !reflect.DeepEqual(replyArray, e) {
+               t.Errorf("Array: expected %v got %v", e, replyArray)
+       }
+}
+
 func TestMalformedInput(t *testing.T) {
        cli, srv := net.Pipe()
        go cli.Write([]byte(`{id:1}`)) // invalid json
index 18ea629b0d6e0548d090b8a19f24c4f08100bad8..1bc570ba524357e093963a868e55eb2fa5f4904d 100644 (file)
@@ -571,6 +571,13 @@ func (server *Server) readRequest(codec ServerCodec) (service *service, mtype *m
        }
 
        replyv = reflect.New(mtype.ReplyType.Elem())
+
+       switch mtype.ReplyType.Elem().Kind() {
+       case reflect.Map:
+               replyv.Elem().Set(reflect.MakeMap(mtype.ReplyType.Elem()))
+       case reflect.Slice:
+               replyv.Elem().Set(reflect.MakeSlice(mtype.ReplyType.Elem(), 0, 0))
+       }
        return
 }
 
index b94ea6f6ab0f80b071ca3ef96a81fb2b4ea66cbe..fb97f82a2f73eb65ede361429e1f2b19736b25d3 100644 (file)
@@ -11,6 +11,7 @@ import (
        "log"
        "net"
        "net/http/httptest"
+       "reflect"
        "runtime"
        "strings"
        "sync"
@@ -85,6 +86,24 @@ type Embed struct {
        hidden
 }
 
+type BuiltinTypes struct{}
+
+func (BuiltinTypes) Map(args *Args, reply *map[int]int) error {
+       (*reply)[args.A] = args.B
+       return nil
+}
+
+func (BuiltinTypes) Slice(args *Args, reply *[]int) error {
+       *reply = append(*reply, args.A, args.B)
+       return nil
+}
+
+func (BuiltinTypes) Array(args *Args, reply *[2]int) error {
+       (*reply)[0] = args.A
+       (*reply)[1] = args.B
+       return nil
+}
+
 func listenTCP() (net.Listener, string) {
        l, e := net.Listen("tcp", "127.0.0.1:0") // any available address
        if e != nil {
@@ -97,6 +116,7 @@ func startServer() {
        Register(new(Arith))
        Register(new(Embed))
        RegisterName("net.rpc.Arith", new(Arith))
+       Register(BuiltinTypes{})
 
        var l net.Listener
        l, serverAddr = listenTCP()
@@ -326,6 +346,49 @@ func testHTTPRPC(t *testing.T, path string) {
        }
 }
 
+func TestBuiltinTypes(t *testing.T) {
+       once.Do(startServer)
+
+       client, err := DialHTTP("tcp", httpServerAddr)
+       if err != nil {
+               t.Fatal("dialing", err)
+       }
+       defer client.Close()
+
+       // Map
+       args := &Args{7, 8}
+       replyMap := map[int]int{}
+       err = client.Call("BuiltinTypes.Map", args, &replyMap)
+       if err != nil {
+               t.Errorf("Map: expected no error but got string %q", err.Error())
+       }
+       if replyMap[args.A] != args.B {
+               t.Errorf("Map: expected %d got %d", args.B, replyMap[args.A])
+       }
+
+       // Slice
+       args = &Args{7, 8}
+       replySlice := []int{}
+       err = client.Call("BuiltinTypes.Slice", args, &replySlice)
+       if err != nil {
+               t.Errorf("Slice: expected no error but got string %q", err.Error())
+       }
+       if e := []int{args.A, args.B}; !reflect.DeepEqual(replySlice, e) {
+               t.Errorf("Slice: expected %v got %v", e, replySlice)
+       }
+
+       // Array
+       args = &Args{7, 8}
+       replyArray := [2]int{}
+       err = client.Call("BuiltinTypes.Array", args, &replyArray)
+       if err != nil {
+               t.Errorf("Array: expected no error but got string %q", err.Error())
+       }
+       if e := [2]int{args.A, args.B}; !reflect.DeepEqual(replyArray, e) {
+               t.Errorf("Array: expected %v got %v", e, replyArray)
+       }
+}
+
 // CodecEmulator provides a client-like api and a ServerCodec interface.
 // Can be used to test ServeRequest.
 type CodecEmulator struct {