internal/impl: support wrapping Go structs to implement proto.Message

Given a pointer to a Go struct (that is well-formed according to the v1
struct field layout), wrap the type such that it implements the v2
protoreflect.Message interface.

Change-Id: I5987cad0d22e53970c613cdbbb1cfd4210897f69
Reviewed-on: https://go-review.googlesource.com/c/138897
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/message_test.go b/internal/impl/message_test.go
index f870119..c21c40e 100644
--- a/internal/impl/message_test.go
+++ b/internal/impl/message_test.go
@@ -14,6 +14,14 @@
 	ptype "github.com/golang/protobuf/v2/reflect/prototype"
 )
 
+func mustMakeMessageDesc(t ptype.StandaloneMessage) pref.MessageDescriptor {
+	md, err := ptype.NewMessage(&t)
+	if err != nil {
+		panic(err)
+	}
+	return md
+}
+
 type (
 	MyBool    bool
 	MyInt32   int32
@@ -52,6 +60,36 @@
 	MyBytesA  *MyString  `protobuf:"22"`
 }
 
+var scalarProto2Desc = mustMakeMessageDesc(ptype.StandaloneMessage{
+	Syntax:   pref.Proto2,
+	FullName: "ScalarProto2",
+	Fields: []ptype.Field{
+		{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: pref.ValueOf(bool(true))},
+		{Name: "f2", Number: 2, Cardinality: pref.Optional, Kind: pref.Int32Kind, Default: pref.ValueOf(int32(2))},
+		{Name: "f3", Number: 3, Cardinality: pref.Optional, Kind: pref.Int64Kind, Default: pref.ValueOf(int64(3))},
+		{Name: "f4", Number: 4, Cardinality: pref.Optional, Kind: pref.Uint32Kind, Default: pref.ValueOf(uint32(4))},
+		{Name: "f5", Number: 5, Cardinality: pref.Optional, Kind: pref.Uint64Kind, Default: pref.ValueOf(uint64(5))},
+		{Name: "f6", Number: 6, Cardinality: pref.Optional, Kind: pref.FloatKind, Default: pref.ValueOf(float32(6))},
+		{Name: "f7", Number: 7, Cardinality: pref.Optional, Kind: pref.DoubleKind, Default: pref.ValueOf(float64(7))},
+		{Name: "f8", Number: 8, Cardinality: pref.Optional, Kind: pref.StringKind, Default: pref.ValueOf(string("8"))},
+		{Name: "f9", Number: 9, Cardinality: pref.Optional, Kind: pref.StringKind, Default: pref.ValueOf(string("9"))},
+		{Name: "f10", Number: 10, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("10"))},
+		{Name: "f11", Number: 11, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("11"))},
+
+		{Name: "f12", Number: 12, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: pref.ValueOf(bool(true))},
+		{Name: "f13", Number: 13, Cardinality: pref.Optional, Kind: pref.Int32Kind, Default: pref.ValueOf(int32(13))},
+		{Name: "f14", Number: 14, Cardinality: pref.Optional, Kind: pref.Int64Kind, Default: pref.ValueOf(int64(14))},
+		{Name: "f15", Number: 15, Cardinality: pref.Optional, Kind: pref.Uint32Kind, Default: pref.ValueOf(uint32(15))},
+		{Name: "f16", Number: 16, Cardinality: pref.Optional, Kind: pref.Uint64Kind, Default: pref.ValueOf(uint64(16))},
+		{Name: "f17", Number: 17, Cardinality: pref.Optional, Kind: pref.FloatKind, Default: pref.ValueOf(float32(17))},
+		{Name: "f18", Number: 18, Cardinality: pref.Optional, Kind: pref.DoubleKind, Default: pref.ValueOf(float64(18))},
+		{Name: "f19", Number: 19, Cardinality: pref.Optional, Kind: pref.StringKind, Default: pref.ValueOf(string("19"))},
+		{Name: "f20", Number: 20, Cardinality: pref.Optional, Kind: pref.StringKind, Default: pref.ValueOf(string("20"))},
+		{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("21"))},
+		{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("22"))},
+	},
+})
+
 type ScalarProto3 struct {
 	Bool    bool    `protobuf:"1"`
 	Int32   int32   `protobuf:"2"`
@@ -78,7 +116,37 @@
 	MyBytesA  MyString  `protobuf:"22"`
 }
 
-func TestFieldFuncs(t *testing.T) {
+var scalarProto3Desc = mustMakeMessageDesc(ptype.StandaloneMessage{
+	Syntax:   pref.Proto3,
+	FullName: "ScalarProto3",
+	Fields: []ptype.Field{
+		{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind},
+		{Name: "f2", Number: 2, Cardinality: pref.Optional, Kind: pref.Int32Kind},
+		{Name: "f3", Number: 3, Cardinality: pref.Optional, Kind: pref.Int64Kind},
+		{Name: "f4", Number: 4, Cardinality: pref.Optional, Kind: pref.Uint32Kind},
+		{Name: "f5", Number: 5, Cardinality: pref.Optional, Kind: pref.Uint64Kind},
+		{Name: "f6", Number: 6, Cardinality: pref.Optional, Kind: pref.FloatKind},
+		{Name: "f7", Number: 7, Cardinality: pref.Optional, Kind: pref.DoubleKind},
+		{Name: "f8", Number: 8, Cardinality: pref.Optional, Kind: pref.StringKind},
+		{Name: "f9", Number: 9, Cardinality: pref.Optional, Kind: pref.StringKind},
+		{Name: "f10", Number: 10, Cardinality: pref.Optional, Kind: pref.BytesKind},
+		{Name: "f11", Number: 11, Cardinality: pref.Optional, Kind: pref.BytesKind},
+
+		{Name: "f12", Number: 12, Cardinality: pref.Optional, Kind: pref.BoolKind},
+		{Name: "f13", Number: 13, Cardinality: pref.Optional, Kind: pref.Int32Kind},
+		{Name: "f14", Number: 14, Cardinality: pref.Optional, Kind: pref.Int64Kind},
+		{Name: "f15", Number: 15, Cardinality: pref.Optional, Kind: pref.Uint32Kind},
+		{Name: "f16", Number: 16, Cardinality: pref.Optional, Kind: pref.Uint64Kind},
+		{Name: "f17", Number: 17, Cardinality: pref.Optional, Kind: pref.FloatKind},
+		{Name: "f18", Number: 18, Cardinality: pref.Optional, Kind: pref.DoubleKind},
+		{Name: "f19", Number: 19, Cardinality: pref.Optional, Kind: pref.StringKind},
+		{Name: "f20", Number: 20, Cardinality: pref.Optional, Kind: pref.StringKind},
+		{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind},
+		{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind},
+	},
+})
+
+func TestKnownFields(t *testing.T) {
 	V := pref.ValueOf
 	type (
 		// has checks that each field matches the list.
@@ -97,39 +165,11 @@
 
 	tests := []struct {
 		structType  reflect.Type
-		messageDesc ptype.StandaloneMessage
+		messageDesc pref.MessageDescriptor
 		testOps     []testOp
 	}{{
-		structType: reflect.TypeOf(ScalarProto2{}),
-		messageDesc: ptype.StandaloneMessage{
-			Syntax:   pref.Proto2,
-			FullName: "ScalarProto2",
-			Fields: []ptype.Field{
-				{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: V(bool(true))},
-				{Name: "f2", Number: 2, Cardinality: pref.Optional, Kind: pref.Int32Kind, Default: V(int32(2))},
-				{Name: "f3", Number: 3, Cardinality: pref.Optional, Kind: pref.Int64Kind, Default: V(int64(3))},
-				{Name: "f4", Number: 4, Cardinality: pref.Optional, Kind: pref.Uint32Kind, Default: V(uint32(4))},
-				{Name: "f5", Number: 5, Cardinality: pref.Optional, Kind: pref.Uint64Kind, Default: V(uint64(5))},
-				{Name: "f6", Number: 6, Cardinality: pref.Optional, Kind: pref.FloatKind, Default: V(float32(6))},
-				{Name: "f7", Number: 7, Cardinality: pref.Optional, Kind: pref.DoubleKind, Default: V(float64(7))},
-				{Name: "f8", Number: 8, Cardinality: pref.Optional, Kind: pref.StringKind, Default: V(string("8"))},
-				{Name: "f9", Number: 9, Cardinality: pref.Optional, Kind: pref.StringKind, Default: V(string("9"))},
-				{Name: "f10", Number: 10, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("10"))},
-				{Name: "f11", Number: 11, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("11"))},
-
-				{Name: "f12", Number: 12, Cardinality: pref.Optional, Kind: pref.BoolKind, Default: V(bool(true))},
-				{Name: "f13", Number: 13, Cardinality: pref.Optional, Kind: pref.Int32Kind, Default: V(int32(13))},
-				{Name: "f14", Number: 14, Cardinality: pref.Optional, Kind: pref.Int64Kind, Default: V(int64(14))},
-				{Name: "f15", Number: 15, Cardinality: pref.Optional, Kind: pref.Uint32Kind, Default: V(uint32(15))},
-				{Name: "f16", Number: 16, Cardinality: pref.Optional, Kind: pref.Uint64Kind, Default: V(uint64(16))},
-				{Name: "f17", Number: 17, Cardinality: pref.Optional, Kind: pref.FloatKind, Default: V(float32(17))},
-				{Name: "f18", Number: 18, Cardinality: pref.Optional, Kind: pref.DoubleKind, Default: V(float64(18))},
-				{Name: "f19", Number: 19, Cardinality: pref.Optional, Kind: pref.StringKind, Default: V(string("19"))},
-				{Name: "f20", Number: 20, Cardinality: pref.Optional, Kind: pref.StringKind, Default: V(string("20"))},
-				{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("21"))},
-				{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("22"))},
-			},
-		},
+		structType:  reflect.TypeOf(ScalarProto2{}),
+		messageDesc: scalarProto2Desc,
 		testOps: []testOp{
 			hasOp([]bool{
 				false, false, false, false, false, false, false, false, false, false, false,
@@ -158,36 +198,8 @@
 			equalOp{&ScalarProto2{}},
 		},
 	}, {
-		structType: reflect.TypeOf(ScalarProto3{}),
-		messageDesc: ptype.StandaloneMessage{
-			Syntax:   pref.Proto3,
-			FullName: "ScalarProto3",
-			Fields: []ptype.Field{
-				{Name: "f1", Number: 1, Cardinality: pref.Optional, Kind: pref.BoolKind},
-				{Name: "f2", Number: 2, Cardinality: pref.Optional, Kind: pref.Int32Kind},
-				{Name: "f3", Number: 3, Cardinality: pref.Optional, Kind: pref.Int64Kind},
-				{Name: "f4", Number: 4, Cardinality: pref.Optional, Kind: pref.Uint32Kind},
-				{Name: "f5", Number: 5, Cardinality: pref.Optional, Kind: pref.Uint64Kind},
-				{Name: "f6", Number: 6, Cardinality: pref.Optional, Kind: pref.FloatKind},
-				{Name: "f7", Number: 7, Cardinality: pref.Optional, Kind: pref.DoubleKind},
-				{Name: "f8", Number: 8, Cardinality: pref.Optional, Kind: pref.StringKind},
-				{Name: "f9", Number: 9, Cardinality: pref.Optional, Kind: pref.StringKind},
-				{Name: "f10", Number: 10, Cardinality: pref.Optional, Kind: pref.BytesKind},
-				{Name: "f11", Number: 11, Cardinality: pref.Optional, Kind: pref.BytesKind},
-
-				{Name: "f12", Number: 12, Cardinality: pref.Optional, Kind: pref.BoolKind},
-				{Name: "f13", Number: 13, Cardinality: pref.Optional, Kind: pref.Int32Kind},
-				{Name: "f14", Number: 14, Cardinality: pref.Optional, Kind: pref.Int64Kind},
-				{Name: "f15", Number: 15, Cardinality: pref.Optional, Kind: pref.Uint32Kind},
-				{Name: "f16", Number: 16, Cardinality: pref.Optional, Kind: pref.Uint64Kind},
-				{Name: "f17", Number: 17, Cardinality: pref.Optional, Kind: pref.FloatKind},
-				{Name: "f18", Number: 18, Cardinality: pref.Optional, Kind: pref.DoubleKind},
-				{Name: "f19", Number: 19, Cardinality: pref.Optional, Kind: pref.StringKind},
-				{Name: "f20", Number: 20, Cardinality: pref.Optional, Kind: pref.StringKind},
-				{Name: "f21", Number: 21, Cardinality: pref.Optional, Kind: pref.BytesKind},
-				{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind},
-			},
-		},
+		structType:  reflect.TypeOf(ScalarProto3{}),
+		messageDesc: scalarProto3Desc,
 		testOps: []testOp{
 			hasOp([]bool{
 				false, false, false, false, false, false, false, false, false, false, false,
@@ -228,19 +240,12 @@
 
 	for _, tt := range tests {
 		t.Run(tt.structType.Name(), func(t *testing.T) {
-			// Construct the message descriptor.
-			md, err := ptype.NewMessage(&tt.messageDesc)
-			if err != nil {
-				t.Fatalf("NewMessage error: %v", err)
-			}
-
-			// Generate the field functions from the message descriptor.
-			var mi MessageInfo
-			mi.generateFieldFuncs(tt.structType, md) // must not panic
+			mi := MessageType{Desc: tt.messageDesc}
 
 			// Test the field functions.
-			m := reflect.New(tt.structType)
-			p := pointerOfValue(m)
+			p := reflect.New(tt.structType).Interface()
+			m := mi.MessageOf(p)
+			fs := m.KnownFields()
 			for i, op := range tt.testOps {
 				switch op := op.(type) {
 				case hasOp:
@@ -248,7 +253,7 @@
 					want := map[pref.FieldNumber]bool{}
 					for j, ok := range op {
 						n := pref.FieldNumber(j + 1)
-						got[n] = mi.fields[n].has(p)
+						got[n] = fs.Has(n)
 						want[n] = ok
 					}
 					if diff := cmp.Diff(want, got); diff != "" {
@@ -259,7 +264,7 @@
 					want := map[pref.FieldNumber]pref.Value{}
 					for j, v := range op {
 						n := pref.FieldNumber(j + 1)
-						got[n] = mi.fields[n].get(p)
+						got[n] = fs.Get(n)
 						want[n] = v
 					}
 					xformValue := cmp.Transformer("", func(v pref.Value) interface{} {
@@ -271,17 +276,17 @@
 				case setOp:
 					for j, v := range op {
 						n := pref.FieldNumber(j + 1)
-						mi.fields[n].set(p, v)
+						fs.Set(n, v)
 					}
 				case clearOp:
 					for j, ok := range op {
 						n := pref.FieldNumber(j + 1)
 						if ok {
-							mi.fields[n].clear(p)
+							fs.Clear(n)
 						}
 					}
 				case equalOp:
-					got := m.Interface()
+					got := m.(interface{ Unwrap() interface{} }).Unwrap()
 					if diff := cmp.Diff(op.want, got); diff != "" {
 						t.Errorf("operation %d, equal mismatch (-want, +got):\n%s", i, diff)
 					}