From a4cbffe4bcbad833cba75e6e16223b2c7c88b48a Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Thu, 6 Dec 2018 13:01:52 -0800 Subject: [PATCH] reflect/prototype: add registration hooks for options Add pseudo-hidden functions to register the concrete Go type used for the optional types. Also, augment protoc-gen-go to specially generate the descriptor proto to register these types. This change does not add validation logic yet to ensure that the correct option types are passed to the API. Change-Id: I5decc897e14b4bf570a61cf17b57a066a2a0f9d7 Reviewed-on: https://go-review.googlesource.com/c/153017 Reviewed-by: Damien Neil --- cmd/protoc-gen-go/internal_gengo/reflect.go | 11 ++++++ reflect/prototype/options.go | 40 +++++++++++++++++++ reflect/prototype/placeholder_type.go | 6 +-- reflect/prototype/protofile_type.go | 44 +++++++++++++-------- reflect/prototype/standalone_type.go | 26 ++++++------ types/descriptor/descriptor.pb.go | 9 +++++ 6 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 reflect/prototype/options.go diff --git a/cmd/protoc-gen-go/internal_gengo/reflect.go b/cmd/protoc-gen-go/internal_gengo/reflect.go index 6b6ea685..0b0e03ca 100644 --- a/cmd/protoc-gen-go/internal_gengo/reflect.go +++ b/cmd/protoc-gen-go/internal_gengo/reflect.go @@ -131,6 +131,17 @@ func genReflectInitFunction(gen *protogen.Plugin, g *protogen.GeneratedFile, f * // TODO: Add v2 registration and stop v1 registration in genInitFunction. + // The descriptor proto needs to register the option types with the + // prototype so that the package can properly handle those option types. + if isDescriptor(f.File) { + for _, m := range f.allMessages { + name := m.GoIdent.GoName + if strings.HasSuffix(name, "Options") { + g.P(prototypePackage.Ident("X"), ".Register", name, "((*", name, ")(nil))") + } + } + } + g.P("}") } diff --git a/reflect/prototype/options.go b/reflect/prototype/options.go new file mode 100644 index 00000000..7ad8c92a --- /dev/null +++ b/reflect/prototype/options.go @@ -0,0 +1,40 @@ +// 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 prototype + +import pref "github.com/golang/protobuf/v2/reflect/protoreflect" + +// X provides functionality internal to the protobuf module. +// +// WARNING: The compatibility agreement covers nothing except for functionality +// needed to keep existing generated messages operational. The Go authors +// are not responsible for breakages that occur due to unauthorized usages. +var X internal + +type internal struct{} + +// optionTypes contains typed nil-pointers to each of the options types. +// These are populated at init time by the descriptor package. +var optionTypes struct { + File pref.ProtoMessage + Enum pref.ProtoMessage + EnumValue pref.ProtoMessage + Message pref.ProtoMessage + Field pref.ProtoMessage + Oneof pref.ProtoMessage + ExtensionRange pref.ProtoMessage + Service pref.ProtoMessage + Method pref.ProtoMessage +} + +func (internal) RegisterFileOptions(m pref.ProtoMessage) { optionTypes.File = m } +func (internal) RegisterEnumOptions(m pref.ProtoMessage) { optionTypes.Enum = m } +func (internal) RegisterEnumValueOptions(m pref.ProtoMessage) { optionTypes.EnumValue = m } +func (internal) RegisterMessageOptions(m pref.ProtoMessage) { optionTypes.Message = m } +func (internal) RegisterFieldOptions(m pref.ProtoMessage) { optionTypes.Field = m } +func (internal) RegisterOneofOptions(m pref.ProtoMessage) { optionTypes.Oneof = m } +func (internal) RegisterExtensionRangeOptions(m pref.ProtoMessage) { optionTypes.ExtensionRange = m } +func (internal) RegisterServiceOptions(m pref.ProtoMessage) { optionTypes.Service = m } +func (internal) RegisterMethodOptions(m pref.ProtoMessage) { optionTypes.Method = m } diff --git a/reflect/prototype/placeholder_type.go b/reflect/prototype/placeholder_type.go index 0838fc2e..85fe30e6 100644 --- a/reflect/prototype/placeholder_type.go +++ b/reflect/prototype/placeholder_type.go @@ -41,7 +41,7 @@ type placeholderFile struct { placeholderName } -func (t placeholderFile) Options() pref.ProtoMessage { return nil } +func (t placeholderFile) Options() pref.ProtoMessage { return optionTypes.File } func (t placeholderFile) Path() string { return t.path } func (t placeholderFile) Package() pref.FullName { return t.FullName() } func (t placeholderFile) Imports() pref.FileImports { return &emptyFiles } @@ -57,7 +57,7 @@ type placeholderMessage struct { placeholderName } -func (t placeholderMessage) Options() pref.ProtoMessage { return nil } +func (t placeholderMessage) Options() pref.ProtoMessage { return optionTypes.Message } func (t placeholderMessage) IsMapEntry() bool { return false } func (t placeholderMessage) Fields() pref.FieldDescriptors { return &emptyFields } func (t placeholderMessage) Oneofs() pref.OneofDescriptors { return &emptyOneofs } @@ -73,7 +73,7 @@ type placeholderEnum struct { placeholderName } -func (t placeholderEnum) Options() pref.ProtoMessage { return nil } +func (t placeholderEnum) Options() pref.ProtoMessage { return optionTypes.Enum } func (t placeholderEnum) Values() pref.EnumValueDescriptors { return &emptyEnumValues } func (t placeholderEnum) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, t) } func (t placeholderEnum) ProtoType(pref.EnumDescriptor) {} diff --git a/reflect/prototype/protofile_type.go b/reflect/prototype/protofile_type.go index 053d8b8b..55631b1b 100644 --- a/reflect/prototype/protofile_type.go +++ b/reflect/prototype/protofile_type.go @@ -47,6 +47,14 @@ type fileMeta struct { } type fileDesc struct{ f *File } +// altOptions returns m as is if it is non-nil. Otherwise, it returns alt. +func altOptions(m, alt pref.ProtoMessage) pref.ProtoMessage { + if m != nil { + return m + } + return alt +} + func newFile(f *File) fileDesc { if f.fileMeta != nil { panic("already initialized") @@ -61,7 +69,7 @@ func (t fileDesc) Name() pref.Name { return t.f func (t fileDesc) FullName() pref.FullName { return t.f.Package } func (t fileDesc) IsPlaceholder() bool { return false } func (t fileDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t fileDesc) Options() pref.ProtoMessage { return t.f.Options } +func (t fileDesc) Options() pref.ProtoMessage { return altOptions(t.f.Options, optionTypes.File) } func (t fileDesc) Path() string { return t.f.Path } func (t fileDesc) Package() pref.FullName { return t.f.Package } func (t fileDesc) Imports() pref.FileImports { return (*fileImports)(&t.f.Imports) } @@ -169,7 +177,7 @@ func (t messageDesc) Name() pref.Name { return t.m.Name } func (t messageDesc) FullName() pref.FullName { return t.m.fullName } func (t messageDesc) IsPlaceholder() bool { return false } func (t messageDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t messageDesc) Options() pref.ProtoMessage { return t.m.Options } +func (t messageDesc) Options() pref.ProtoMessage { return altOptions(t.m.Options, optionTypes.Message) } func (t messageDesc) IsMapEntry() bool { return t.m.mo.lazyInit(t).isMapEntry } func (t messageDesc) Fields() pref.FieldDescriptors { return t.m.fs.lazyInit(t, t.m.Fields) } func (t messageDesc) Oneofs() pref.OneofDescriptors { return t.m.os.lazyInit(t, t.m.Oneofs) } @@ -189,7 +197,7 @@ type messageOptions struct { func (p *messageOptions) lazyInit(m pref.MessageDescriptor) *messageOptions { p.once.Do(func() { - if m.Options() != nil { + if m.Options() != optionTypes.Message { const mapEntryFieldNumber = 7 // google.protobuf.MessageOptions.map_entry fs := m.Options().ProtoReflect().KnownFields() p.isMapEntry = fs.Get(mapEntryFieldNumber).Bool() @@ -217,7 +225,7 @@ func (t fieldDesc) Name() pref.Name { return t.f.Name func (t fieldDesc) FullName() pref.FullName { return t.f.fullName } func (t fieldDesc) IsPlaceholder() bool { return false } func (t fieldDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t fieldDesc) Options() pref.ProtoMessage { return t.f.Options } +func (t fieldDesc) Options() pref.ProtoMessage { return altOptions(t.f.Options, optionTypes.Field) } func (t fieldDesc) Number() pref.FieldNumber { return t.f.Number } func (t fieldDesc) Cardinality() pref.Cardinality { return t.f.Cardinality } func (t fieldDesc) Kind() pref.Kind { return t.f.Kind } @@ -302,7 +310,7 @@ func (p *fieldOptions) lazyInit(f pref.FieldDescriptor) *fieldOptions { } } - if f.Options() != nil { + if f.Options() != optionTypes.Field { const packedFieldNumber = 2 // google.protobuf.FieldOptions.packed const weakFieldNumber = 10 // google.protobuf.FieldOptions.weak fs := f.Options().ProtoReflect().KnownFields() @@ -317,7 +325,7 @@ func (p *fieldOptions) lazyInit(f pref.FieldDescriptor) *fieldOptions { // isPacked reports whether the packed options is set. func isPacked(m pref.ProtoMessage) (isPacked bool) { - if m != nil { + if m != optionTypes.Field { const packedFieldNumber = 2 // google.protobuf.FieldOptions.packed fs := m.ProtoReflect().KnownFields() isPacked = fs.Get(packedFieldNumber).Bool() @@ -327,7 +335,7 @@ func isPacked(m pref.ProtoMessage) (isPacked bool) { // isWeak reports whether the weak options is set. func isWeak(m pref.ProtoMessage) (isWeak bool) { - if m != nil { + if m != optionTypes.Field { const weakFieldNumber = 10 // google.protobuf.FieldOptions.weak fs := m.ProtoReflect().KnownFields() isWeak = fs.Get(weakFieldNumber).Bool() @@ -366,7 +374,7 @@ func (t oneofDesc) Name() pref.Name { return t.o.Name } func (t oneofDesc) FullName() pref.FullName { return t.o.fullName } func (t oneofDesc) IsPlaceholder() bool { return false } func (t oneofDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t oneofDesc) Options() pref.ProtoMessage { return t.o.Options } +func (t oneofDesc) Options() pref.ProtoMessage { return altOptions(t.o.Options, optionTypes.Oneof) } func (t oneofDesc) Fields() pref.FieldDescriptors { return t.o.fs.lazyInit(t) } func (t oneofDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, t) } func (t oneofDesc) ProtoType(pref.OneofDescriptor) {} @@ -389,7 +397,7 @@ func (t extensionDesc) Name() pref.Name { return t.x. func (t extensionDesc) FullName() pref.FullName { return t.x.fullName } func (t extensionDesc) IsPlaceholder() bool { return false } func (t extensionDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t extensionDesc) Options() pref.ProtoMessage { return t.x.Options } +func (t extensionDesc) Options() pref.ProtoMessage { return altOptions(t.x.Options, optionTypes.Field) } func (t extensionDesc) Number() pref.FieldNumber { return t.x.Number } func (t extensionDesc) Cardinality() pref.Cardinality { return t.x.Cardinality } func (t extensionDesc) Kind() pref.Kind { return t.x.Kind } @@ -426,7 +434,7 @@ func (t enumDesc) Name() pref.Name { return t.e.Name } func (t enumDesc) FullName() pref.FullName { return t.e.fullName } func (t enumDesc) IsPlaceholder() bool { return false } func (t enumDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t enumDesc) Options() pref.ProtoMessage { return t.e.Options } +func (t enumDesc) Options() pref.ProtoMessage { return altOptions(t.e.Options, optionTypes.Enum) } func (t enumDesc) Values() pref.EnumValueDescriptors { return t.e.vs.lazyInit(t, t.e.Values) } func (t enumDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, t) } func (t enumDesc) ProtoType(pref.EnumDescriptor) {} @@ -444,11 +452,13 @@ func (t enumValueDesc) Name() pref.Name { return t.v.Name func (t enumValueDesc) FullName() pref.FullName { return t.v.fullName } func (t enumValueDesc) IsPlaceholder() bool { return false } func (t enumValueDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t enumValueDesc) Options() pref.ProtoMessage { return t.v.Options } -func (t enumValueDesc) Number() pref.EnumNumber { return t.v.Number } -func (t enumValueDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, t) } -func (t enumValueDesc) ProtoType(pref.EnumValueDescriptor) {} -func (t enumValueDesc) ProtoInternal(pragma.DoNotImplement) {} +func (t enumValueDesc) Options() pref.ProtoMessage { + return altOptions(t.v.Options, optionTypes.EnumValue) +} +func (t enumValueDesc) Number() pref.EnumNumber { return t.v.Number } +func (t enumValueDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, t) } +func (t enumValueDesc) ProtoType(pref.EnumValueDescriptor) {} +func (t enumValueDesc) ProtoInternal(pragma.DoNotImplement) {} type serviceMeta struct { inheritedMeta @@ -464,7 +474,7 @@ func (t serviceDesc) Name() pref.Name { return t.s.Name } func (t serviceDesc) FullName() pref.FullName { return t.s.fullName } func (t serviceDesc) IsPlaceholder() bool { return false } func (t serviceDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t serviceDesc) Options() pref.ProtoMessage { return t.s.Options } +func (t serviceDesc) Options() pref.ProtoMessage { return altOptions(t.s.Options, optionTypes.Service) } func (t serviceDesc) Methods() pref.MethodDescriptors { return t.s.ms.lazyInit(t, t.s.Methods) } func (t serviceDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, t) } func (t serviceDesc) ProtoType(pref.ServiceDescriptor) {} @@ -485,7 +495,7 @@ func (t methodDesc) Name() pref.Name { return t.m.Name } func (t methodDesc) FullName() pref.FullName { return t.m.fullName } func (t methodDesc) IsPlaceholder() bool { return false } func (t methodDesc) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t methodDesc) Options() pref.ProtoMessage { return t.m.Options } +func (t methodDesc) Options() pref.ProtoMessage { return altOptions(t.m.Options, optionTypes.Method) } func (t methodDesc) InputType() pref.MessageDescriptor { return t.m.mit.lazyInit(t, &t.m.InputType) } func (t methodDesc) OutputType() pref.MessageDescriptor { return t.m.mot.lazyInit(t, &t.m.OutputType) } func (t methodDesc) IsStreamingClient() bool { return t.m.IsStreamingClient } diff --git a/reflect/prototype/standalone_type.go b/reflect/prototype/standalone_type.go index 01f694fd..12c23355 100644 --- a/reflect/prototype/standalone_type.go +++ b/reflect/prototype/standalone_type.go @@ -21,7 +21,9 @@ func (t standaloneMessage) Name() pref.Name { return t.m.F func (t standaloneMessage) FullName() pref.FullName { return t.m.FullName } func (t standaloneMessage) IsPlaceholder() bool { return false } func (t standaloneMessage) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t standaloneMessage) Options() pref.ProtoMessage { return t.m.Options } +func (t standaloneMessage) Options() pref.ProtoMessage { + return altOptions(t.m.Options, optionTypes.Message) +} func (t standaloneMessage) IsMapEntry() bool { return t.m.options.lazyInit(t).isMapEntry } func (t standaloneMessage) Fields() pref.FieldDescriptors { return t.m.fields.lazyInit(t, t.m.Fields) } func (t standaloneMessage) Oneofs() pref.OneofDescriptors { return t.m.oneofs.lazyInit(t, t.m.Oneofs) } @@ -43,7 +45,7 @@ func (t standaloneEnum) Name() pref.Name { return t.e.Full func (t standaloneEnum) FullName() pref.FullName { return t.e.FullName } func (t standaloneEnum) IsPlaceholder() bool { return false } func (t standaloneEnum) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t standaloneEnum) Options() pref.ProtoMessage { return t.e.Options } +func (t standaloneEnum) Options() pref.ProtoMessage { return altOptions(t.e.Options, optionTypes.Enum) } func (t standaloneEnum) Values() pref.EnumValueDescriptors { return t.e.vals.lazyInit(t, t.e.Values) } func (t standaloneEnum) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, t) } func (t standaloneEnum) ProtoType(pref.EnumDescriptor) {} @@ -58,15 +60,17 @@ func (t standaloneExtension) Name() pref.Name { return t.x func (t standaloneExtension) FullName() pref.FullName { return t.x.FullName } func (t standaloneExtension) IsPlaceholder() bool { return false } func (t standaloneExtension) DescriptorProto() (pref.Message, bool) { return nil, false } -func (t standaloneExtension) Options() pref.ProtoMessage { return t.x.Options } -func (t standaloneExtension) Number() pref.FieldNumber { return t.x.Number } -func (t standaloneExtension) Cardinality() pref.Cardinality { return t.x.Cardinality } -func (t standaloneExtension) Kind() pref.Kind { return t.x.Kind } -func (t standaloneExtension) JSONName() string { return "" } -func (t standaloneExtension) IsPacked() bool { return isPacked(t.Options()) } -func (t standaloneExtension) IsWeak() bool { return false } -func (t standaloneExtension) IsMap() bool { return false } -func (t standaloneExtension) Default() pref.Value { return t.x.dv.value(t, t.x.Default) } +func (t standaloneExtension) Options() pref.ProtoMessage { + return altOptions(t.x.Options, optionTypes.Field) +} +func (t standaloneExtension) Number() pref.FieldNumber { return t.x.Number } +func (t standaloneExtension) Cardinality() pref.Cardinality { return t.x.Cardinality } +func (t standaloneExtension) Kind() pref.Kind { return t.x.Kind } +func (t standaloneExtension) JSONName() string { return "" } +func (t standaloneExtension) IsPacked() bool { return isPacked(t.Options()) } +func (t standaloneExtension) IsWeak() bool { return false } +func (t standaloneExtension) IsMap() bool { return false } +func (t standaloneExtension) Default() pref.Value { return t.x.dv.value(t, t.x.Default) } func (t standaloneExtension) DefaultEnumValue() pref.EnumValueDescriptor { return t.x.dv.enum(t, t.x.Default) } diff --git a/types/descriptor/descriptor.pb.go b/types/descriptor/descriptor.pb.go index 2732983a..e91475db 100644 --- a/types/descriptor/descriptor.pb.go +++ b/types/descriptor/descriptor.pb.go @@ -3039,6 +3039,15 @@ func init() { if err != nil { panic(err) } + prototype.X.RegisterExtensionRangeOptions((*ExtensionRangeOptions)(nil)) + prototype.X.RegisterFileOptions((*FileOptions)(nil)) + prototype.X.RegisterMessageOptions((*MessageOptions)(nil)) + prototype.X.RegisterFieldOptions((*FieldOptions)(nil)) + prototype.X.RegisterOneofOptions((*OneofOptions)(nil)) + prototype.X.RegisterEnumOptions((*EnumOptions)(nil)) + prototype.X.RegisterEnumValueOptions((*EnumValueOptions)(nil)) + prototype.X.RegisterServiceOptions((*ServiceOptions)(nil)) + prototype.X.RegisterMethodOptions((*MethodOptions)(nil)) } const _ = protoimpl.EnforceVersion(protoimpl.Version - 0)