internal/filedesc, internal/filetype: initial commit
The internal/fileinit package is split apart into two packages:
* internal/filedesc constructs descriptors from the raw proto.
It is very similar to the previous internal/fileinit package.
* internal/filetype wraps descriptors with Go type information
Overview:
* The internal/fileinit package will be deleted in a future CL.
It is kept around since the v1 repo currently depends on it.
* The internal/prototype package is deleted. All former usages of it
are now using internal/filedesc instead. Most significantly,
the reflect/protodesc package was almost entirely re-written.
* The internal/impl package drops support for messages that do not
have a Descriptor method (pre-2016). This removes a significant amount
of technical debt.
filedesc.Builder to parse raw descriptors.
* The internal/encoding/defval package now handles enum values by name.
Change-Id: I3957bcc8588a70470fd6c7de1122216b80615ab7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/182360
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/fileinit/desc_lazy.go b/internal/fileinit/desc_lazy.go
index 0e26e17..673b100 100644
--- a/internal/fileinit/desc_lazy.go
+++ b/internal/fileinit/desc_lazy.go
@@ -12,8 +12,8 @@
defval "google.golang.org/protobuf/internal/encoding/defval"
wire "google.golang.org/protobuf/internal/encoding/wire"
fieldnum "google.golang.org/protobuf/internal/fieldnum"
+ fdesc "google.golang.org/protobuf/internal/filedesc"
pimpl "google.golang.org/protobuf/internal/impl"
- ptype "google.golang.org/protobuf/internal/prototype"
pvalue "google.golang.org/protobuf/internal/value"
pref "google.golang.org/protobuf/reflect/protoreflect"
preg "google.golang.org/protobuf/reflect/protoregistry"
@@ -95,7 +95,11 @@
fd.isPacked = true
}
}
- fd.defVal.lazyInit(fd.kind, file.enumValuesOf(fd.enumType))
+
+ // Default is resolved here since it depends on Enum being resolved.
+ if v := fd.defVal.val; v.IsValid() {
+ fd.defVal = unmarshalDefault(v.Bytes(), fd.kind, file, fd.enumType)
+ }
}
}
}
@@ -121,7 +125,7 @@
et := pimpl.Export{}.EnumTypeOf(reflect.Zero(typ).Interface())
xd.lazy.typ = typ
xd.lazy.new = func() pref.Value {
- return xd.lazy.defVal.get()
+ return xd.lazy.defVal.get(xd)
}
xd.lazy.valueOf = func(v interface{}) pref.Value {
ev := v.(pref.Enum)
@@ -146,7 +150,7 @@
default:
xd.lazy.typ = goTypeForPBKind[xd.lazy.kind]
xd.lazy.new = func() pref.Value {
- return xd.lazy.defVal.get()
+ return xd.lazy.defVal.get(xd)
}
xd.lazy.valueOf = func(v interface{}) pref.Value {
return pref.ValueOf(v)
@@ -179,7 +183,11 @@
case pref.MessageKind, pref.GroupKind:
xd.lazy.messageType = file.popMessageDependency()
}
- xd.lazy.defVal.lazyInit(xd.lazy.kind, file.enumValuesOf(xd.lazy.enumType))
+
+ // Default is resolved here since it depends on Enum being resolved.
+ if v := xd.lazy.defVal.val; v.IsValid() {
+ xd.lazy.defVal = unmarshalDefault(v.Bytes(), xd.lazy.kind, file, xd.lazy.enumType)
+ }
}
}
@@ -271,67 +279,84 @@
*fi = fileInit{} // clear fileInit for GC to reclaim resources
}
+func DefaultValue(v pref.Value, ev pref.EnumValueDescriptor) defaultValue {
+ dv := defaultValue{has: v.IsValid(), val: v, enum: ev}
+ if b, ok := v.Interface().([]byte); ok {
+ // Store a copy of the default bytes, so that we can detect
+ // accidental mutations of the original value.
+ dv.bytes = append([]byte(nil), b...)
+ }
+ return dv
+}
+
+func unmarshalDefault(b []byte, k pref.Kind, pf *fileDesc, ed pref.EnumDescriptor) defaultValue {
+ var evs pref.EnumValueDescriptors
+ if k == pref.EnumKind {
+ // If the enum is declared within the same file, be careful not to
+ // blindly call the Values method, lest we bind ourselves in a deadlock.
+ if ed, ok := ed.(*enumDesc); ok && ed.parentFile == pf {
+ evs = &ed.lazy.values
+ } else {
+ evs = ed.Values()
+ }
+ }
+
+ v, ev, err := defval.Unmarshal(string(b), k, evs, defval.Descriptor)
+ if err != nil {
+ panic(err)
+ }
+ dv := defaultValue{has: v.IsValid(), val: v, enum: ev}
+ if b, ok := v.Interface().([]byte); ok {
+ // Store a copy of the default bytes, so that we can detect
+ // accidental mutations of the original value.
+ dv.bytes = append([]byte(nil), b...)
+ }
+ return dv
+}
+
type defaultValue struct {
has bool
val pref.Value
enum pref.EnumValueDescriptor
- check func() // only set for non-empty bytes
+ bytes []byte
}
-func (dv *defaultValue) get() pref.Value {
- if dv.check != nil {
- dv.check()
+func (dv *defaultValue) get(fd pref.FieldDescriptor) pref.Value {
+ // Return the zero value as the default if unpopulated.
+ if !dv.has {
+ switch fd.Kind() {
+ case pref.BoolKind:
+ return pref.ValueOf(false)
+ case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
+ return pref.ValueOf(int32(0))
+ case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
+ return pref.ValueOf(int64(0))
+ case pref.Uint32Kind, pref.Fixed32Kind:
+ return pref.ValueOf(uint32(0))
+ case pref.Uint64Kind, pref.Fixed64Kind:
+ return pref.ValueOf(uint64(0))
+ case pref.FloatKind:
+ return pref.ValueOf(float32(0))
+ case pref.DoubleKind:
+ return pref.ValueOf(float64(0))
+ case pref.StringKind:
+ return pref.ValueOf(string(""))
+ case pref.BytesKind:
+ return pref.ValueOf([]byte(nil))
+ case pref.EnumKind:
+ return pref.ValueOf(fd.Enum().Values().Get(0).Number())
+ }
+ }
+
+ if len(dv.bytes) > 0 && !bytes.Equal(dv.bytes, dv.val.Bytes()) {
+ // TODO: Avoid panic if we're running with the race detector
+ // and instead spawn a goroutine that periodically resets
+ // this value back to the original to induce a race.
+ panic("detected mutation on the default bytes")
}
return dv.val
}
-func (dv *defaultValue) lazyInit(k pref.Kind, eds pref.EnumValueDescriptors) {
- if dv.has {
- switch k {
- case pref.EnumKind:
- // File descriptors always store default enums by name.
- dv.enum = eds.ByName(pref.Name(dv.val.String()))
- dv.val = pref.ValueOf(dv.enum.Number())
- case pref.BytesKind:
- // Store a copy of the default bytes, so that we can detect
- // accidental mutations of the original value.
- b := append([]byte(nil), dv.val.Bytes()...)
- dv.check = func() {
- if !bytes.Equal(b, dv.val.Bytes()) {
- // TODO: Avoid panic if we're running with the race detector
- // and instead spawn a goroutine that periodically resets
- // this value back to the original to induce a race.
- panic("detected mutation on the default bytes")
- }
- }
- }
- } else {
- switch k {
- case pref.BoolKind:
- dv.val = pref.ValueOf(false)
- case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
- dv.val = pref.ValueOf(int32(0))
- case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
- dv.val = pref.ValueOf(int64(0))
- case pref.Uint32Kind, pref.Fixed32Kind:
- dv.val = pref.ValueOf(uint32(0))
- case pref.Uint64Kind, pref.Fixed64Kind:
- dv.val = pref.ValueOf(uint64(0))
- case pref.FloatKind:
- dv.val = pref.ValueOf(float32(0))
- case pref.DoubleKind:
- dv.val = pref.ValueOf(float64(0))
- case pref.StringKind:
- dv.val = pref.ValueOf(string(""))
- case pref.BytesKind:
- dv.val = pref.ValueOf([]byte(nil))
- case pref.EnumKind:
- dv.enum = eds.Get(0)
- dv.val = pref.ValueOf(dv.enum.Number())
- }
- }
-}
-
func (fd *fileDesc) unmarshalFull(b []byte) {
nb := getNameBuilder()
defer putNameBuilder(nb)
@@ -368,7 +393,7 @@
}
case fieldnum.FileDescriptorProto_Dependency:
fd.lazy.imports = append(fd.lazy.imports, pref.FileImport{
- FileDescriptor: ptype.PlaceholderFile(nb.MakeString(v), ""),
+ FileDescriptor: fdesc.PlaceholderFile(nb.MakeString(v)),
})
case fieldnum.FileDescriptorProto_EnumType:
fd.enums.list[enumIdx].unmarshalFull(v, nb)
@@ -627,7 +652,6 @@
fd.parent = pd
fd.index = i
- var rawDefVal []byte
var rawTypeName []byte
for len(b) > 0 {
num, typ, n := wire.ConsumeTag(b)
@@ -664,8 +688,7 @@
fd.hasJSONName = true
fd.jsonName = nb.MakeString(v)
case fieldnum.FieldDescriptorProto_DefaultValue:
- fd.defVal.has = true
- rawDefVal = v
+ fd.defVal.val = pref.ValueOf(v) // temporarily store as bytes; later resolved in resolveMessages
case fieldnum.FieldDescriptorProto_TypeName:
rawTypeName = v
case fieldnum.FieldDescriptorProto_Options:
@@ -680,13 +703,6 @@
if !fd.hasJSONName {
fd.jsonName = nb.MakeJSONName(fd.Name())
}
- if rawDefVal != nil {
- var err error
- fd.defVal.val, err = defval.Unmarshal(string(rawDefVal), fd.kind, defval.Descriptor)
- if err != nil {
- panic(err)
- }
- }
if fd.isWeak {
if len(rawTypeName) == 0 || rawTypeName[0] != '.' {
panic("weak target name must be fully qualified")
@@ -695,7 +711,7 @@
name := pref.FullName(rawTypeName[1:])
fd.messageType, _ = preg.GlobalFiles.FindMessageByName(name)
if fd.messageType == nil {
- fd.messageType = ptype.PlaceholderMessage(pref.FullName(rawTypeName[1:]))
+ fd.messageType = fdesc.PlaceholderMessage(pref.FullName(rawTypeName[1:]))
}
}
}
@@ -749,7 +765,6 @@
}
func (xd *extensionDesc) unmarshalFull(b []byte, nb *nameBuilder) {
- var rawDefVal []byte
xd.lazy = new(extensionLazy)
for len(b) > 0 {
num, typ, n := wire.ConsumeTag(b)
@@ -772,8 +787,7 @@
xd.lazy.hasJSONName = true
xd.lazy.jsonName = nb.MakeString(v)
case fieldnum.FieldDescriptorProto_DefaultValue:
- xd.lazy.defVal.has = true
- rawDefVal = v
+ xd.lazy.defVal.val = pref.ValueOf(v) // temporarily store as bytes; later resolved in resolveExtensions
case fieldnum.FieldDescriptorProto_Options:
xd.unmarshalOptions(v)
}
@@ -782,14 +796,6 @@
b = b[m:]
}
}
-
- if rawDefVal != nil {
- var err error
- xd.lazy.defVal.val, err = defval.Unmarshal(string(rawDefVal), xd.lazy.kind, defval.Descriptor)
- if err != nil {
- panic(err)
- }
- }
}
func (xd *extensionDesc) unmarshalOptions(b []byte) {