blob: 7868bb28d065df861c9a2cabd5139b4bcc96a637 [file] [log] [blame]
Joe Tsai23ddbd12018-08-26 22:48:17 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Joe Tsaie1f8d502018-11-26 18:55:29 -08005// Package protodesc provides for converting descriptorpb.FileDescriptorProto
6// to/from the reflective protoreflect.FileDescriptor.
7package protodesc
Joe Tsai23ddbd12018-08-26 22:48:17 -07008
9import (
Joe Tsai23ddbd12018-08-26 22:48:17 -070010 "strings"
11
Damien Neile89e6242019-05-13 23:55:40 -070012 "google.golang.org/protobuf/internal/encoding/defval"
13 "google.golang.org/protobuf/internal/errors"
Joe Tsaid8881392019-06-06 13:01:53 -070014 "google.golang.org/protobuf/internal/filedesc"
Damien Neile89e6242019-05-13 23:55:40 -070015 "google.golang.org/protobuf/reflect/protoreflect"
Damien Neil3631e222019-06-19 10:21:17 -070016 "google.golang.org/protobuf/reflect/protoregistry"
Joe Tsaie1f8d502018-11-26 18:55:29 -080017
Joe Tsaia95b29f2019-05-16 12:47:20 -070018 "google.golang.org/protobuf/types/descriptorpb"
Joe Tsai23ddbd12018-08-26 22:48:17 -070019)
20
Joe Tsai5857a5a2019-06-16 00:36:42 -070021// Resolver is the resolver used by NewFile to resolve dependencies.
22// It is implemented by protoregistry.Files.
23type Resolver interface {
24 FindFileByPath(string) (protoreflect.FileDescriptor, error)
25 FindEnumByName(protoreflect.FullName) (protoreflect.EnumDescriptor, error)
26 FindMessageByName(protoreflect.FullName) (protoreflect.MessageDescriptor, error)
27}
28
Joe Tsai23ddbd12018-08-26 22:48:17 -070029// TODO: Should we be responsible for validating other parts of the descriptor
30// that we don't directly use?
31//
32// For example:
Joe Tsai23ddbd12018-08-26 22:48:17 -070033// * That "json_name" is not set for an extension field. Maybe, maybe not.
Joe Tsai23ddbd12018-08-26 22:48:17 -070034// * That "weak" is not set for an extension field (double check this).
35
36// TODO: Store the input file descriptor to implement:
37// * protoreflect.Descriptor.DescriptorProto
38// * protoreflect.Descriptor.DescriptorOptions
39
40// TODO: Should we return a File instead of protoreflect.FileDescriptor?
41// This would allow users to mutate the File before converting it.
42// However, this will complicate future work for validation since File may now
43// diverge from the stored descriptor proto (see above TODO).
44
Joe Tsaid8881392019-06-06 13:01:53 -070045// TODO: This is important to prevent users from creating invalid types,
46// but is not functionality needed now.
47//
48// Things to verify:
49// * Weak fields are only used if flags.Proto1Legacy is set
50// * Weak fields can only reference singular messages
51// (check if this the case for oneof fields)
52// * FieldDescriptor.MessageType cannot reference a remote type when the
53// remote name is a type within the local file.
54// * Default enum identifiers resolve to a declared number.
55// * Default values are only allowed in proto2.
56// * Default strings are valid UTF-8? Note that protoc does not check this.
57// * Field extensions are only valid in proto2, except when extending the
58// descriptor options.
59// * Remote enum and message types are actually found in imported files.
60// * Placeholder messages and types may only be for weak fields.
61// * Placeholder full names must be valid.
62// * The name of each descriptor must be valid.
63// * Options are of the correct Go type (e.g. *descriptorpb.MessageOptions).
64// * len(ExtensionRangeOptions) <= len(ExtensionRanges)
65
Joe Tsaie1f8d502018-11-26 18:55:29 -080066// NewFile creates a new protoreflect.FileDescriptor from the provided
67// file descriptor message. The file must represent a valid proto file according
68// to protobuf semantics.
Joe Tsai23ddbd12018-08-26 22:48:17 -070069//
70// Any import files, enum types, or message types referenced in the file are
71// resolved using the provided registry. When looking up an import file path,
72// the path must be unique. The newly created file descriptor is not registered
73// back into the provided file registry.
74//
75// The caller must relinquish full ownership of the input fd and must not
76// access or mutate any fields.
Joe Tsai5857a5a2019-06-16 00:36:42 -070077func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
Damien Neil3631e222019-06-19 10:21:17 -070078 if r == nil {
79 r = (*protoregistry.Files)(nil) // empty resolver
80 }
Joe Tsaid8881392019-06-06 13:01:53 -070081 f := &filedesc.File{L2: &filedesc.FileL2{}}
Joe Tsai23ddbd12018-08-26 22:48:17 -070082 switch fd.GetSyntax() {
Joe Tsaibce82b82018-12-06 09:39:03 -080083 case "proto2", "":
Joe Tsaid8881392019-06-06 13:01:53 -070084 f.L1.Syntax = protoreflect.Proto2
Joe Tsai23ddbd12018-08-26 22:48:17 -070085 case "proto3":
Joe Tsaid8881392019-06-06 13:01:53 -070086 f.L1.Syntax = protoreflect.Proto3
Joe Tsai23ddbd12018-08-26 22:48:17 -070087 default:
88 return nil, errors.New("invalid syntax: %v", fd.GetSyntax())
89 }
Joe Tsaid8881392019-06-06 13:01:53 -070090 f.L1.Path = fd.GetName()
91 f.L1.Package = protoreflect.FullName(fd.GetPackage())
92 if opts := fd.GetOptions(); opts != nil {
93 f.L2.Options = func() protoreflect.ProtoMessage { return opts }
94 }
Joe Tsai23ddbd12018-08-26 22:48:17 -070095
Joe Tsaid8881392019-06-06 13:01:53 -070096 f.L2.Imports = make(filedesc.FileImports, len(fd.GetDependency()))
Joe Tsai23ddbd12018-08-26 22:48:17 -070097 for _, i := range fd.GetPublicDependency() {
Joe Tsaid8881392019-06-06 13:01:53 -070098 if int(i) >= len(f.L2.Imports) || f.L2.Imports[i].IsPublic {
Joe Tsai23ddbd12018-08-26 22:48:17 -070099 return nil, errors.New("invalid or duplicate public import index: %d", i)
100 }
Joe Tsaid8881392019-06-06 13:01:53 -0700101 f.L2.Imports[i].IsPublic = true
Joe Tsai23ddbd12018-08-26 22:48:17 -0700102 }
103 for _, i := range fd.GetWeakDependency() {
Joe Tsaid8881392019-06-06 13:01:53 -0700104 if int(i) >= len(f.L2.Imports) || f.L2.Imports[i].IsWeak {
Joe Tsai23ddbd12018-08-26 22:48:17 -0700105 return nil, errors.New("invalid or duplicate weak import index: %d", i)
106 }
Joe Tsaid8881392019-06-06 13:01:53 -0700107 f.L2.Imports[i].IsWeak = true
Joe Tsai23ddbd12018-08-26 22:48:17 -0700108 }
109 for i, path := range fd.GetDependency() {
Joe Tsaid8881392019-06-06 13:01:53 -0700110 imp := &f.L2.Imports[i]
111 f, err := r.FindFileByPath(path)
Joe Tsaibd7b7a92019-06-15 05:39:04 -0700112 if err != nil {
Joe Tsaid8881392019-06-06 13:01:53 -0700113 // TODO: This should be an error.
114 f = filedesc.PlaceholderFile(path)
Joe Tsai23ddbd12018-08-26 22:48:17 -0700115 }
Joe Tsaid8881392019-06-06 13:01:53 -0700116 imp.FileDescriptor = f
Joe Tsai23ddbd12018-08-26 22:48:17 -0700117 }
118
Joe Tsaid8881392019-06-06 13:01:53 -0700119 // Step 1: Pre-initialize all declared enums and messages.
120 // This enables step 2 to properly resolve references to locally defined
121 // enums and messages.
122 rl := make(descsByName)
123 f.L1.Enums.List = rl.initEnumsFromDescriptorProto(fd.GetEnumType(), f)
124 f.L1.Messages.List = rl.initMessagesFromDescriptorProto(fd.GetMessageType(), f)
125 f.L1.Extensions.List = rl.initExtensionsFromDescriptorProto(fd.GetExtension(), f)
126 f.L1.Services.List = rl.initServicesFromDescriptorProto(fd.GetService(), f)
John Wright9a824c92019-05-03 14:05:20 -0600127
Joe Tsaid8881392019-06-06 13:01:53 -0700128 // Step 2: Handle every enum, message, extension, or service declaration.
129 r = resolver{local: rl, remote: r} // wrap remote resolver with local declarations
130 imps := importSet{f.L1.Path: true}
131 imps.importFiles(f.L2.Imports)
132 if err := enumsFromDescriptorProto(f.L1.Enums.List, fd.GetEnumType(), imps, r); err != nil {
Joe Tsai23ddbd12018-08-26 22:48:17 -0700133 return nil, err
134 }
Joe Tsaid8881392019-06-06 13:01:53 -0700135 if err := messagesFromDescriptorProto(f.L1.Messages.List, fd.GetMessageType(), imps, r); err != nil {
Joe Tsai23ddbd12018-08-26 22:48:17 -0700136 return nil, err
137 }
Joe Tsaid8881392019-06-06 13:01:53 -0700138 if err := extensionsFromDescriptorProto(f.L1.Extensions.List, fd.GetExtension(), imps, r); err != nil {
Joe Tsai23ddbd12018-08-26 22:48:17 -0700139 return nil, err
140 }
Joe Tsaid8881392019-06-06 13:01:53 -0700141 if err := servicesFromDescriptorProto(f.L1.Services.List, fd.GetService(), imps, r); err != nil {
Joe Tsai23ddbd12018-08-26 22:48:17 -0700142 return nil, err
143 }
144
Joe Tsaid8881392019-06-06 13:01:53 -0700145 return f, nil
Joe Tsai23ddbd12018-08-26 22:48:17 -0700146}
147
Joe Tsaid8881392019-06-06 13:01:53 -0700148type descsByName map[protoreflect.FullName]protoreflect.Descriptor
John Wright9a824c92019-05-03 14:05:20 -0600149
Joe Tsaid8881392019-06-06 13:01:53 -0700150func (r descsByName) initEnumsFromDescriptorProto(eds []*descriptorpb.EnumDescriptorProto, parent protoreflect.Descriptor) []filedesc.Enum {
151 es := make([]filedesc.Enum, len(eds)) // allocate up-front to ensure stable pointers
152 for i, ed := range eds {
153 e := &es[i]
154 e.L0 = makeBase(parent, ed.GetName(), i)
155 r[e.FullName()] = e
John Wright9a824c92019-05-03 14:05:20 -0600156 }
Joe Tsaid8881392019-06-06 13:01:53 -0700157 return es
John Wright9a824c92019-05-03 14:05:20 -0600158}
159
Joe Tsaid8881392019-06-06 13:01:53 -0700160func (r descsByName) initMessagesFromDescriptorProto(mds []*descriptorpb.DescriptorProto, parent protoreflect.Descriptor) []filedesc.Message {
161 ms := make([]filedesc.Message, len(mds)) // allocate up-front to ensure stable pointers
162 for i, md := range mds {
163 m := &ms[i]
164 m.L0 = makeBase(parent, md.GetName(), i)
165 m.L1.Enums.List = r.initEnumsFromDescriptorProto(md.GetEnumType(), m)
166 m.L1.Messages.List = r.initMessagesFromDescriptorProto(md.GetNestedType(), m)
167 m.L1.Extensions.List = r.initExtensionsFromDescriptorProto(md.GetExtension(), m)
168 r[m.FullName()] = m
169 }
170 return ms
171}
172
173func (r descsByName) initExtensionsFromDescriptorProto(xds []*descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor) []filedesc.Extension {
174 xs := make([]filedesc.Extension, len(xds)) // allocate up-front to ensure stable pointers
175 for i, xd := range xds {
176 x := &xs[i]
177 x.L0 = makeBase(parent, xd.GetName(), i)
178 r[x.FullName()] = x
179 }
180 return xs
181}
182
183func (r descsByName) initServicesFromDescriptorProto(sds []*descriptorpb.ServiceDescriptorProto, parent protoreflect.Descriptor) []filedesc.Service {
184 ss := make([]filedesc.Service, len(sds)) // allocate up-front to ensure stable pointers
185 for i, sd := range sds {
186 s := &ss[i]
187 s.L0 = makeBase(parent, sd.GetName(), i)
188 r[s.FullName()] = s
189 }
190 return ss
191}
192
193func makeBase(parent protoreflect.Descriptor, name string, idx int) filedesc.BaseL0 {
194 return filedesc.BaseL0{
195 FullName: parent.FullName().Append(protoreflect.Name(name)),
196 ParentFile: parent.ParentFile().(*filedesc.File),
197 Parent: parent,
198 Index: idx,
199 }
200}
201
202func enumsFromDescriptorProto(es []filedesc.Enum, eds []*descriptorpb.EnumDescriptorProto, imps importSet, r Resolver) error {
203 for i, ed := range eds {
204 e := &es[i]
205 e.L2 = new(filedesc.EnumL2)
206 if opts := ed.GetOptions(); opts != nil {
207 e.L2.Options = func() protoreflect.ProtoMessage { return opts }
208 }
209 for _, s := range ed.GetReservedName() {
210 e.L2.ReservedNames.List = append(e.L2.ReservedNames.List, protoreflect.Name(s))
211 }
212 for _, rr := range ed.GetReservedRange() {
213 e.L2.ReservedRanges.List = append(e.L2.ReservedRanges.List, [2]protoreflect.EnumNumber{
214 protoreflect.EnumNumber(rr.GetStart()),
215 protoreflect.EnumNumber(rr.GetEnd()),
216 })
217 }
218 e.L2.Values.List = make([]filedesc.EnumValue, len(ed.GetValue()))
219 for j, vd := range ed.GetValue() {
220 v := &e.L2.Values.List[j]
221 v.L0 = makeBase(e, vd.GetName(), j)
222 v.L0.FullName = e.L0.Parent.FullName().Append(protoreflect.Name(vd.GetName())) // enum values are in the same scope as the enum itself
223 if opts := vd.GetOptions(); opts != nil {
224 v.L1.Options = func() protoreflect.ProtoMessage { return opts }
225 }
226 v.L1.Number = protoreflect.EnumNumber(vd.GetNumber())
227 if e.L2.ReservedNames.Has(v.Name()) {
228 return errors.New("enum %v contains value with reserved name %q", e.Name(), v.Name())
229 }
230 if e.L2.ReservedRanges.Has(v.Number()) {
231 return errors.New("enum %v contains value with reserved number %d", e.Name(), v.Number())
232 }
John Wright9a824c92019-05-03 14:05:20 -0600233 }
234 }
Joe Tsaid8881392019-06-06 13:01:53 -0700235 return nil
John Wright9a824c92019-05-03 14:05:20 -0600236}
237
Joe Tsaid8881392019-06-06 13:01:53 -0700238func messagesFromDescriptorProto(ms []filedesc.Message, mds []*descriptorpb.DescriptorProto, imps importSet, r Resolver) error {
239 for i, md := range mds {
240 m := &ms[i]
John Wright9a824c92019-05-03 14:05:20 -0600241
Joe Tsaid8881392019-06-06 13:01:53 -0700242 // Handle nested declarations. All enums must be handled before handling
243 // any messages since an enum field may have a default value that is
244 // specified in the same file being constructed.
245 if err := enumsFromDescriptorProto(m.L1.Enums.List, md.GetEnumType(), imps, r); err != nil {
246 return err
247 }
248 if err := messagesFromDescriptorProto(m.L1.Messages.List, md.GetNestedType(), imps, r); err != nil {
249 return err
250 }
251 if err := extensionsFromDescriptorProto(m.L1.Extensions.List, md.GetExtension(), imps, r); err != nil {
252 return err
253 }
254
255 // Handle the message descriptor itself.
256 m.L2 = new(filedesc.MessageL2)
257 if opts := md.GetOptions(); opts != nil {
258 m.L2.Options = func() protoreflect.ProtoMessage { return opts }
259 m.L2.IsMapEntry = opts.GetMapEntry()
260 m.L2.IsMessageSet = opts.GetMessageSetWireFormat()
261 }
John Wright9a824c92019-05-03 14:05:20 -0600262 for _, s := range md.GetReservedName() {
Joe Tsaid8881392019-06-06 13:01:53 -0700263 m.L2.ReservedNames.List = append(m.L2.ReservedNames.List, protoreflect.Name(s))
John Wright9a824c92019-05-03 14:05:20 -0600264 }
265 for _, rr := range md.GetReservedRange() {
Joe Tsaid8881392019-06-06 13:01:53 -0700266 m.L2.ReservedRanges.List = append(m.L2.ReservedRanges.List, [2]protoreflect.FieldNumber{
John Wright9a824c92019-05-03 14:05:20 -0600267 protoreflect.FieldNumber(rr.GetStart()),
268 protoreflect.FieldNumber(rr.GetEnd()),
269 })
270 }
271 for _, xr := range md.GetExtensionRange() {
Joe Tsaid8881392019-06-06 13:01:53 -0700272 m.L2.ExtensionRanges.List = append(m.L2.ExtensionRanges.List, [2]protoreflect.FieldNumber{
John Wright9a824c92019-05-03 14:05:20 -0600273 protoreflect.FieldNumber(xr.GetStart()),
274 protoreflect.FieldNumber(xr.GetEnd()),
275 })
Joe Tsaid8881392019-06-06 13:01:53 -0700276 var optsFunc func() protoreflect.ProtoMessage
277 if opts := xr.GetOptions(); opts != nil {
278 optsFunc = func() protoreflect.ProtoMessage { return opts }
279 }
280 m.L2.ExtensionRangeOptions = append(m.L2.ExtensionRangeOptions, optsFunc)
John Wright9a824c92019-05-03 14:05:20 -0600281 }
Joe Tsaid8881392019-06-06 13:01:53 -0700282 m.L2.Fields.List = make([]filedesc.Field, len(md.GetField()))
283 m.L2.Oneofs.List = make([]filedesc.Oneof, len(md.GetOneofDecl()))
284 for j, fd := range md.GetField() {
285 f := &m.L2.Fields.List[j]
286 f.L0 = makeBase(m, fd.GetName(), j)
287 if opts := fd.GetOptions(); opts != nil {
288 f.L1.Options = func() protoreflect.ProtoMessage { return opts }
289 f.L1.IsWeak = opts.GetWeak()
290 f.L1.HasPacked = opts.Packed != nil
291 f.L1.IsPacked = opts.GetPacked()
John Wright9a824c92019-05-03 14:05:20 -0600292 }
Joe Tsaid8881392019-06-06 13:01:53 -0700293 if m.L2.ReservedNames.Has(f.Name()) {
294 return errors.New("%v contains field with reserved name %q", m.Name(), f.Name())
John Wright9a824c92019-05-03 14:05:20 -0600295 }
Joe Tsaid8881392019-06-06 13:01:53 -0700296 f.L1.Number = protoreflect.FieldNumber(fd.GetNumber())
297 if m.L2.ReservedRanges.Has(f.Number()) {
298 return errors.New("%v contains field with reserved number %d", m.Name(), f.Number())
John Wright9a824c92019-05-03 14:05:20 -0600299 }
Joe Tsaid8881392019-06-06 13:01:53 -0700300 if m.L2.ExtensionRanges.Has(f.Number()) {
301 return errors.New("%v contains field with number %d in extension range", m.Name(), f.Number())
John Wright9a824c92019-05-03 14:05:20 -0600302 }
Joe Tsaid8881392019-06-06 13:01:53 -0700303 f.L1.Cardinality = protoreflect.Cardinality(fd.GetLabel())
304 if f.L1.Cardinality == protoreflect.Required {
305 m.L2.RequiredNumbers.List = append(m.L2.RequiredNumbers.List, f.L1.Number)
Damien Neil232ea152018-12-10 15:14:36 -0800306 }
Joe Tsaid8881392019-06-06 13:01:53 -0700307 f.L1.Kind = protoreflect.Kind(fd.GetType())
308 if fd.JsonName != nil {
309 f.L1.JSONName = filedesc.JSONName(fd.GetJsonName())
Joe Tsai23ddbd12018-08-26 22:48:17 -0700310 }
311 if fd.OneofIndex != nil {
Joe Tsaid8881392019-06-06 13:01:53 -0700312 k := int(fd.GetOneofIndex())
313 if k >= len(md.GetOneofDecl()) {
314 return errors.New("invalid oneof index: %d", k)
Joe Tsai23ddbd12018-08-26 22:48:17 -0700315 }
Joe Tsaid8881392019-06-06 13:01:53 -0700316 o := &m.L2.Oneofs.List[k]
317 f.L1.ContainingOneof = o
318 o.L1.Fields.List = append(o.L1.Fields.List, f)
Joe Tsai23ddbd12018-08-26 22:48:17 -0700319 }
Joe Tsaid8881392019-06-06 13:01:53 -0700320 if fd.GetExtendee() != "" {
321 return errors.New("message field may not have extendee")
322 }
323 switch f.L1.Kind {
Joe Tsai23ddbd12018-08-26 22:48:17 -0700324 case protoreflect.EnumKind:
Joe Tsaid8881392019-06-06 13:01:53 -0700325 ed, err := findEnumDescriptor(fd.GetTypeName(), f.L1.IsWeak, imps, r)
Joe Tsai23ddbd12018-08-26 22:48:17 -0700326 if err != nil {
Joe Tsaid8881392019-06-06 13:01:53 -0700327 return err
Joe Tsai23ddbd12018-08-26 22:48:17 -0700328 }
Joe Tsaid8881392019-06-06 13:01:53 -0700329 f.L1.Enum = ed
Joe Tsai23ddbd12018-08-26 22:48:17 -0700330 case protoreflect.MessageKind, protoreflect.GroupKind:
Joe Tsaid8881392019-06-06 13:01:53 -0700331 md, err := findMessageDescriptor(fd.GetTypeName(), f.L1.IsWeak, imps, r)
Joe Tsai23ddbd12018-08-26 22:48:17 -0700332 if err != nil {
Joe Tsaid8881392019-06-06 13:01:53 -0700333 return err
Joe Tsai23ddbd12018-08-26 22:48:17 -0700334 }
Joe Tsaid8881392019-06-06 13:01:53 -0700335 f.L1.Message = md
John Wright9a824c92019-05-03 14:05:20 -0600336 default:
337 if fd.GetTypeName() != "" {
Joe Tsaid8881392019-06-06 13:01:53 -0700338 return errors.New("field of kind %v has type_name set", f.L1.Kind)
John Wright9a824c92019-05-03 14:05:20 -0600339 }
Joe Tsai23ddbd12018-08-26 22:48:17 -0700340 }
Joe Tsaid8881392019-06-06 13:01:53 -0700341 if fd.DefaultValue != nil {
342 // Handle default value after resolving the enum since the
343 // list of enum values is needed to resolve enums by name.
344 var evs protoreflect.EnumValueDescriptors
345 if f.L1.Kind == protoreflect.EnumKind {
346 evs = f.L1.Enum.Values()
347 }
348 v, ev, err := defval.Unmarshal(fd.GetDefaultValue(), f.L1.Kind, evs, defval.Descriptor)
349 if err != nil {
350 return err
351 }
352 f.L1.Default = filedesc.DefaultValue(v, ev)
353 }
Joe Tsai23ddbd12018-08-26 22:48:17 -0700354 }
Joe Tsaid8881392019-06-06 13:01:53 -0700355 for j, od := range md.GetOneofDecl() {
356 o := &m.L2.Oneofs.List[j]
357 o.L0 = makeBase(m, od.GetName(), j)
358 if opts := od.GetOptions(); opts != nil {
359 o.L1.Options = func() protoreflect.ProtoMessage { return opts }
360 }
Joe Tsai23ddbd12018-08-26 22:48:17 -0700361 }
Joe Tsai23ddbd12018-08-26 22:48:17 -0700362 }
Joe Tsaid8881392019-06-06 13:01:53 -0700363 return nil
Joe Tsai23ddbd12018-08-26 22:48:17 -0700364}
365
Joe Tsaid8881392019-06-06 13:01:53 -0700366func extensionsFromDescriptorProto(xs []filedesc.Extension, xds []*descriptorpb.FieldDescriptorProto, imps importSet, r Resolver) error {
367 for i, xd := range xds {
368 x := &xs[i]
369 x.L2 = new(filedesc.ExtensionL2)
370 if opts := xd.GetOptions(); opts != nil {
371 x.L2.Options = func() protoreflect.ProtoMessage { return opts }
372 x.L2.IsPacked = opts.GetPacked()
Joe Tsaibce82b82018-12-06 09:39:03 -0800373 }
Joe Tsaid8881392019-06-06 13:01:53 -0700374 x.L1.Number = protoreflect.FieldNumber(xd.GetNumber())
375 x.L2.Cardinality = protoreflect.Cardinality(xd.GetLabel())
376 x.L1.Kind = protoreflect.Kind(xd.GetType())
377 if xd.JsonName != nil {
378 x.L2.JSONName = filedesc.JSONName(xd.GetJsonName())
Joe Tsaibce82b82018-12-06 09:39:03 -0800379 }
John Wright9a824c92019-05-03 14:05:20 -0600380 if xd.OneofIndex != nil {
Joe Tsaid8881392019-06-06 13:01:53 -0700381 return errors.New("extension may not have oneof_index")
John Wright9a824c92019-05-03 14:05:20 -0600382 }
Joe Tsaid8881392019-06-06 13:01:53 -0700383 md, err := findMessageDescriptor(xd.GetExtendee(), false, imps, r)
384 if err != nil {
385 return err
Joe Tsai23ddbd12018-08-26 22:48:17 -0700386 }
Joe Tsaid8881392019-06-06 13:01:53 -0700387 x.L1.Extendee = md
388 switch x.L1.Kind {
Joe Tsai23ddbd12018-08-26 22:48:17 -0700389 case protoreflect.EnumKind:
Joe Tsaid8881392019-06-06 13:01:53 -0700390 ed, err := findEnumDescriptor(xd.GetTypeName(), false, imps, r)
Joe Tsai23ddbd12018-08-26 22:48:17 -0700391 if err != nil {
Joe Tsaid8881392019-06-06 13:01:53 -0700392 return err
Joe Tsai23ddbd12018-08-26 22:48:17 -0700393 }
Joe Tsaid8881392019-06-06 13:01:53 -0700394 x.L2.Enum = ed
Joe Tsai23ddbd12018-08-26 22:48:17 -0700395 case protoreflect.MessageKind, protoreflect.GroupKind:
Joe Tsaid8881392019-06-06 13:01:53 -0700396 md, err := findMessageDescriptor(xd.GetTypeName(), false, imps, r)
Joe Tsai23ddbd12018-08-26 22:48:17 -0700397 if err != nil {
Joe Tsaid8881392019-06-06 13:01:53 -0700398 return err
Joe Tsai23ddbd12018-08-26 22:48:17 -0700399 }
Joe Tsaid8881392019-06-06 13:01:53 -0700400 x.L2.Message = md
John Wright9a824c92019-05-03 14:05:20 -0600401 default:
402 if xd.GetTypeName() != "" {
Joe Tsaid8881392019-06-06 13:01:53 -0700403 return errors.New("field of kind %v has type_name set", x.L1.Kind)
John Wright9a824c92019-05-03 14:05:20 -0600404 }
Joe Tsai23ddbd12018-08-26 22:48:17 -0700405 }
Joe Tsaid8881392019-06-06 13:01:53 -0700406 if xd.DefaultValue != nil {
407 // Handle default value after resolving the enum since the
408 // list of enum values is needed to resolve enums by name.
409 var evs protoreflect.EnumValueDescriptors
410 if x.L1.Kind == protoreflect.EnumKind {
411 evs = x.L2.Enum.Values()
412 }
413 v, ev, err := defval.Unmarshal(xd.GetDefaultValue(), x.L1.Kind, evs, defval.Descriptor)
414 if err != nil {
415 return err
416 }
417 x.L2.Default = filedesc.DefaultValue(v, ev)
Joe Tsai23ddbd12018-08-26 22:48:17 -0700418 }
Joe Tsai23ddbd12018-08-26 22:48:17 -0700419 }
Joe Tsaid8881392019-06-06 13:01:53 -0700420 return nil
Joe Tsai23ddbd12018-08-26 22:48:17 -0700421}
422
Joe Tsaid8881392019-06-06 13:01:53 -0700423func servicesFromDescriptorProto(ss []filedesc.Service, sds []*descriptorpb.ServiceDescriptorProto, imps importSet, r Resolver) (err error) {
424 for i, sd := range sds {
425 s := &ss[i]
426 s.L2 = new(filedesc.ServiceL2)
427 if opts := sd.GetOptions(); opts != nil {
428 s.L2.Options = func() protoreflect.ProtoMessage { return opts }
Joe Tsai23ddbd12018-08-26 22:48:17 -0700429 }
Joe Tsaid8881392019-06-06 13:01:53 -0700430 s.L2.Methods.List = make([]filedesc.Method, len(sd.GetMethod()))
431 for j, md := range sd.GetMethod() {
432 m := &s.L2.Methods.List[j]
433 m.L0 = makeBase(s, md.GetName(), j)
434 if opts := md.GetOptions(); opts != nil {
435 m.L1.Options = func() protoreflect.ProtoMessage { return opts }
436 }
437 m.L1.Input, err = findMessageDescriptor(md.GetInputType(), false, imps, r)
438 if err != nil {
439 return err
440 }
441 m.L1.Output, err = findMessageDescriptor(md.GetOutputType(), false, imps, r)
442 if err != nil {
443 return err
444 }
445 m.L1.IsStreamingClient = md.GetClientStreaming()
446 m.L1.IsStreamingServer = md.GetServerStreaming()
447 }
Joe Tsai23ddbd12018-08-26 22:48:17 -0700448 }
Joe Tsaid8881392019-06-06 13:01:53 -0700449 return nil
450}
451
452type resolver struct {
453 local descsByName
454 remote Resolver
455}
456
457func (r resolver) FindFileByPath(s string) (protoreflect.FileDescriptor, error) {
458 return r.remote.FindFileByPath(s)
459}
460
461func (r resolver) FindEnumByName(s protoreflect.FullName) (protoreflect.EnumDescriptor, error) {
462 if d, ok := r.local[s]; ok {
463 if ed, ok := d.(protoreflect.EnumDescriptor); ok {
464 return ed, nil
465 }
466 return nil, errors.New("found wrong type")
467 }
468 return r.remote.FindEnumByName(s)
469}
470
471func (r resolver) FindMessageByName(s protoreflect.FullName) (protoreflect.MessageDescriptor, error) {
472 if d, ok := r.local[s]; ok {
473 if md, ok := d.(protoreflect.MessageDescriptor); ok {
474 return md, nil
475 }
476 return nil, errors.New("found wrong type")
477 }
478 return r.remote.FindMessageByName(s)
479}
480
481type importSet map[string]bool
482
483func (is importSet) importFiles(files []protoreflect.FileImport) {
484 for _, imp := range files {
485 is[imp.Path()] = true
486 is.importPublic(imp)
487 }
488}
489
490func (is importSet) importPublic(fd protoreflect.FileDescriptor) {
491 imps := fd.Imports()
492 for i := 0; i < imps.Len(); i++ {
493 if imp := imps.Get(i); imp.IsPublic {
494 is[imp.Path()] = true
495 is.importPublic(imp)
496 }
497 }
498}
499
500// check returns an error if d does not belong to a currently imported file.
501func (is importSet) check(d protoreflect.Descriptor) error {
502 if !is[d.ParentFile().Path()] {
503 return errors.New("reference to type %v without import of %v", d.FullName(), d.ParentFile().Path())
504 }
505 return nil
Joe Tsai23ddbd12018-08-26 22:48:17 -0700506}
507
508// TODO: Should we allow relative names? The protoc compiler has emitted
509// absolute names for some time now. Requiring absolute names as an input
510// simplifies our implementation as we won't need to implement C++'s namespace
511// scoping rules.
512
Joe Tsaid8881392019-06-06 13:01:53 -0700513func findEnumDescriptor(s string, isWeak bool, imps importSet, r Resolver) (protoreflect.EnumDescriptor, error) {
Joe Tsai23ddbd12018-08-26 22:48:17 -0700514 if !strings.HasPrefix(s, ".") {
515 return nil, errors.New("identifier name must be fully qualified with a leading dot: %v", s)
516 }
517 name := protoreflect.FullName(strings.TrimPrefix(s, "."))
Damien Neil2300c182019-04-15 13:05:13 -0700518 ed, err := r.FindEnumByName(name)
519 if err != nil {
Joe Tsaid8881392019-06-06 13:01:53 -0700520 if err == protoregistry.NotFound {
521 if isWeak {
522 return filedesc.PlaceholderEnum(name), nil
523 }
524 // TODO: This should be an error.
525 return filedesc.PlaceholderEnum(name), nil
526 // return nil, errors.New("could not resolve enum: %v", name)
527 }
528 return nil, err
Joe Tsai23ddbd12018-08-26 22:48:17 -0700529 }
Joe Tsaid8881392019-06-06 13:01:53 -0700530 if err := imps.check(ed); err != nil {
John Wright9a824c92019-05-03 14:05:20 -0600531 return nil, err
532 }
Damien Neil2300c182019-04-15 13:05:13 -0700533 return ed, nil
Joe Tsai23ddbd12018-08-26 22:48:17 -0700534}
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700535
Joe Tsaid8881392019-06-06 13:01:53 -0700536func findMessageDescriptor(s string, isWeak bool, imps importSet, r Resolver) (protoreflect.MessageDescriptor, error) {
537 if !strings.HasPrefix(s, ".") {
538 return nil, errors.New("identifier name must be fully qualified with a leading dot: %v", s)
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700539 }
Joe Tsaid8881392019-06-06 13:01:53 -0700540 name := protoreflect.FullName(strings.TrimPrefix(s, "."))
541 md, err := r.FindMessageByName(name)
542 if err != nil {
543 if err == protoregistry.NotFound {
544 if isWeak {
545 return filedesc.PlaceholderMessage(name), nil
546 }
547 // TODO: This should be an error.
548 return filedesc.PlaceholderMessage(name), nil
549 // return nil, errors.New("could not resolve message: %v", name)
550 }
551 return nil, err
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700552 }
Joe Tsaid8881392019-06-06 13:01:53 -0700553 if err := imps.check(md); err != nil {
554 return nil, err
555 }
556 return md, nil
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700557}