internal/impl: support legacy unknown extension fields

The unknown fields in legacy messages is split across the XXX_unrecognized
field and also the XXX_InternalExtensions field. Implement support for
wrapping both fields and presenting it as if it were a unified set of
unknown fields.

Change-Id: If274fae2b48962520edd8a640080b6eced747684
Reviewed-on: https://go-review.googlesource.com/c/146517
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/legacy_extension.go b/internal/impl/legacy_extension.go
new file mode 100644
index 0000000..b229581
--- /dev/null
+++ b/internal/impl/legacy_extension.go
@@ -0,0 +1,126 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package impl
+
+import (
+	"reflect"
+	"sync"
+	"unsafe"
+
+	protoV1 "github.com/golang/protobuf/proto"
+	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+)
+
+// TODO: The logic below this is a hack since v1 currently exposes no
+// exported functionality for interacting with these data structures.
+// Eventually make changes to v1 such that v2 can access the necessary
+// fields without relying on unsafe.
+
+var (
+	extTypeA = reflect.TypeOf(map[int32]protoV1.Extension(nil))
+	extTypeB = reflect.TypeOf(protoV1.XXX_InternalExtensions{})
+)
+
+type legacyExtensionIface interface {
+	Len() int
+	Get(pref.FieldNumber) legacyExtensionEntry
+	Set(pref.FieldNumber, legacyExtensionEntry)
+	Range(f func(pref.FieldNumber, legacyExtensionEntry) bool)
+}
+
+func makeLegacyExtensionMapFunc(t reflect.Type) func(*messageDataType) legacyExtensionIface {
+	fx1, _ := t.FieldByName("XXX_extensions")
+	fx2, _ := t.FieldByName("XXX_InternalExtensions")
+	switch {
+	case fx1.Type == extTypeA:
+		return func(p *messageDataType) legacyExtensionIface {
+			rv := p.p.asType(t).Elem()
+			return (*legacyExtensionMap)(unsafe.Pointer(rv.UnsafeAddr() + fx1.Offset))
+		}
+	case fx2.Type == extTypeB:
+		return func(p *messageDataType) legacyExtensionIface {
+			rv := p.p.asType(t).Elem()
+			return (*legacyExtensionSyncMap)(unsafe.Pointer(rv.UnsafeAddr() + fx2.Offset))
+		}
+	default:
+		return nil
+	}
+}
+
+// legacyExtensionSyncMap is identical to protoV1.XXX_InternalExtensions.
+// It implements legacyExtensionIface.
+type legacyExtensionSyncMap struct {
+	p *struct {
+		mu sync.Mutex
+		m  legacyExtensionMap
+	}
+}
+
+func (m legacyExtensionSyncMap) Len() int {
+	if m.p == nil {
+		return 0
+	}
+	m.p.mu.Lock()
+	defer m.p.mu.Unlock()
+	return m.p.m.Len()
+}
+func (m legacyExtensionSyncMap) Get(n pref.FieldNumber) legacyExtensionEntry {
+	if m.p == nil {
+		return legacyExtensionEntry{}
+	}
+	m.p.mu.Lock()
+	defer m.p.mu.Unlock()
+	return m.p.m.Get(n)
+}
+func (m *legacyExtensionSyncMap) Set(n pref.FieldNumber, x legacyExtensionEntry) {
+	if m.p == nil {
+		m.p = new(struct {
+			mu sync.Mutex
+			m  legacyExtensionMap
+		})
+	}
+	m.p.mu.Lock()
+	defer m.p.mu.Unlock()
+	m.p.m.Set(n, x)
+}
+func (m legacyExtensionSyncMap) Range(f func(pref.FieldNumber, legacyExtensionEntry) bool) {
+	if m.p == nil {
+		return
+	}
+	m.p.mu.Lock()
+	defer m.p.mu.Unlock()
+	m.p.m.Range(f)
+}
+
+// legacyExtensionMap is identical to map[int32]protoV1.Extension.
+// It implements legacyExtensionIface.
+type legacyExtensionMap map[pref.FieldNumber]legacyExtensionEntry
+
+func (m legacyExtensionMap) Len() int {
+	return len(m)
+}
+func (m legacyExtensionMap) Get(n pref.FieldNumber) legacyExtensionEntry {
+	return m[n]
+}
+func (m *legacyExtensionMap) Set(n pref.FieldNumber, x legacyExtensionEntry) {
+	if *m == nil {
+		*m = make(map[pref.FieldNumber]legacyExtensionEntry)
+	}
+	(*m)[n] = x
+}
+func (m legacyExtensionMap) Range(f func(pref.FieldNumber, legacyExtensionEntry) bool) {
+	for n, x := range m {
+		if !f(n, x) {
+			return
+		}
+	}
+}
+
+// legacyExtensionEntry is identical to protoV1.Extension.
+type legacyExtensionEntry struct {
+	desc *protoV1.ExtensionDesc
+	val  interface{}
+	raw  []byte
+}