encoding: support weak fields in text and JSON unmarshaling

If the message for a weak field is linked in,
we treat it as if it were identical to a normal known field.
However, if the weak field is not linked in,
we treat it as if the field were not known.

Change-Id: I576d911deec98e13211304024a6353734d055465
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185457
Reviewed-by: Herbie Ong <herbie@google.com>
This commit is contained in:
Joe Tsai 2019-07-09 22:38:15 -07:00
parent 3d8e369c4e
commit d47ea19d2f
6 changed files with 63 additions and 14 deletions

View File

@ -186,6 +186,9 @@ Loop:
if fd == nil {
fd = fieldDescs.ByName(pref.Name(name))
}
if fd != nil && fd.IsWeak() && fd.Message().IsPlaceholder() {
fd = nil // reset since the weak reference is not linked in
}
}
if fd == nil {

View File

@ -9,12 +9,15 @@ import (
"testing"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/encoding/testprotos/pb2"
"google.golang.org/protobuf/encoding/testprotos/pb3"
"google.golang.org/protobuf/internal/flags"
pimpl "google.golang.org/protobuf/internal/impl"
"google.golang.org/protobuf/proto"
preg "google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/encoding/testprotos/pb2"
"google.golang.org/protobuf/encoding/testprotos/pb3"
testpb "google.golang.org/protobuf/internal/testprotos/test"
weakpb "google.golang.org/protobuf/internal/testprotos/test/weak1"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/emptypb"
@ -33,6 +36,7 @@ func TestUnmarshal(t *testing.T) {
wantMessage proto.Message
// TODO: verify expected error message substring.
wantErr bool
skip bool
}{{
desc: "proto2 empty message",
inputMessage: &pb2.Scalars{},
@ -2440,10 +2444,29 @@ func TestUnmarshal(t *testing.T) {
wantMessage: &anypb.Any{
TypeUrl: "type.googleapis.com/google.protobuf.Empty",
},
}, {
desc: "weak fields",
inputMessage: &testpb.TestWeak{},
inputText: `{"weak_message1":{"a":1}}`,
wantMessage: func() *testpb.TestWeak {
m := new(testpb.TestWeak)
m.SetWeakMessage1(&weakpb.WeakImportMessage1{A: proto.Int32(1)})
return m
}(),
skip: !flags.Proto1Legacy,
}, {
desc: "weak fields; unknown field",
inputMessage: &testpb.TestWeak{},
inputText: `{"weak_message1":{"a":1}, "weak_message2":{"a":1}}`,
wantErr: true, // weak_message2 is unknown since the package containing it is not imported
skip: !flags.Proto1Legacy,
}}
for _, tt := range tests {
tt := tt
if tt.skip {
continue
}
t.Run(tt.desc, func(t *testing.T) {
err := tt.umo.Unmarshal([]byte(tt.inputText), tt.inputMessage)
if err != nil && !tt.wantErr {

View File

@ -95,19 +95,19 @@ func (o UnmarshalOptions) unmarshalMessage(tmsg [][2]text.Value, m pref.Message)
case text.Name:
name, _ = tkey.Name()
fd = fieldDescs.ByName(name)
// The proto name of a group field is in all lowercase. However, the
// textproto field name is the type name. Check to make sure that
// group name is correct.
if fd == nil {
switch {
case fd == nil:
// The proto name of a group field is in all lowercase,
// while the textproto field name is the group message name.
// Check to make sure that group name is correct.
gd := fieldDescs.ByName(pref.Name(strings.ToLower(string(name))))
if gd != nil && gd.Kind() == pref.GroupKind && gd.Message().Name() == name {
fd = gd
}
} else {
if fd.Kind() == pref.GroupKind && fd.Message().Name() != name {
// Reset fd to nil because name does not match.
fd = nil
}
case fd.Kind() == pref.GroupKind && fd.Message().Name() != name:
fd = nil // reset since field name is actually the message name
case fd.IsWeak() && fd.Message().IsPlaceholder():
fd = nil // reset since the weak reference is not linked in
}
case text.String:
// Handle extensions only. This code path is not for Any.

View File

@ -9,12 +9,15 @@ import (
"testing"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/internal/flags"
pimpl "google.golang.org/protobuf/internal/impl"
"google.golang.org/protobuf/proto"
preg "google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/encoding/testprotos/pb2"
"google.golang.org/protobuf/encoding/testprotos/pb3"
testpb "google.golang.org/protobuf/internal/testprotos/test"
weakpb "google.golang.org/protobuf/internal/testprotos/test/weak1"
"google.golang.org/protobuf/types/known/anypb"
)
@ -26,6 +29,7 @@ func TestUnmarshal(t *testing.T) {
inputText string
wantMessage proto.Message
wantErr bool // TODO: Verify error message content.
skip bool
}{{
desc: "proto2 empty message",
inputMessage: &pb2.Scalars{},
@ -1484,10 +1488,29 @@ unknown: ""
type_url: "pb2.Nested"
`,
wantErr: true,
}, {
desc: "weak fields",
inputMessage: &testpb.TestWeak{},
inputText: `weak_message1:{a:1}`,
wantMessage: func() *testpb.TestWeak {
m := new(testpb.TestWeak)
m.SetWeakMessage1(&weakpb.WeakImportMessage1{A: proto.Int32(1)})
return m
}(),
skip: !flags.Proto1Legacy,
}, {
desc: "weak fields; unknown field",
inputMessage: &testpb.TestWeak{},
inputText: `weak_message1:{a:1} weak_message2:{a:1}`,
wantErr: true, // weak_message2 is unknown since the package containing it is not imported
skip: !flags.Proto1Legacy,
}}
for _, tt := range tests {
tt := tt
if tt.skip {
continue
}
t.Run(tt.desc, func(t *testing.T) {
err := tt.umo.Unmarshal([]byte(tt.inputText), tt.inputMessage)
if err != nil && !tt.wantErr {

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: test/weak2/test_weak.proto
package weak1
package weak2
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
@ -69,7 +69,7 @@ var file_test_weak2_test_weak_proto_rawDesc = []byte{
0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74,
0x2f, 0x77, 0x65, 0x61, 0x6b, 0x31,
0x2f, 0x77, 0x65, 0x61, 0x6b, 0x32,
}
var (

View File

@ -6,7 +6,7 @@ syntax = "proto2";
package goproto.proto.test.weak;
option go_package = "google.golang.org/protobuf/internal/testprotos/test/weak1";
option go_package = "google.golang.org/protobuf/internal/testprotos/test/weak2";
message WeakImportMessage2 {
optional int32 a = 1;