internal/filedesc: avoid deep-copying the options
The protoreflect.Descriptor.Options method is currently documented as
returning a reference to the options, where the user must not mutate
the returned message. This changes internal/filedesc to avoid returning
a copy of the options by caching the first unmarshal.
See golang/protobuf#877
Change-Id: I15701d33fbda7535b21b2add72628b02992c373f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185197
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
diff --git a/internal/filedesc/desc_lazy.go b/internal/filedesc/desc_lazy.go
index 9fc5b1b..4d31109 100644
--- a/internal/filedesc/desc_lazy.go
+++ b/internal/filedesc/desc_lazy.go
@@ -6,6 +6,7 @@
import (
"reflect"
+ "sync"
"google.golang.org/protobuf/internal/descopts"
"google.golang.org/protobuf/internal/encoding/wire"
@@ -675,14 +676,18 @@
if b == nil {
return nil
}
+ var opts pref.ProtoMessage
+ var once sync.Once
return func() pref.ProtoMessage {
- p := reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.ProtoMessage)
- if err := (proto.UnmarshalOptions{
- AllowPartial: true,
- Resolver: db.TypeResolver,
- }).Unmarshal(b, p); err != nil {
- panic(err)
- }
- return p
+ once.Do(func() {
+ opts = reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.ProtoMessage)
+ if err := (proto.UnmarshalOptions{
+ AllowPartial: true,
+ Resolver: db.TypeResolver,
+ }).Unmarshal(b, opts); err != nil {
+ panic(err)
+ }
+ })
+ return opts
}
}
diff --git a/reflect/protoreflect/type.go b/reflect/protoreflect/type.go
index a733be1..d252caf 100644
--- a/reflect/protoreflect/type.go
+++ b/reflect/protoreflect/type.go
@@ -85,8 +85,6 @@
// For FileDescriptor, the Path and Package are also valid.
IsPlaceholder() bool
- // TODO: Decide memory ownership of Options.
-
// Options returns the descriptor options. The caller must not modify
// the returned value.
//