1// Copyright 2019 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
5package protodesc
6
7import (
8	"google.golang.org/protobuf/internal/errors"
9	"google.golang.org/protobuf/internal/filedesc"
10	"google.golang.org/protobuf/internal/strs"
11	"google.golang.org/protobuf/proto"
12	"google.golang.org/protobuf/reflect/protoreflect"
13
14	"google.golang.org/protobuf/types/descriptorpb"
15)
16
17type descsByName map[protoreflect.FullName]protoreflect.Descriptor
18
19func (r descsByName) initEnumDeclarations(eds []*descriptorpb.EnumDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (es []filedesc.Enum, err error) {
20	es = make([]filedesc.Enum, len(eds)) // allocate up-front to ensure stable pointers
21	for i, ed := range eds {
22		e := &es[i]
23		e.L2 = new(filedesc.EnumL2)
24		if e.L0, err = r.makeBase(e, parent, ed.GetName(), i, sb); err != nil {
25			return nil, err
26		}
27		if opts := ed.GetOptions(); opts != nil {
28			opts = proto.Clone(opts).(*descriptorpb.EnumOptions)
29			e.L2.Options = func() protoreflect.ProtoMessage { return opts }
30		}
31		for _, s := range ed.GetReservedName() {
32			e.L2.ReservedNames.List = append(e.L2.ReservedNames.List, protoreflect.Name(s))
33		}
34		for _, rr := range ed.GetReservedRange() {
35			e.L2.ReservedRanges.List = append(e.L2.ReservedRanges.List, [2]protoreflect.EnumNumber{
36				protoreflect.EnumNumber(rr.GetStart()),
37				protoreflect.EnumNumber(rr.GetEnd()),
38			})
39		}
40		if e.L2.Values.List, err = r.initEnumValuesFromDescriptorProto(ed.GetValue(), e, sb); err != nil {
41			return nil, err
42		}
43	}
44	return es, nil
45}
46
47func (r descsByName) initEnumValuesFromDescriptorProto(vds []*descriptorpb.EnumValueDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (vs []filedesc.EnumValue, err error) {
48	vs = make([]filedesc.EnumValue, len(vds)) // allocate up-front to ensure stable pointers
49	for i, vd := range vds {
50		v := &vs[i]
51		if v.L0, err = r.makeBase(v, parent, vd.GetName(), i, sb); err != nil {
52			return nil, err
53		}
54		if opts := vd.GetOptions(); opts != nil {
55			opts = proto.Clone(opts).(*descriptorpb.EnumValueOptions)
56			v.L1.Options = func() protoreflect.ProtoMessage { return opts }
57		}
58		v.L1.Number = protoreflect.EnumNumber(vd.GetNumber())
59	}
60	return vs, nil
61}
62
63func (r descsByName) initMessagesDeclarations(mds []*descriptorpb.DescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (ms []filedesc.Message, err error) {
64	ms = make([]filedesc.Message, len(mds)) // allocate up-front to ensure stable pointers
65	for i, md := range mds {
66		m := &ms[i]
67		m.L2 = new(filedesc.MessageL2)
68		if m.L0, err = r.makeBase(m, parent, md.GetName(), i, sb); err != nil {
69			return nil, err
70		}
71		if opts := md.GetOptions(); opts != nil {
72			opts = proto.Clone(opts).(*descriptorpb.MessageOptions)
73			m.L2.Options = func() protoreflect.ProtoMessage { return opts }
74			m.L1.IsMapEntry = opts.GetMapEntry()
75			m.L1.IsMessageSet = opts.GetMessageSetWireFormat()
76		}
77		for _, s := range md.GetReservedName() {
78			m.L2.ReservedNames.List = append(m.L2.ReservedNames.List, protoreflect.Name(s))
79		}
80		for _, rr := range md.GetReservedRange() {
81			m.L2.ReservedRanges.List = append(m.L2.ReservedRanges.List, [2]protoreflect.FieldNumber{
82				protoreflect.FieldNumber(rr.GetStart()),
83				protoreflect.FieldNumber(rr.GetEnd()),
84			})
85		}
86		for _, xr := range md.GetExtensionRange() {
87			m.L2.ExtensionRanges.List = append(m.L2.ExtensionRanges.List, [2]protoreflect.FieldNumber{
88				protoreflect.FieldNumber(xr.GetStart()),
89				protoreflect.FieldNumber(xr.GetEnd()),
90			})
91			var optsFunc func() protoreflect.ProtoMessage
92			if opts := xr.GetOptions(); opts != nil {
93				opts = proto.Clone(opts).(*descriptorpb.ExtensionRangeOptions)
94				optsFunc = func() protoreflect.ProtoMessage { return opts }
95			}
96			m.L2.ExtensionRangeOptions = append(m.L2.ExtensionRangeOptions, optsFunc)
97		}
98		if m.L2.Fields.List, err = r.initFieldsFromDescriptorProto(md.GetField(), m, sb); err != nil {
99			return nil, err
100		}
101		if m.L2.Oneofs.List, err = r.initOneofsFromDescriptorProto(md.GetOneofDecl(), m, sb); err != nil {
102			return nil, err
103		}
104		if m.L1.Enums.List, err = r.initEnumDeclarations(md.GetEnumType(), m, sb); err != nil {
105			return nil, err
106		}
107		if m.L1.Messages.List, err = r.initMessagesDeclarations(md.GetNestedType(), m, sb); err != nil {
108			return nil, err
109		}
110		if m.L1.Extensions.List, err = r.initExtensionDeclarations(md.GetExtension(), m, sb); err != nil {
111			return nil, err
112		}
113	}
114	return ms, nil
115}
116
117func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (fs []filedesc.Field, err error) {
118	fs = make([]filedesc.Field, len(fds)) // allocate up-front to ensure stable pointers
119	for i, fd := range fds {
120		f := &fs[i]
121		if f.L0, err = r.makeBase(f, parent, fd.GetName(), i, sb); err != nil {
122			return nil, err
123		}
124		f.L1.IsProto3Optional = fd.GetProto3Optional()
125		if opts := fd.GetOptions(); opts != nil {
126			opts = proto.Clone(opts).(*descriptorpb.FieldOptions)
127			f.L1.Options = func() protoreflect.ProtoMessage { return opts }
128			f.L1.IsWeak = opts.GetWeak()
129			f.L1.HasPacked = opts.Packed != nil
130			f.L1.IsPacked = opts.GetPacked()
131		}
132		f.L1.Number = protoreflect.FieldNumber(fd.GetNumber())
133		f.L1.Cardinality = protoreflect.Cardinality(fd.GetLabel())
134		if fd.Type != nil {
135			f.L1.Kind = protoreflect.Kind(fd.GetType())
136		}
137		if fd.JsonName != nil {
138			f.L1.StringName.InitJSON(fd.GetJsonName())
139		}
140	}
141	return fs, nil
142}
143
144func (r descsByName) initOneofsFromDescriptorProto(ods []*descriptorpb.OneofDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (os []filedesc.Oneof, err error) {
145	os = make([]filedesc.Oneof, len(ods)) // allocate up-front to ensure stable pointers
146	for i, od := range ods {
147		o := &os[i]
148		if o.L0, err = r.makeBase(o, parent, od.GetName(), i, sb); err != nil {
149			return nil, err
150		}
151		if opts := od.GetOptions(); opts != nil {
152			opts = proto.Clone(opts).(*descriptorpb.OneofOptions)
153			o.L1.Options = func() protoreflect.ProtoMessage { return opts }
154		}
155	}
156	return os, nil
157}
158
159func (r descsByName) initExtensionDeclarations(xds []*descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (xs []filedesc.Extension, err error) {
160	xs = make([]filedesc.Extension, len(xds)) // allocate up-front to ensure stable pointers
161	for i, xd := range xds {
162		x := &xs[i]
163		x.L2 = new(filedesc.ExtensionL2)
164		if x.L0, err = r.makeBase(x, parent, xd.GetName(), i, sb); err != nil {
165			return nil, err
166		}
167		if opts := xd.GetOptions(); opts != nil {
168			opts = proto.Clone(opts).(*descriptorpb.FieldOptions)
169			x.L2.Options = func() protoreflect.ProtoMessage { return opts }
170			x.L2.IsPacked = opts.GetPacked()
171		}
172		x.L1.Number = protoreflect.FieldNumber(xd.GetNumber())
173		x.L1.Cardinality = protoreflect.Cardinality(xd.GetLabel())
174		if xd.Type != nil {
175			x.L1.Kind = protoreflect.Kind(xd.GetType())
176		}
177		if xd.JsonName != nil {
178			x.L2.StringName.InitJSON(xd.GetJsonName())
179		}
180	}
181	return xs, nil
182}
183
184func (r descsByName) initServiceDeclarations(sds []*descriptorpb.ServiceDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (ss []filedesc.Service, err error) {
185	ss = make([]filedesc.Service, len(sds)) // allocate up-front to ensure stable pointers
186	for i, sd := range sds {
187		s := &ss[i]
188		s.L2 = new(filedesc.ServiceL2)
189		if s.L0, err = r.makeBase(s, parent, sd.GetName(), i, sb); err != nil {
190			return nil, err
191		}
192		if opts := sd.GetOptions(); opts != nil {
193			opts = proto.Clone(opts).(*descriptorpb.ServiceOptions)
194			s.L2.Options = func() protoreflect.ProtoMessage { return opts }
195		}
196		if s.L2.Methods.List, err = r.initMethodsFromDescriptorProto(sd.GetMethod(), s, sb); err != nil {
197			return nil, err
198		}
199	}
200	return ss, nil
201}
202
203func (r descsByName) initMethodsFromDescriptorProto(mds []*descriptorpb.MethodDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (ms []filedesc.Method, err error) {
204	ms = make([]filedesc.Method, len(mds)) // allocate up-front to ensure stable pointers
205	for i, md := range mds {
206		m := &ms[i]
207		if m.L0, err = r.makeBase(m, parent, md.GetName(), i, sb); err != nil {
208			return nil, err
209		}
210		if opts := md.GetOptions(); opts != nil {
211			opts = proto.Clone(opts).(*descriptorpb.MethodOptions)
212			m.L1.Options = func() protoreflect.ProtoMessage { return opts }
213		}
214		m.L1.IsStreamingClient = md.GetClientStreaming()
215		m.L1.IsStreamingServer = md.GetServerStreaming()
216	}
217	return ms, nil
218}
219
220func (r descsByName) makeBase(child, parent protoreflect.Descriptor, name string, idx int, sb *strs.Builder) (filedesc.BaseL0, error) {
221	if !protoreflect.Name(name).IsValid() {
222		return filedesc.BaseL0{}, errors.New("descriptor %q has an invalid nested name: %q", parent.FullName(), name)
223	}
224
225	// Derive the full name of the child.
226	// Note that enum values are a sibling to the enum parent in the namespace.
227	var fullName protoreflect.FullName
228	if _, ok := parent.(protoreflect.EnumDescriptor); ok {
229		fullName = sb.AppendFullName(parent.FullName().Parent(), protoreflect.Name(name))
230	} else {
231		fullName = sb.AppendFullName(parent.FullName(), protoreflect.Name(name))
232	}
233	if _, ok := r[fullName]; ok {
234		return filedesc.BaseL0{}, errors.New("descriptor %q already declared", fullName)
235	}
236	r[fullName] = child
237
238	// TODO: Verify that the full name does not already exist in the resolver?
239	// This is not as critical since most usages of NewFile will register
240	// the created file back into the registry, which will perform this check.
241
242	return filedesc.BaseL0{
243		FullName:   fullName,
244		ParentFile: parent.ParentFile().(*filedesc.File),
245		Parent:     parent,
246		Index:      idx,
247	}, nil
248}
249