all: implement proto1 weak fields
This implements generation of and reflection support for weak fields.
Weak fields are a proto1 feature where the "weak" option can be specified
on a singular message field. A weak reference results in generated code
that does not directly link in the dependency containing the weak message.
Weak field support is not added to any of the serialization logic.
Change-Id: I08ccfa72bc80b2ffb6af527a1677a0a81dcf33fb
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185399
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/legacy_export.go b/internal/impl/legacy_export.go
index f08f242..07c16b5 100644
--- a/internal/impl/legacy_export.go
+++ b/internal/impl/legacy_export.go
@@ -7,11 +7,15 @@
import (
"encoding/binary"
"encoding/json"
+ "fmt"
"hash/crc32"
"math"
+ "reflect"
"google.golang.org/protobuf/internal/errors"
pref "google.golang.org/protobuf/reflect/protoreflect"
+ "google.golang.org/protobuf/reflect/protoregistry"
+ piface "google.golang.org/protobuf/runtime/protoiface"
)
// These functions exist to support exported APIs in generated protobufs.
@@ -74,3 +78,13 @@
out = append(out, gzipFooter[:]...)
return out
}
+
+// WeakNil returns a typed nil pointer to a concrete message.
+// It panics if the message is not linked into the binary.
+func (Export) WeakNil(s pref.FullName) piface.MessageV1 {
+ mt, err := protoregistry.GlobalTypes.FindMessageByName(s)
+ if err == nil {
+ panic(fmt.Sprintf("weak message %v is not linked in", s))
+ }
+ return reflect.Zero(mt.GoType()).Interface().(piface.MessageV1)
+}
diff --git a/internal/impl/message.go b/internal/impl/message.go
index 2e8d185..5b4beb0 100644
--- a/internal/impl/message.go
+++ b/internal/impl/message.go
@@ -120,18 +120,21 @@
type (
SizeCache = int32
+ WeakFields = map[int32]piface.MessageV1
UnknownFields = []byte
ExtensionFields = map[int32]ExtensionField
)
var (
sizecacheType = reflect.TypeOf(SizeCache(0))
+ weakFieldsType = reflect.TypeOf(WeakFields(nil))
unknownFieldsType = reflect.TypeOf(UnknownFields(nil))
extensionFieldsType = reflect.TypeOf(ExtensionFields(nil))
)
type structInfo struct {
sizecacheOffset offset
+ weakOffset offset
unknownOffset offset
extensionOffset offset
@@ -144,6 +147,7 @@
func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
si := structInfo{
sizecacheOffset: invalidOffset,
+ weakOffset: invalidOffset,
unknownOffset: invalidOffset,
extensionOffset: invalidOffset,
@@ -159,6 +163,9 @@
if f, _ := t.FieldByName("XXX_sizecache"); f.Type == sizecacheType {
si.sizecacheOffset = offsetOf(f, mi.Exporter)
}
+ if f, _ := t.FieldByName("XXX_weak"); f.Type == weakFieldsType {
+ si.weakOffset = offsetOf(f, mi.Exporter)
+ }
if f, _ := t.FieldByName("unknownFields"); f.Type == unknownFieldsType {
si.unknownOffset = offsetOf(f, mi.Exporter)
}
@@ -235,6 +242,8 @@
fi = fieldInfoForMap(fd, fs, mi.Exporter)
case fd.IsList():
fi = fieldInfoForList(fd, fs, mi.Exporter)
+ case fd.IsWeak():
+ fi = fieldInfoForWeakMessage(fd, si.weakOffset)
case fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind:
fi = fieldInfoForMessage(fd, fs, mi.Exporter)
default:
diff --git a/internal/impl/message_field.go b/internal/impl/message_field.go
index 7cba71d..1787f32 100644
--- a/internal/impl/message_field.go
+++ b/internal/impl/message_field.go
@@ -9,8 +9,10 @@
"math"
"reflect"
+ "google.golang.org/protobuf/internal/flags"
pvalue "google.golang.org/protobuf/internal/value"
pref "google.golang.org/protobuf/reflect/protoreflect"
+ preg "google.golang.org/protobuf/reflect/protoregistry"
piface "google.golang.org/protobuf/runtime/protoiface"
)
@@ -289,6 +291,89 @@
}
}
+func fieldInfoForWeakMessage(fd pref.FieldDescriptor, weakOffset offset) fieldInfo {
+ if !flags.Proto1Legacy {
+ panic("no support for proto1 weak fields")
+ }
+
+ messageName := fd.Message().FullName()
+ messageType, _ := preg.GlobalTypes.FindMessageByName(messageName)
+ if messageType == nil {
+ return fieldInfo{
+ fieldDesc: fd,
+ has: func(p pointer) bool { return false },
+ clear: func(p pointer) {},
+ get: func(p pointer) pref.Value {
+ panic(fmt.Sprintf("weak message %v is not linked in", messageName))
+ },
+ set: func(p pointer, v pref.Value) {
+ panic(fmt.Sprintf("weak message %v is not linked in", messageName))
+ },
+ mutable: func(p pointer) pref.Value {
+ panic(fmt.Sprintf("weak message %v is not linked in", messageName))
+ },
+ newMessage: func() pref.Message {
+ panic(fmt.Sprintf("weak message %v is not linked in", messageName))
+ },
+ }
+ }
+
+ num := int32(fd.Number())
+ frozenEmpty := pref.ValueOf(frozenMessage{messageType.New()})
+ return fieldInfo{
+ fieldDesc: fd,
+ has: func(p pointer) bool {
+ if p.IsNil() {
+ return false
+ }
+ fs := p.Apply(weakOffset).WeakFields()
+ _, ok := (*fs)[num]
+ return ok
+ },
+ clear: func(p pointer) {
+ fs := p.Apply(weakOffset).WeakFields()
+ delete(*fs, num)
+ },
+ get: func(p pointer) pref.Value {
+ if p.IsNil() {
+ return frozenEmpty
+ }
+ fs := p.Apply(weakOffset).WeakFields()
+ m, ok := (*fs)[num]
+ if !ok {
+ return frozenEmpty
+ }
+ return pref.ValueOf(m.(pref.ProtoMessage).ProtoReflect())
+ },
+ set: func(p pointer, v pref.Value) {
+ m := v.Message()
+ if m.Descriptor() != messageType.Descriptor() {
+ panic("mismatching message descriptor")
+ }
+ fs := p.Apply(weakOffset).WeakFields()
+ if *fs == nil {
+ *fs = make(WeakFields)
+ }
+ (*fs)[num] = m.Interface().(piface.MessageV1)
+ },
+ mutable: func(p pointer) pref.Value {
+ fs := p.Apply(weakOffset).WeakFields()
+ if *fs == nil {
+ *fs = make(WeakFields)
+ }
+ m, ok := (*fs)[num]
+ if !ok {
+ m = messageType.New().Interface().(piface.MessageV1)
+ (*fs)[num] = m
+ }
+ return pref.ValueOf(m.(pref.ProtoMessage).ProtoReflect())
+ },
+ newMessage: func() pref.Message {
+ return messageType.New()
+ },
+ }
+}
+
func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
ft := fs.Type
conv, _ := newConverter(ft, fd.Kind())
diff --git a/internal/impl/pointer_reflect.go b/internal/impl/pointer_reflect.go
index 020af8e..d076b9d 100644
--- a/internal/impl/pointer_reflect.go
+++ b/internal/impl/pointer_reflect.go
@@ -122,6 +122,7 @@
func (p pointer) StringSlice() *[]string { return p.v.Interface().(*[]string) }
func (p pointer) Bytes() *[]byte { return p.v.Interface().(*[]byte) }
func (p pointer) BytesSlice() *[][]byte { return p.v.Interface().(*[][]byte) }
+func (p pointer) WeakFields() *WeakFields { return p.v.Interface().(*WeakFields) }
func (p pointer) Extensions() *map[int32]ExtensionField {
return p.v.Interface().(*map[int32]ExtensionField)
}
diff --git a/internal/impl/pointer_unsafe.go b/internal/impl/pointer_unsafe.go
index ab0d6ee..5cebd5c 100644
--- a/internal/impl/pointer_unsafe.go
+++ b/internal/impl/pointer_unsafe.go
@@ -110,6 +110,7 @@
func (p pointer) StringSlice() *[]string { return (*[]string)(p.p) }
func (p pointer) Bytes() *[]byte { return (*[]byte)(p.p) }
func (p pointer) BytesSlice() *[][]byte { return (*[][]byte)(p.p) }
+func (p pointer) WeakFields() *WeakFields { return (*WeakFields)(p.p) }
func (p pointer) Extensions() *map[int32]ExtensionField { return (*map[int32]ExtensionField)(p.p) }
func (p pointer) Elem() pointer {