internal/impl: add MessageState to every generated message

We define MessageState, which is essentially an atomically set *MessageInfo.
By nesting this as the first field in every generated message, we can
implement the reflective methods on a *MessageState when obtained by
unsafe casting a concrete message pointer as a *MessageState.
The MessageInfo held by MessageState provides additional Go type information
to interpret the memory that comes after the contents of the MessageState.

Since we are nesting a MessageState in every message,
the memory use of every message instance grows by 8B.

On average, the body of ProtoReflect grows from 133B to 202B (+50%).
However, this is offset by XXX_Methods, which is 108B and
will be removed in a future CL. Taking into account the eventual removal
of XXX_Methods, this is a net reduction of 25%.

name          old time/op    new time/op    delta
Name/Value-4    70.3ns ± 2%    17.5ns ± 6%   -75.08%  (p=0.000 n=10+10)
Name/Nil-4      70.6ns ± 3%    33.4ns ± 2%   -52.66%  (p=0.000 n=10+10)

name          old alloc/op   new alloc/op   delta
Name/Value-4     16.0B ± 0%      0.0B       -100.00%  (p=0.000 n=10+10)
Name/Nil-4       16.0B ± 0%      0.0B       -100.00%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
Name/Value-4      1.00 ± 0%      0.00       -100.00%  (p=0.000 n=10+10)
Name/Nil-4        1.00 ± 0%      0.00       -100.00%  (p=0.000 n=10+10)

Change-Id: I92bd58dc681c57c92612fd5ba7fc066aea34e95a
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185460
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/message_test.go b/internal/impl/message_test.go
index af7196d..f2fd290 100644
--- a/internal/impl/message_test.go
+++ b/internal/impl/message_test.go
@@ -8,7 +8,9 @@
 	"fmt"
 	"math"
 	"reflect"
+	"runtime"
 	"strings"
+	"sync"
 	"testing"
 
 	cmp "github.com/google/go-cmp/cmp"
@@ -23,6 +25,7 @@
 	"google.golang.org/protobuf/reflect/prototype"
 
 	proto2_20180125 "google.golang.org/protobuf/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
+	testpb "google.golang.org/protobuf/internal/testprotos/test"
 	"google.golang.org/protobuf/types/descriptorpb"
 )
 
@@ -1435,3 +1438,71 @@
 	}
 	return strings.Join(ss, ".")
 }
+
+// The MessageState implementation makes the assumption that when a
+// concrete message is unsafe casted as a *MessageState, the Go GC does
+// not reclaim the memory for the remainder of the concrete message.
+func TestUnsafeAssumptions(t *testing.T) {
+	if !pimpl.UnsafeEnabled {
+		t.Skip()
+	}
+
+	var wg sync.WaitGroup
+	for i := 0; i < 10; i++ {
+		wg.Add(1)
+		go func() {
+			var ms [10]pref.Message
+
+			// Store the message only in its reflective form.
+			// Trigger the GC after each iteration.
+			for j := 0; j < 10; j++ {
+				ms[j] = (&testpb.TestAllTypes{
+					OptionalInt32: scalar.Int32(int32(j)),
+					OptionalFloat: scalar.Float32(float32(j)),
+					RepeatedInt32: []int32{int32(j)},
+					RepeatedFloat: []float32{float32(j)},
+					DefaultInt32:  scalar.Int32(int32(j)),
+					DefaultFloat:  scalar.Float32(float32(j)),
+				}).ProtoReflect()
+				runtime.GC()
+			}
+
+			// Convert the reflective form back into a concrete form.
+			// Verify that the values written previously are still the same.
+			for j := 0; j < 10; j++ {
+				switch m := ms[j].Interface().(*testpb.TestAllTypes); {
+				case m.GetOptionalInt32() != int32(j):
+				case m.GetOptionalFloat() != float32(j):
+				case m.GetRepeatedInt32()[0] != int32(j):
+				case m.GetRepeatedFloat()[0] != float32(j):
+				case m.GetDefaultInt32() != int32(j):
+				case m.GetDefaultFloat() != float32(j):
+				default:
+					continue
+				}
+				t.Error("memory corrupted detected")
+			}
+			defer wg.Done()
+		}()
+	}
+	wg.Wait()
+}
+
+func BenchmarkName(b *testing.B) {
+	var sink pref.FullName
+	b.Run("Value", func(b *testing.B) {
+		b.ReportAllocs()
+		m := new(descriptorpb.FileDescriptorProto)
+		for i := 0; i < b.N; i++ {
+			sink = m.ProtoReflect().Descriptor().FullName()
+		}
+	})
+	b.Run("Nil", func(b *testing.B) {
+		b.ReportAllocs()
+		m := (*descriptorpb.FileDescriptorProto)(nil)
+		for i := 0; i < b.N; i++ {
+			sink = m.ProtoReflect().Descriptor().FullName()
+		}
+	})
+	runtime.KeepAlive(sink)
+}