internal/filedesc: don't close over descopts values
It is possible for filedesc to construct a lazy options decoder before
the descriptor package has been imported. For example, top-level enum
values are eagerly decoded, so a generated proto package can construct a
lazy options decoder for an enum value at init time.
Don't close over the variables in descopts. Instead, close over a pointer
to the variable.
Panic with an informative message in the case where options are decoded
before the descriptor package has been initialized.
Change-Id: I277a57602b083cb7bbf92c8114c50b467e59521f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188820
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/filedesc/desc_lazy.go b/internal/filedesc/desc_lazy.go
index 9e2b21c..ae9358a 100644
--- a/internal/filedesc/desc_lazy.go
+++ b/internal/filedesc/desc_lazy.go
@@ -183,7 +183,7 @@
b = b[m:]
}
}
- fd.L2.Options = fd.builder.optionsUnmarshaler(descopts.File, rawOptions)
+ fd.L2.Options = fd.builder.optionsUnmarshaler(&descopts.File, rawOptions)
}
func (ed *Enum) unmarshalFull(b []byte, sb *strs.Builder) {
@@ -220,7 +220,7 @@
ed.L2.Values.List[i].unmarshalFull(b, sb, ed.L0.ParentFile, ed, i)
}
}
- ed.L2.Options = ed.L0.ParentFile.builder.optionsUnmarshaler(descopts.Enum, rawOptions)
+ ed.L2.Options = ed.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Enum, rawOptions)
}
func unmarshalEnumReservedRange(b []byte) (r [2]pref.EnumNumber) {
@@ -277,7 +277,7 @@
b = b[m:]
}
}
- vd.L1.Options = pf.builder.optionsUnmarshaler(descopts.EnumValue, rawOptions)
+ vd.L1.Options = pf.builder.optionsUnmarshaler(&descopts.EnumValue, rawOptions)
}
func (md *Message) unmarshalFull(b []byte, sb *strs.Builder) {
@@ -303,7 +303,7 @@
md.L2.ReservedRanges.List = append(md.L2.ReservedRanges.List, unmarshalMessageReservedRange(v))
case fieldnum.DescriptorProto_ExtensionRange:
r, rawOptions := unmarshalMessageExtensionRange(v)
- opts := md.L0.ParentFile.builder.optionsUnmarshaler(descopts.ExtensionRange, rawOptions)
+ opts := md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.ExtensionRange, rawOptions)
md.L2.ExtensionRanges.List = append(md.L2.ExtensionRanges.List, r)
md.L2.ExtensionRangeOptions = append(md.L2.ExtensionRangeOptions, opts)
case fieldnum.DescriptorProto_EnumType:
@@ -339,7 +339,7 @@
od.unmarshalFull(b, sb, md.L0.ParentFile, md, i)
}
}
- md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(descopts.Message, rawOptions)
+ md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Message, rawOptions)
}
func (md *Message) unmarshalOptions(b []byte) {
@@ -476,7 +476,7 @@
fd.L1.Message = PlaceholderMessage(name)
}
}
- fd.L1.Options = pf.builder.optionsUnmarshaler(descopts.Field, rawOptions)
+ fd.L1.Options = pf.builder.optionsUnmarshaler(&descopts.Field, rawOptions)
}
func (fd *Field) unmarshalOptions(b []byte) {
@@ -530,7 +530,7 @@
b = b[m:]
}
}
- od.L1.Options = pf.builder.optionsUnmarshaler(descopts.Oneof, rawOptions)
+ od.L1.Options = pf.builder.optionsUnmarshaler(&descopts.Oneof, rawOptions)
}
func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) {
@@ -576,7 +576,7 @@
xd.L2.Message = PlaceholderMessage(name)
}
}
- xd.L2.Options = xd.L0.ParentFile.builder.optionsUnmarshaler(descopts.Field, rawOptions)
+ xd.L2.Options = xd.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Field, rawOptions)
}
func (xd *Extension) unmarshalOptions(b []byte) {
@@ -626,7 +626,7 @@
sd.L2.Methods.List[i].unmarshalFull(b, sb, sd.L0.ParentFile, sd, i)
}
}
- sd.L2.Options = sd.L0.ParentFile.builder.optionsUnmarshaler(descopts.Service, rawOptions)
+ sd.L2.Options = sd.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Service, rawOptions)
}
func (md *Method) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd pref.Descriptor, i int) {
@@ -666,7 +666,7 @@
b = b[m:]
}
}
- md.L1.Options = pf.builder.optionsUnmarshaler(descopts.Method, rawOptions)
+ md.L1.Options = pf.builder.optionsUnmarshaler(&descopts.Method, rawOptions)
}
// appendOptions appends src to dst, where the returned slice is never nil.
@@ -678,7 +678,11 @@
return append(dst, src...)
}
-func (db *DescBuilder) optionsUnmarshaler(p pref.ProtoMessage, b []byte) func() pref.ProtoMessage {
+// optionsUnmarshaler constructs a lazy unmarshal function for an options message.
+//
+// The type of message to unmarshal to is passed as a pointer since the
+// vars in descopts may not yet be populated at the time this function is called.
+func (db *DescBuilder) optionsUnmarshaler(p *pref.ProtoMessage, b []byte) func() pref.ProtoMessage {
if b == nil {
return nil
}
@@ -686,7 +690,10 @@
var once sync.Once
return func() pref.ProtoMessage {
once.Do(func() {
- opts = reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.ProtoMessage)
+ if *p == nil {
+ panic("Descriptor.Options called without importing the descriptor package")
+ }
+ opts = reflect.New(reflect.TypeOf(*p).Elem()).Interface().(pref.ProtoMessage)
if err := (proto.UnmarshalOptions{
AllowPartial: true,
Resolver: db.TypeResolver,