blob: 5cef6f5527d38ef9a296f0c49613cce8ec075618 [file] [log] [blame]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32/*
33 The code generator for the plugin for the Google protocol buffer compiler.
34 It generates Go code from the protocol buffer description files read by the
35 main routine.
36
37 Not supported yet:
38 services
39 options
40*/
41package generator
42
43import (
44 "bytes"
45 "fmt"
46 "log"
47 "os"
48 "strings"
49 "unicode"
50
51 "goprotobuf.googlecode.com/hg/proto"
52 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
53 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
54)
55
56// A Plugin provides functionality to add to the output during Go code generation,
57// such as to produce RPC stubs.
58type Plugin interface {
59 // Name identifies the plugin.
60 Name() string
61 // Generate produces the code generated by the plugin for this file, except for the imports,
62 // by calling the generator's methods P, In, and Out.
63 Generate(g *Generator, file *FileDescriptor)
64 // GenerateImports produces the import declarations for this file.
65 GenerateImports(g *Generator, file *FileDescriptor)
66}
67
68var plugins []Plugin
69
70// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
71// It is typically called during initialization.
72func RegisterPlugin(p Plugin) {
73 n := len(plugins)
74 if cap(plugins) == n {
75 nplugins := make([]Plugin, n, n+10) // very unlikely to need more than this
76 copy(nplugins, plugins)
77 plugins = nplugins
78 }
79 plugins = plugins[0:n+1]
80 plugins[n] = p
81 log.Stderr("installed plugin:", p.Name())
82}
83
84// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
85// a pointer to the FileDescriptorProto that represents it. These types achieve that
86// wrapping by placing each Proto inside a struct with the pointer to its File. The
87// structs have the same names as their contents, with "Proto" removed.
88// FileDescriptor is used to store the things that it points to.
89
90// The file and package name method are common to messages and enums.
91type common struct {
92 File *descriptor.FileDescriptorProto // File this object comes from.
93}
94
95// PackageName is name in the package clause in the generated file.
96func (c *common) PackageName() string { return uniquePackageOf(c.File) }
97
98// Descriptor represents a protocol buffer message.
99type Descriptor struct {
100 common
101 *descriptor.DescriptorProto
102 parent *Descriptor // The containing message, if any.
103 nested []*Descriptor // Inner messages, if any.
104 ext []*ExtensionDescriptor // Extensions, if any.
105 typename []string // Cached typename vector.
106}
107
108// TypeName returns the elements of the dotted type name.
109// The package name is not part of this name.
110func (d *Descriptor) TypeName() []string {
111 if d.typename != nil {
112 return d.typename
113 }
114 n := 0
115 for parent := d; parent != nil; parent = parent.parent {
116 n++
117 }
118 s := make([]string, n, n)
119 for parent := d; parent != nil; parent = parent.parent {
120 n--
121 s[n] = proto.GetString(parent.Name)
122 }
123 d.typename = s
124 return s
125}
126
127// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
128// Otherwise it will be the descriptor of the message in which it is defined.
129type EnumDescriptor struct {
130 common
131 *descriptor.EnumDescriptorProto
132 parent *Descriptor // The containing message, if any.
133 typename []string // Cached typename vector.
134}
135
136// TypeName returns the elements of the dotted type name.
137// The package name is not part of this name.
138func (e *EnumDescriptor) TypeName() (s []string) {
139 if e.typename != nil {
140 return e.typename
141 }
142 name := proto.GetString(e.Name)
143 if e.parent == nil {
144 s = make([]string, 1)
145 } else {
146 pname := e.parent.TypeName()
147 s = make([]string, len(pname)+1)
148 copy(s, pname)
149 }
150 s[len(s)-1] = name
151 e.typename = s
152 return s
153}
154
155// Everything but the last element of the full type name, CamelCased.
156// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
157func (e *EnumDescriptor) prefix() string {
158 typeName := e.TypeName()
159 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
160 if e.parent == nil {
161 // If the enum is not part of a message, the prefix is just the type name.
162 ccPrefix = CamelCase(*e.Name) + "_"
163 }
164 return ccPrefix
165}
166
167// The integer value of the named constant in this enumerated type.
168func (e *EnumDescriptor) integerValueAsString(name string) string {
169 for _, c := range e.Value {
170 if proto.GetString(c.Name) == name {
171 return fmt.Sprint(proto.GetInt32(c.Number))
172 }
173 }
174 log.Exit("cannot find value for enum constant")
175 return ""
176}
177
178// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
179// Otherwise it will be the descriptor of the message in which it is defined.
180type ExtensionDescriptor struct {
181 common
182 *descriptor.FieldDescriptorProto
183 parent *Descriptor // The containing message, if any.
184}
185
186// TypeName returns the elements of the dotted type name.
187// The package name is not part of this name.
188func (e *ExtensionDescriptor) TypeName() (s []string) {
189 name := proto.GetString(e.Name)
190 if e.parent == nil {
191 // top-level extension
192 s = make([]string, 1)
193 } else {
194 pname := e.parent.TypeName()
195 s = make([]string, len(pname)+1)
196 copy(s, pname)
197 }
198 s[len(s)-1] = name
199 return s
200}
201
202// FileDescriptor describes an protocol buffer descriptor file (.proto).
203// It includes slices of all the messages and enums defined within it.
204// Those slices are constructed by WrapTypes.
205type FileDescriptor struct {
206 *descriptor.FileDescriptorProto
207 desc []*Descriptor // All the messages defined in this file.
208 enum []*EnumDescriptor // All the enums defined in this file.
209 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
210}
211
212// PackageName is the package name we'll use in the generated code to refer to this file.
213func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
214
215// The package named defined in the input for this file, possibly dotted.
216func (d *FileDescriptor) originalPackageName() string {
217 return proto.GetString(d.Package)
218}
219
220// Whether the proto library needs importing.
221// This will be true if there are any enums, extensions, or messages with extension ranges.
222func (d *FileDescriptor) needProtoImport() bool {
223 if len(d.enum) > 0 || len(d.ext) > 0 {
224 return true
225 }
226 for _, desc := range d.desc {
227 if len(desc.ext) > 0 || len(desc.ExtensionRange) > 0 {
228 return true
229 }
230 }
231 return false
232}
233
234// Object is an interface abstracting the abilities shared by enums and messages.
235type Object interface {
236 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
237 TypeName() []string
238}
239
240// Each package name we generate must be unique. The package we're generating
241// gets its own name but every other package must have a unqiue name that does
242// not conflict in the code we generate. These names are chosen globally (although
243// they don't have to be, it simplifies things to do them globally).
244func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
245 s, ok := uniquePackageName[fd]
246 if !ok {
247 log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
248 }
249 return s
250}
251
252// Generator is the type whose methods generate the output, stored in the associated response structure.
253type Generator struct {
254 bytes.Buffer
255
256 Request *plugin.CodeGeneratorRequest // The input.
257 Response *plugin.CodeGeneratorResponse // The output.
258
259 packageName string // What we're calling ourselves.
260 allFiles []*FileDescriptor // All files in the tree
261 genFiles []*FileDescriptor // Those files we will generate output for.
262 file *FileDescriptor // The file we are compiling now.
263 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
264 indent string
265}
266
267// New creates a new generator and allocates the request and response protobufs.
268func New() *Generator {
269 g := new(Generator)
270 g.Request = plugin.NewCodeGeneratorRequest()
271 g.Response = plugin.NewCodeGeneratorResponse()
272 return g
273}
274
275// Error reports a problem, including an os.Error, and exits the program.
276func (g *Generator) Error(err os.Error, msgs ...string) {
277 s := strings.Join(msgs, " ") + ":" + err.String()
278 log.Stderr("protoc-gen-go: error: ", s)
279 g.Response.Error = proto.String(s)
280 os.Exit(1)
281}
282
283// Fail reports a problem and exits the program.
284func (g *Generator) Fail(msgs ...string) {
285 s := strings.Join(msgs, " ")
286 log.Stderr("protoc-gen-go: error: ", s)
287 g.Response.Error = proto.String(s)
288 os.Exit(1)
289}
290
291// DefaultPackageName returns the package name printed for the object.
292// If its file is in a different package, it returns the package name we're using for this file, plus ".".
293// Otherwise it returns the empty string.
294func (g *Generator) DefaultPackageName(obj Object) string {
295 pkg := obj.PackageName()
296 if pkg == g.packageName {
297 return ""
298 }
299 return pkg + "."
300}
301
302// For each input file, the unique package name to use, underscored.
303var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
304
305// SetPackageNames Sets the package name for this run.
306// The package name must agree across all files being generated.
307// It also defines unique package names for all imported files.
308func (g *Generator) SetPackageNames() {
309 inUse := make(map[string]bool)
310 pkg := proto.GetString(g.genFiles[0].Package)
311 g.packageName = strings.Map(DotToUnderscore, pkg)
312 inUse[pkg] = true
313 for _, f := range g.genFiles {
314 thisPkg := proto.GetString(f.Package)
315 if thisPkg != pkg {
316 g.Fail("inconsistent package names:", thisPkg, pkg)
317 }
318 }
319AllFiles:
320 for _, f := range g.allFiles {
321 for _, genf := range g.genFiles {
322 if f == genf {
323 // In this package already.
324 uniquePackageName[f.FileDescriptorProto] = g.packageName
325 continue AllFiles
326 }
327 }
328 truePkg := proto.GetString(f.Package)
329 pkg := truePkg
330 for {
331 _, present := inUse[pkg]
332 if present {
333 // It's a duplicate; must rename.
334 pkg += "X"
335 continue
336 }
337 break
338 }
339 // Install it.
340 inUse[pkg] = true
341 uniquePackageName[f.FileDescriptorProto] = strings.Map(DotToUnderscore, pkg)
342 }
343}
344
345// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
346// and FileDescriptorProtos into file-referenced objects within the Generator.
347// It also creates the list of files to generate and so should be called before GenerateAllFiles.
348func (g *Generator) WrapTypes() {
349 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
350 for i, f := range g.Request.ProtoFile {
351 pkg := proto.GetString(f.Package)
352 if pkg == "" {
353 g.Fail(proto.GetString(f.Name), "is missing a package declaration")
354 }
355 // We must wrap the descriptors before we wrap the enums
356 descs := wrapDescriptors(f)
357 g.buildNestedDescriptors(descs)
358 enums := wrapEnumDescriptors(f, descs)
359 exts := wrapExtensions(f)
360 g.allFiles[i] = &FileDescriptor{
361 FileDescriptorProto: f,
362 desc: descs,
363 enum: enums,
364 ext: exts,
365 }
366 }
367
368 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
369FindFiles:
370 for i, fileName := range g.Request.FileToGenerate {
371 // Search the list. This algorithm is n^2 but n is tiny.
372 for _, file := range g.allFiles {
373 if fileName == proto.GetString(file.Name) {
374 g.genFiles[i] = file
375 continue FindFiles
376 }
377 }
378 g.Fail("could not find file named", fileName)
379 }
380 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
381}
382
383// Scan the descriptors in this file. For each one, build the slice of nested descriptors
384func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
385 for _, desc := range descs {
386 if len(desc.NestedType) != 0 {
387 desc.nested = make([]*Descriptor, len(desc.NestedType))
388 n := 0
389 for _, nest := range descs {
390 if nest.parent == desc {
391 desc.nested[n] = nest
392 n++
393 }
394 }
395 if n != len(desc.NestedType) {
396 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
397 }
398 }
399 }
400}
401
402// Construct the Descriptor and add it to the slice
403func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
404 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
405
406 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
407 for i, field := range desc.Extension {
408 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
409 }
410
411 if len(sl) == cap(sl) {
412 nsl := make([]*Descriptor, len(sl), 2*len(sl))
413 copy(nsl, sl)
414 sl = nsl
415 }
416 sl = sl[0 : len(sl)+1]
417 sl[len(sl)-1] = d
418 return sl
419}
420
421// Return a slice of all the Descriptors defined within this file
422func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
423 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
424 for _, desc := range file.MessageType {
425 sl = wrapThisDescriptor(sl, desc, nil, file)
426 }
427 return sl
428}
429
430// Wrap this Descriptor, recursively
431func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
432 sl = addDescriptor(sl, desc, parent, file)
433 me := sl[len(sl)-1]
434 for _, nested := range desc.NestedType {
435 sl = wrapThisDescriptor(sl, nested, me, file)
436 }
437 return sl
438}
439
440// Construct the EnumDescriptor and add it to the slice
441func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
442 if len(sl) == cap(sl) {
443 nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
444 copy(nsl, sl)
445 sl = nsl
446 }
447 sl = sl[0 : len(sl)+1]
448 sl[len(sl)-1] = &EnumDescriptor{common{File: file}, desc, parent, nil}
449 return sl
450}
451
452// Return a slice of all the EnumDescriptors defined within this file
453func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
454 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
455 for _, enum := range file.EnumType {
456 sl = addEnumDescriptor(sl, enum, nil, file)
457 }
458 for _, nested := range descs {
459 sl = wrapEnumDescriptorsInMessage(sl, nested, file)
460 }
461 return sl
462}
463
464// Wrap this EnumDescriptor, recursively
465func wrapEnumDescriptorsInMessage(sl []*EnumDescriptor, desc *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
466 for _, enum := range desc.EnumType {
467 sl = addEnumDescriptor(sl, enum, desc, file)
468 }
469 for _, nested := range desc.nested {
470 sl = wrapEnumDescriptorsInMessage(sl, nested, file)
471 }
472 return sl
473}
474
475// Return a slice of all the top-level ExtensionDescriptors defined within this file.
476func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
477 sl := make([]*ExtensionDescriptor, len(file.Extension))
478 for i, field := range file.Extension {
479 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
480 }
481 return sl
482}
483
484// BuildTypeNameMap builds the map from fully qualified type names to objects.
485// The key names for the map come from the input data, which puts a period at the beginning.
486// It should be called after SetPackageNames and before GenerateAllFiles.
487func (g *Generator) BuildTypeNameMap() {
488 g.typeNameToObject = make(map[string]Object)
489 for _, f := range g.allFiles {
490 dottedPkg := "." + f.originalPackageName() + "."
491 for _, enum := range f.enum {
492 name := dottedPkg + dottedSlice(enum.TypeName())
493 g.typeNameToObject[name] = enum
494 }
495 for _, desc := range f.desc {
496 name := dottedPkg + dottedSlice(desc.TypeName())
497 g.typeNameToObject[name] = desc
498 }
499 }
500}
501
502// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
503// returns the descriptor for the message or enum with that name.
504func (g *Generator) ObjectNamed(typeName string) Object {
505 f, ok := g.typeNameToObject[typeName]
506 if !ok {
507 g.Fail("can't find object with type", typeName)
508 }
509 return f
510}
511
512// P prints the arguments to the generated output. It handles strings and int32s, plus
513// handling indirections because they may be *string, etc.
514func (g *Generator) P(str ...interface{}) {
515 g.WriteString(g.indent)
516 for _, v := range str {
517 switch s := v.(type) {
518 case string:
519 g.WriteString(s)
520 case *string:
521 g.WriteString(*s)
522 case *int32:
523 g.WriteString(fmt.Sprintf("%d", *s))
524 default:
525 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
526 }
527 }
528 g.WriteByte('\n')
529}
530
531// In Indents the output one tab stop.
532func (g *Generator) In() { g.indent += "\t" }
533
534// Out unindents the output one tab stop.
535func (g *Generator) Out() {
536 if len(g.indent) > 0 {
537 g.indent = g.indent[1:]
538 }
539}
540
541// GenerateAllFiles generates the output for all the files we're outputting.
542func (g *Generator) GenerateAllFiles() {
543 for i, file := range g.genFiles {
544 g.Reset()
545 g.generate(file)
546 g.runPlugins(file)
547 g.Response.File[i] = plugin.NewCodeGeneratorResponse_File()
548 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
549 g.Response.File[i].Content = proto.String(g.String())
550 }
551}
552
553// Run all the plugins associated with the file.
554func (g *Generator) runPlugins(file *FileDescriptor) {
555 for _, p := range plugins {
556 p.Generate(g, file)
557 }
558}
559
560
561// FileOf return the FileDescriptor for this FileDescriptorProto.
562func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
563 for _, file := range g.allFiles {
564 if file.FileDescriptorProto == fd {
565 return file
566 }
567 }
568 g.Fail("could not find file in table:", proto.GetString(fd.Name))
569 return nil
570}
571
572// Fill the response protocol buffer with the generated output for all the files we're
573// supposed to generate.
574func (g *Generator) generate(file *FileDescriptor) {
575 g.file = g.FileOf(file.FileDescriptorProto)
576 g.generateHeader()
577 g.generateImports()
578 for _, enum := range g.file.enum {
579 g.generateEnum(enum)
580 }
581 for _, desc := range g.file.desc {
582 g.generateMessage(desc)
583 }
584 for _, ext := range g.file.ext {
585 g.generateExtension(ext)
586 }
587 g.generateInitFunction()
588}
589
590// Generate the header, including package definition and imports
591func (g *Generator) generateHeader() {
592 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
593 g.P("// DO NOT EDIT!")
594 g.P()
595 g.P("package ", g.file.PackageName())
596 g.P()
597}
598
599// Generate the header, including package definition and imports
600func (g *Generator) generateImports() {
601 if g.file.needProtoImport() {
602 g.P(`import "goprotobuf.googlecode.com/hg/proto"`)
603 }
604 for _, s := range g.file.Dependency {
605 // Need to find the descriptor for this file
606 for _, fd := range g.allFiles {
607 if proto.GetString(fd.Name) == s {
608 filename := goFileName(s)
609 if strings.HasSuffix(filename, ".go") {
610 filename = filename[0:len(filename)-3]
611 }
612 g.P("import ", fd.PackageName(), " ", Quote(filename))
613 break
614 }
615 }
616 }
617 g.P()
618 // TODO: may need to worry about uniqueness across plugins
619 for _, p := range plugins {
620 p.GenerateImports(g, g.file)
621 g.P()
622 }
623}
624
625// Generate the enum definitions for this EnumDescriptor.
626func (g *Generator) generateEnum(enum *EnumDescriptor) {
627 // The full type name
628 typeName := enum.TypeName()
629 // The full type name, CamelCased.
630 ccTypeName := CamelCaseSlice(typeName)
631 ccPrefix := enum.prefix()
632 g.P("type ", ccTypeName, " int32")
633 g.P("const (")
634 g.In()
635 for _, e := range enum.Value {
636 g.P(ccPrefix+*e.Name, " = ", e.Number)
637 }
638 g.Out()
639 g.P(")")
640 g.P("var ", ccTypeName, "_name = map[int32] string {")
641 g.In()
642 generated := make(map[int32] bool) // avoid duplicate values
643 for _, e := range enum.Value {
644 duplicate := ""
645 if _, present := generated[*e.Number]; present {
646 duplicate = "// Duplicate value: "
647 }
648 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
649 generated[*e.Number] = true
650 }
651 g.Out()
652 g.P("}")
653 g.P("var ", ccTypeName, "_value = map[string] int32 {")
654 g.In()
655 for _, e := range enum.Value {
656 g.P(Quote(*e.Name), ": ", e.Number, ",")
657 }
658 g.Out()
659 g.P("}")
660 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
661 g.In()
662 g.P("e := ", ccTypeName, "(x)")
663 g.P("return &e")
664 g.Out()
665 g.P("}")
666 g.P()
667}
668
669// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
670// identifies details of the field for the protocol buffer marshaling and unmarshaling
671// code. The fields are:
672// wire encoding
673// protocol tag number
674// opt,req,rep for optional, required, or repeated
675// name= the original declared name
676// enum= the name of the enum type if it is an enum-typed field.
677// def= string representation of the default value, if any.
678// The default value must be in a representation that can be used at run-time
679// to generate the default value. Thus bools become 0 and 1, for instance.
680func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
681 optrepreq := ""
682 switch {
683 case isOptional(field):
684 optrepreq = "opt"
685 case isRequired(field):
686 optrepreq = "req"
687 case isRepeated(field):
688 optrepreq = "rep"
689 }
690 defaultValue := proto.GetString(field.DefaultValue)
691 if defaultValue != "" {
692 switch *field.Type {
693 case descriptor.FieldDescriptorProto_TYPE_BOOL:
694 if defaultValue == "true" {
695 defaultValue = "1"
696 } else {
697 defaultValue = "0"
698 }
699 case descriptor.FieldDescriptorProto_TYPE_STRING,
700 descriptor.FieldDescriptorProto_TYPE_BYTES:
701 // Protect frogs.
702 defaultValue = Quote(defaultValue)
703 // Don't need the quotes
704 defaultValue = defaultValue[1 : len(defaultValue)-1]
705 case descriptor.FieldDescriptorProto_TYPE_ENUM:
706 // For enums we need to provide the integer constant.
707 obj := g.ObjectNamed(proto.GetString(field.TypeName))
708 enum, ok := obj.(*EnumDescriptor)
709 if !ok {
710 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
711 }
712 defaultValue = enum.integerValueAsString(defaultValue)
713 }
714 defaultValue = ",def=" + defaultValue
715 }
716 enum := ""
717 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
718 obj := g.ObjectNamed(proto.GetString(field.TypeName))
719 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
720 }
721 name := proto.GetString(field.Name)
722 if name == CamelCase(name) {
723 name = ""
724 } else {
725 name = ",name=" + name
726 }
727 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
728 wiretype,
729 proto.GetInt32(field.Number),
730 optrepreq,
731 name,
732 enum,
733 defaultValue))
734}
735
736func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
737 switch typ {
738 case descriptor.FieldDescriptorProto_TYPE_GROUP:
739 return false
740 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
741 return false
742 case descriptor.FieldDescriptorProto_TYPE_BYTES:
743 return false
744 }
745 return true
746}
747
748// TypeName is the printed name appropriate for an item. If the object is in the current file,
749// TypeName drops the package name and underscores the rest.
750// Otherwise the object is from another package; and the result is the underscored
751// package name followed by the item name.
752// The result always has an initial capital.
753func (g *Generator) TypeName(obj Object) string {
754 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
755}
756
757// TypeNameWithPackage is like TypeName, but always includes the package
758// name even if the object is in our own package.
759func (g *Generator) TypeNameWithPackage(obj Object) string {
760 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
761}
762
763// GoType returns a string representing the type name, and the wire type
764func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
765 // TODO: Options.
766 switch *field.Type {
767 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
768 typ, wire = "float64", "fixed64"
769 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
770 typ, wire = "float32", "fixed32"
771 case descriptor.FieldDescriptorProto_TYPE_INT64:
772 typ, wire = "int64", "varint"
773 case descriptor.FieldDescriptorProto_TYPE_UINT64:
774 typ, wire = "uint64", "varint"
775 case descriptor.FieldDescriptorProto_TYPE_INT32:
776 typ, wire = "int32", "varint"
777 case descriptor.FieldDescriptorProto_TYPE_UINT32:
778 typ, wire = "uint32", "varint"
779 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
780 typ, wire = "uint64", "fixed64"
781 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
782 typ, wire = "uint32", "fixed32"
783 case descriptor.FieldDescriptorProto_TYPE_BOOL:
784 typ, wire = "bool", "varint"
785 case descriptor.FieldDescriptorProto_TYPE_STRING:
786 typ, wire = "string", "bytes"
787 case descriptor.FieldDescriptorProto_TYPE_GROUP:
788 desc := g.ObjectNamed(proto.GetString(field.TypeName))
789 typ, wire = "*"+g.TypeName(desc), "group"
790 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
791 desc := g.ObjectNamed(proto.GetString(field.TypeName))
792 typ, wire = "*"+g.TypeName(desc), "bytes"
793 case descriptor.FieldDescriptorProto_TYPE_BYTES:
794 typ, wire = "[]byte", "bytes"
795 case descriptor.FieldDescriptorProto_TYPE_ENUM:
796 desc := g.ObjectNamed(proto.GetString(field.TypeName))
797 typ, wire = g.TypeName(desc), "varint"
798 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
799 typ, wire = "int32", "fixed32"
800 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
801 typ, wire = "int64", "fixed64"
802 case descriptor.FieldDescriptorProto_TYPE_SINT32:
803 typ, wire = "int32", "zigzag32"
804 case descriptor.FieldDescriptorProto_TYPE_SINT64:
805 typ, wire = "int64", "zigzag64"
806 default:
807 g.Fail("unknown type for", proto.GetString(field.Name))
808 }
809 if isRepeated(field) {
810 typ = "[]" + typ
811 } else if needsStar(*field.Type) {
812 typ = "*" + typ
813 }
814 return
815}
816
817// Generate the type and default constant definitions for this Descriptor.
818func (g *Generator) generateMessage(message *Descriptor) {
819 // The full type name
820 typeName := message.TypeName()
821 // The full type name, CamelCased.
822 ccTypeName := CamelCaseSlice(typeName)
823
824 g.P("type ", ccTypeName, " struct {")
825 g.In()
826 for _, field := range message.Field {
827 fieldname := CamelCase(*field.Name)
828 typename, wiretype := g.GoType(message, field)
829 tag := g.goTag(field, wiretype)
830 g.P(fieldname, "\t", typename, "\t", tag)
831 }
832 if len(message.ExtensionRange) > 0 {
833 g.P("XXX_extensions\t\tmap[int32][]byte")
834 }
835 g.P("XXX_unrecognized\t[]byte")
836 g.Out()
837 g.P("}")
838
839 // Reset and New functions
840 g.P("func (this *", ccTypeName, ") Reset() {")
841 g.In()
842 g.P("*this = ", ccTypeName, "{}")
843 g.Out()
844 g.P("}")
845 g.P("func New", ccTypeName, "() *", ccTypeName, " {")
846 g.In()
847 g.P("return new(", ccTypeName, ")")
848 g.Out()
849 g.P("}")
850
851 // Extension support methods
852 if len(message.ExtensionRange) > 0 {
853 g.P()
854 g.P("var extRange_", ccTypeName, " = []proto.ExtensionRange{")
855 g.In()
856 for _, r := range message.ExtensionRange {
857 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
858 g.P("proto.ExtensionRange{", r.Start, ", ", end, "},")
859 }
860 g.Out()
861 g.P("}")
862 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []proto.ExtensionRange {")
863 g.In()
864 g.P("return extRange_", ccTypeName)
865 g.Out()
866 g.P("}")
867 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
868 g.In()
869 g.P("if this.XXX_extensions == nil {")
870 g.In()
871 g.P("this.XXX_extensions = make(map[int32][]byte)")
872 g.Out()
873 g.P("}")
874 g.P("return this.XXX_extensions")
875 g.Out()
876 g.P("}")
877 }
878
879 // Default constants
880 for _, field := range message.Field {
881 def := proto.GetString(field.DefaultValue)
882 if def == "" {
883 continue
884 }
885 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
886 typename, _ := g.GoType(message, field)
887 if typename[0] == '*' {
888 typename = typename[1:]
889 }
890 kind := "const "
891 switch {
892 case typename == "bool":
893 case typename == "string":
894 def = Quote(def)
895 case typename == "[]byte":
896 def = "[]byte(" + Quote(def) + ")"
897 kind = "var "
898 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
899 // Must be an enum. Need to construct the prefixed name.
900 obj := g.ObjectNamed(proto.GetString(field.TypeName))
901 enum, ok := obj.(*EnumDescriptor)
902 if !ok {
903 log.Stderr("don't know how to generate constant for", fieldname)
904 continue
905 }
906 def = enum.prefix() + def
907 }
908 g.P(kind, fieldname, " ", typename, " = ", def)
909 }
910 g.P()
911
912 for _, ext := range message.ext {
913 g.generateExtension(ext)
914 }
915}
916
917func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
918 // The full type name
919 typeName := ext.TypeName()
920 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
921 for i, s := range typeName {
922 typeName[i] = CamelCase(s)
923 }
924 ccTypeName := "E_" + strings.Join(typeName, "_")
925
926 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
927 field := ext.FieldDescriptorProto
928 fieldType, wireType := g.GoType(ext.parent, field)
929 tag := g.goTag(field, wireType)
930
931 g.P("var ", ccTypeName, " = &proto.ExtensionDesc{")
932 g.In()
933 g.P("ExtendedType: (", extendedType, ")(nil),")
934 g.P("ExtensionType: (", fieldType, ")(nil),")
935 g.P("Field: ", field.Number, ",")
936 g.P("Tag: ", tag, ",")
937
938 g.Out()
939 g.P("}")
940 g.P()
941}
942
943func (g *Generator) generateInitFunction() {
944 g.P("func init() {")
945 g.In()
946 for _, enum := range g.file.enum {
947 g.generateEnumRegistration(enum)
948 }
949 g.Out()
950 g.P("}")
951}
952
953func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
954 pkg := g.packageName + "." // We always print the full package name here.
955 // The full type name
956 typeName := enum.TypeName()
957 // The full type name, CamelCased.
958 ccTypeName := CamelCaseSlice(typeName)
959 g.P("proto.RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
960}
961
962// And now lots of helper functions.
963
964// CamelCase returns the CamelCased name. Given foo_bar_Baz, the result is FooBar_Baz.
965func CamelCase(name string) string {
966 elems := strings.Split(name, "_", 0)
967 for i, e := range elems {
968 if e == "" {
969 elems[i] = "_"
970 continue
971 }
972 runes := []int(e)
973 if unicode.IsLower(runes[0]) {
974 runes[0] = unicode.ToUpper(runes[0])
975 elems[i] = string(runes)
976 } else {
977 if i > 0 {
978 elems[i] = "_" + e
979 }
980 }
981 }
982 s := strings.Join(elems, "")
983 // Name must not begin with an underscore.
984 if len(s) > 0 && s[0] == '_' {
985 s = "X" + s[1:]
986 }
987 return s
988}
989
990// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
991// be joined with "_".
992func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
993
994// dottedSlice turns a sliced name into a dotted name.
995func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
996
997// Quote returns a Go-source quoted string representation of s.
998func Quote(s string) string { return fmt.Sprintf("%q", s) }
999
1000// Given a .proto file name, return the output name for the generated Go program.
1001func goFileName(name string) string {
1002 if strings.HasSuffix(name, ".proto") {
1003 name = name[0 : len(name)-6]
1004 }
1005 return name + ".pb.go"
1006}
1007
1008// Is this field optional?
1009func isOptional(field *descriptor.FieldDescriptorProto) bool {
1010 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1011}
1012
1013// Is this field required?
1014func isRequired(field *descriptor.FieldDescriptorProto) bool {
1015 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1016}
1017
1018// Is this field repeated?
1019func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1020 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1021}
1022
1023// DotToUnderscore is the mapping function used to generate Go names from package names,
1024// which can be dotted in the input .proto file.
1025func DotToUnderscore(rune int) int {
1026 if rune == '.' {
1027 return '_'
1028 }
1029 return rune
1030}