all: consistently treat nil message interface as an empty read-only message
To assist users in migrating from github.com/golang/protobuf
to google.golang.org/protobuf, make it such that functiionality like
proto.Marshal doesn't panic on nil interfaces.
Similar to how the new implementation treats a typed nil message
as an empty message, we treat a nil interface as being equivalent
to an "untyped" empty message.
Change-Id: Ic037f386f855b122f732b34d370e524b7c0d76f1
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/228837
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/encoding/protojson/encode.go b/encoding/protojson/encode.go
index 243d158..e545feb 100644
--- a/encoding/protojson/encode.go
+++ b/encoding/protojson/encode.go
@@ -116,6 +116,12 @@
return nil, err
}
+ // Treat nil message interface as an empty message,
+ // in which case the output in an empty JSON object.
+ if m == nil {
+ return []byte("{}"), nil
+ }
+
enc := encoder{internalEnc, o}
if err := enc.marshalMessage(m.ProtoReflect()); err != nil {
return nil, err
diff --git a/encoding/prototext/encode.go b/encoding/prototext/encode.go
index 83b65a6..e207c69 100644
--- a/encoding/prototext/encode.go
+++ b/encoding/prototext/encode.go
@@ -106,6 +106,12 @@
return nil, err
}
+ // Treat nil message interface as an empty message,
+ // in which case there is nothing to output.
+ if m == nil {
+ return []byte{}, nil
+ }
+
enc := encoder{internalEnc, o}
err = enc.marshalMessage(m.ProtoReflect(), false)
if err != nil {
diff --git a/proto/checkinit.go b/proto/checkinit.go
index d7c9923..3e9a6a2 100644
--- a/proto/checkinit.go
+++ b/proto/checkinit.go
@@ -12,6 +12,12 @@
// CheckInitialized returns an error if any required fields in m are not set.
func CheckInitialized(m Message) error {
+ // Treat a nil message interface as an "untyped" empty message,
+ // which we assume to have no required fields.
+ if m == nil {
+ return nil
+ }
+
return checkInitialized(m.ProtoReflect())
}
diff --git a/proto/encode.go b/proto/encode.go
index fa738a1..862c38b 100644
--- a/proto/encode.go
+++ b/proto/encode.go
@@ -74,12 +74,22 @@
// Marshal returns the wire-format encoding of m.
func Marshal(m Message) ([]byte, error) {
+ // Treat nil message interface as an empty message; nothing to output.
+ if m == nil {
+ return nil, nil
+ }
+
out, err := MarshalOptions{}.marshal(nil, m.ProtoReflect())
return out.Buf, err
}
// Marshal returns the wire-format encoding of m.
func (o MarshalOptions) Marshal(m Message) ([]byte, error) {
+ // Treat nil message interface as an empty message; nothing to output.
+ if m == nil {
+ return nil, nil
+ }
+
out, err := o.marshal(nil, m.ProtoReflect())
return out.Buf, err
}
@@ -87,6 +97,11 @@
// MarshalAppend appends the wire-format encoding of m to b,
// returning the result.
func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
+ // Treat nil message interface as an empty message; nothing to append.
+ if m == nil {
+ return b, nil
+ }
+
out, err := o.marshal(b, m.ProtoReflect())
return out.Buf, err
}
diff --git a/proto/merge.go b/proto/merge.go
index df72f98..27ab1a6 100644
--- a/proto/merge.go
+++ b/proto/merge.go
@@ -21,6 +21,9 @@
// It is semantically equivalent to unmarshaling the encoded form of src
// into dst with the UnmarshalOptions.Merge option specified.
func Merge(dst, src Message) {
+ // TODO: Should nil src be treated as semantically equivalent to a
+ // untyped, read-only, empty message? What about a nil dst?
+
dstMsg, srcMsg := dst.ProtoReflect(), src.ProtoReflect()
if dstMsg.Descriptor() != srcMsg.Descriptor() {
panic("descriptor mismatch")
diff --git a/proto/nil_test.go b/proto/nil_test.go
index 06e76ae..d4563a8 100644
--- a/proto/nil_test.go
+++ b/proto/nil_test.go
@@ -27,14 +27,12 @@
}{{
label: "Size",
test: func() { proto.Size(nil) },
- panic: true,
}, {
label: "Size",
test: func() { proto.Size(nilMsg) },
}, {
label: "Marshal",
test: func() { proto.Marshal(nil) },
- panic: true,
}, {
label: "Marshal",
test: func() { proto.Marshal(nilMsg) },
diff --git a/proto/size.go b/proto/size.go
index a4e72bd..11ba841 100644
--- a/proto/size.go
+++ b/proto/size.go
@@ -18,6 +18,11 @@
// Size returns the size in bytes of the wire-format encoding of m.
func (o MarshalOptions) Size(m Message) int {
+ // Treat a nil message interface as an empty message; nothing to output.
+ if m == nil {
+ return 0
+ }
+
return sizeMessage(m.ProtoReflect())
}