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>
diff --git a/encoding/protojson/decode.go b/encoding/protojson/decode.go
index 8dad1d5..d97a60c 100644
--- a/encoding/protojson/decode.go
+++ b/encoding/protojson/decode.go
@@ -186,6 +186,9 @@
 			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 {
diff --git a/encoding/protojson/decode_test.go b/encoding/protojson/decode_test.go
index 00b1a54..03bd10a 100644
--- a/encoding/protojson/decode_test.go
+++ b/encoding/protojson/decode_test.go
@@ -9,12 +9,15 @@
 	"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 @@
 		wantMessage  proto.Message
 		// TODO: verify expected error message substring.
 		wantErr bool
+		skip    bool
 	}{{
 		desc:         "proto2 empty message",
 		inputMessage: &pb2.Scalars{},
@@ -2440,10 +2444,29 @@
 		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 {
diff --git a/encoding/prototext/decode.go b/encoding/prototext/decode.go
index df05f95..7874410 100644
--- a/encoding/prototext/decode.go
+++ b/encoding/prototext/decode.go
@@ -95,19 +95,19 @@
 		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.
diff --git a/encoding/prototext/decode_test.go b/encoding/prototext/decode_test.go
index 66c77f3..31de4d7 100644
--- a/encoding/prototext/decode_test.go
+++ b/encoding/prototext/decode_test.go
@@ -9,12 +9,15 @@
 	"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 @@
 		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 @@
 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 {
diff --git a/internal/testprotos/test/weak2/test_weak.pb.go b/internal/testprotos/test/weak2/test_weak.pb.go
index 0ce6419..bc6f9df 100644
--- a/internal/testprotos/test/weak2/test_weak.pb.go
+++ b/internal/testprotos/test/weak2/test_weak.pb.go
@@ -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 @@
 	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 (
diff --git a/internal/testprotos/test/weak2/test_weak.proto b/internal/testprotos/test/weak2/test_weak.proto
index 456af8a..eb4d0ff 100644
--- a/internal/testprotos/test/weak2/test_weak.proto
+++ b/internal/testprotos/test/weak2/test_weak.proto
@@ -6,7 +6,7 @@
 
 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;