blob: 884fef1f1fe7390420c2a61b77435f084372dcae [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.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070036*/
37package generator
38
39import (
40 "bytes"
41 "fmt"
David Symondsb1d55a02011-04-08 09:55:06 +100042 "go/parser"
43 "go/printer"
44 "go/token"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070045 "log"
46 "os"
Rob Pike87af39e2010-07-19 10:48:02 -070047 "path"
David Symonds79eae332010-10-16 11:33:20 +110048 "strconv"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070049 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070050
51 "goprotobuf.googlecode.com/hg/proto"
David Symonds9f402812011-04-28 18:08:44 +100052 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070053 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.
Rob Pikec9e7d972010-06-10 10:30:22 -070060 Name() string
61 // Init is called once after data structures are built but before
62 // code generation begins.
63 Init(g *Generator)
64 // Generate produces the code generated by the plugin for this file,
65 // except for the imports, by calling the generator's methods P, In, and Out.
66 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070067 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070068 // It is called after Generate.
69 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070070}
71
72var plugins []Plugin
73
74// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
75// It is typically called during initialization.
76func RegisterPlugin(p Plugin) {
David Symondscc7142e2010-11-06 14:37:15 +110077 plugins = append(plugins, p)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070078}
79
80// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
81// a pointer to the FileDescriptorProto that represents it. These types achieve that
82// wrapping by placing each Proto inside a struct with the pointer to its File. The
83// structs have the same names as their contents, with "Proto" removed.
84// FileDescriptor is used to store the things that it points to.
85
86// The file and package name method are common to messages and enums.
87type common struct {
88 File *descriptor.FileDescriptorProto // File this object comes from.
89}
90
91// PackageName is name in the package clause in the generated file.
92func (c *common) PackageName() string { return uniquePackageOf(c.File) }
93
94// Descriptor represents a protocol buffer message.
95type Descriptor struct {
96 common
97 *descriptor.DescriptorProto
98 parent *Descriptor // The containing message, if any.
99 nested []*Descriptor // Inner messages, if any.
100 ext []*ExtensionDescriptor // Extensions, if any.
101 typename []string // Cached typename vector.
102}
103
104// TypeName returns the elements of the dotted type name.
105// The package name is not part of this name.
106func (d *Descriptor) TypeName() []string {
107 if d.typename != nil {
108 return d.typename
109 }
110 n := 0
111 for parent := d; parent != nil; parent = parent.parent {
112 n++
113 }
114 s := make([]string, n, n)
115 for parent := d; parent != nil; parent = parent.parent {
116 n--
117 s[n] = proto.GetString(parent.Name)
118 }
119 d.typename = s
120 return s
121}
122
123// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
124// Otherwise it will be the descriptor of the message in which it is defined.
125type EnumDescriptor struct {
126 common
127 *descriptor.EnumDescriptorProto
128 parent *Descriptor // The containing message, if any.
129 typename []string // Cached typename vector.
130}
131
132// TypeName returns the elements of the dotted type name.
133// The package name is not part of this name.
134func (e *EnumDescriptor) TypeName() (s []string) {
135 if e.typename != nil {
136 return e.typename
137 }
138 name := proto.GetString(e.Name)
139 if e.parent == nil {
140 s = make([]string, 1)
141 } else {
142 pname := e.parent.TypeName()
143 s = make([]string, len(pname)+1)
144 copy(s, pname)
145 }
146 s[len(s)-1] = name
147 e.typename = s
148 return s
149}
150
151// Everything but the last element of the full type name, CamelCased.
152// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
153func (e *EnumDescriptor) prefix() string {
154 typeName := e.TypeName()
155 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
156 if e.parent == nil {
157 // If the enum is not part of a message, the prefix is just the type name.
158 ccPrefix = CamelCase(*e.Name) + "_"
159 }
160 return ccPrefix
161}
162
163// The integer value of the named constant in this enumerated type.
164func (e *EnumDescriptor) integerValueAsString(name string) string {
165 for _, c := range e.Value {
166 if proto.GetString(c.Name) == name {
167 return fmt.Sprint(proto.GetInt32(c.Number))
168 }
169 }
David Symonds9d0000e2011-02-03 10:48:14 +1100170 log.Fatal("cannot find value for enum constant")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700171 return ""
172}
173
174// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
175// Otherwise it will be the descriptor of the message in which it is defined.
176type ExtensionDescriptor struct {
177 common
178 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700179 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700180}
181
182// TypeName returns the elements of the dotted type name.
183// The package name is not part of this name.
184func (e *ExtensionDescriptor) TypeName() (s []string) {
185 name := proto.GetString(e.Name)
186 if e.parent == nil {
187 // top-level extension
188 s = make([]string, 1)
189 } else {
190 pname := e.parent.TypeName()
191 s = make([]string, len(pname)+1)
192 copy(s, pname)
193 }
194 s[len(s)-1] = name
195 return s
196}
197
David Symondse37856c2011-06-22 12:52:53 +1000198// DescName returns the variable name used for the generated descriptor.
199func (e *ExtensionDescriptor) DescName() string {
200 // The full type name.
201 typeName := e.TypeName()
202 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
203 for i, s := range typeName {
204 typeName[i] = CamelCase(s)
205 }
206 return "E_" + strings.Join(typeName, "_")
207}
208
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700209// FileDescriptor describes an protocol buffer descriptor file (.proto).
210// It includes slices of all the messages and enums defined within it.
211// Those slices are constructed by WrapTypes.
212type FileDescriptor struct {
213 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700214 desc []*Descriptor // All the messages defined in this file.
215 enum []*EnumDescriptor // All the enums defined in this file.
216 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds31d58a22011-01-20 18:33:21 +1100217
218 // The full list of symbols that are exported.
219 // This is used for supporting public imports.
220 exported []Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700221}
222
223// PackageName is the package name we'll use in the generated code to refer to this file.
224func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
225
226// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700227// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700228func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700229 // Does the file have a package clause?
David Symonds7d5c8242011-03-14 12:03:50 -0700230 if pkg := proto.GetString(d.Package); pkg != "" {
Rob Pikec9e7d972010-06-10 10:30:22 -0700231 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700232 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700233 // Use the file base name.
234 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700235}
236
David Symonds31d58a22011-01-20 18:33:21 +1100237func (d *FileDescriptor) addExport(symbol Symbol) {
238 d.exported = append(d.exported, symbol)
239}
240
241// Symbol is an interface representing an exported Go symbol.
242type Symbol interface {
243 // GenerateAlias should generate an appropriate alias
244 // for the symbol from the named package.
245 GenerateAlias(g *Generator, pkg string)
246}
247
248type messageSymbol struct {
249 sym string
250 hasExtensions, isMessageSet bool
251}
252
253func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
254 remoteSym := pkg + "." + ms.sym
255
256 g.P("type ", ms.sym, " ", remoteSym)
257 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
David Symondse37856c2011-06-22 12:52:53 +1000258 g.P("func (this *", ms.sym, ") String() string { return (*", remoteSym, ")(this).String() }")
David Symonds31d58a22011-01-20 18:33:21 +1100259 if ms.hasExtensions {
260 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
261 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
262 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32][]byte ",
263 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
264 if ms.isMessageSet {
265 g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
266 "{ return (*", remoteSym, ")(this).Marshal() }")
267 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
268 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
269 }
270 }
271}
272
273type enumSymbol string
274
275func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
276 s := string(es)
277 g.P("type ", s, " ", pkg, ".", s)
278 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
279 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
280 g.P("func New", s, "(x int32) *", s, " { e := ", s, "(x); return &e }")
281}
282
283type constOrVarSymbol struct {
284 sym string
285 typ string // either "const" or "var"
286}
287
288func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
289 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
290}
291
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700292// Object is an interface abstracting the abilities shared by enums and messages.
293type Object interface {
294 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
295 TypeName() []string
296}
297
298// Each package name we generate must be unique. The package we're generating
299// gets its own name but every other package must have a unqiue name that does
300// not conflict in the code we generate. These names are chosen globally (although
301// they don't have to be, it simplifies things to do them globally).
302func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
303 s, ok := uniquePackageName[fd]
304 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100305 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700306 }
307 return s
308}
309
310// Generator is the type whose methods generate the output, stored in the associated response structure.
311type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000312 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700313
314 Request *plugin.CodeGeneratorRequest // The input.
315 Response *plugin.CodeGeneratorResponse // The output.
316
Rob Pikec9e7d972010-06-10 10:30:22 -0700317 Param map[string]string // Command-line parameters.
318 ImportPrefix string // String to prefix to imported package file names.
319 ImportMap map[string]string // Mapping from import name to generated name
320
321 ProtoPkg string // The name under which we import the library's package proto.
322
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700323 packageName string // What we're calling ourselves.
324 allFiles []*FileDescriptor // All files in the tree
325 genFiles []*FileDescriptor // Those files we will generate output for.
326 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000327 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700328 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
329 indent string
330}
331
332// New creates a new generator and allocates the request and response protobufs.
333func New() *Generator {
334 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000335 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100336 g.Request = new(plugin.CodeGeneratorRequest)
337 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700338 return g
339}
340
341// Error reports a problem, including an os.Error, and exits the program.
342func (g *Generator) Error(err os.Error, msgs ...string) {
343 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700344 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700345 g.Response.Error = proto.String(s)
346 os.Exit(1)
347}
348
349// Fail reports a problem and exits the program.
350func (g *Generator) Fail(msgs ...string) {
351 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700352 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700353 g.Response.Error = proto.String(s)
354 os.Exit(1)
355}
356
Rob Pikec9e7d972010-06-10 10:30:22 -0700357// CommandLineParameters breaks the comma-separated list of key=value pairs
358// in the parameter (a member of the request protobuf) into a key/value map.
359// It then sets file name mappings defined by those entries.
360func (g *Generator) CommandLineParameters(parameter string) {
361 g.Param = make(map[string]string)
Rob Pike53385442010-06-30 22:22:43 -0700362 for _, p := range strings.Split(parameter, ",", -1) {
Rob Pikec9e7d972010-06-10 10:30:22 -0700363 if i := strings.Index(p, "="); i < 0 {
364 g.Param[p] = ""
365 } else {
366 g.Param[p[0:i]] = p[i+1:]
367 }
368 }
369
370 g.ImportMap = make(map[string]string)
371 for k, v := range g.Param {
372 if k == "import_prefix" {
373 g.ImportPrefix = v
374 } else if len(k) > 0 && k[0] == 'M' {
375 g.ImportMap[k[1:]] = v
376 }
377 }
378}
379
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700380// DefaultPackageName returns the package name printed for the object.
381// If its file is in a different package, it returns the package name we're using for this file, plus ".".
382// Otherwise it returns the empty string.
383func (g *Generator) DefaultPackageName(obj Object) string {
384 pkg := obj.PackageName()
385 if pkg == g.packageName {
386 return ""
387 }
388 return pkg + "."
389}
390
391// For each input file, the unique package name to use, underscored.
392var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700393// Package names already registered. Key is the name from the .proto file;
394// value is the name that appears in the generated code.
395var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700396
Rob Pikec9e7d972010-06-10 10:30:22 -0700397// Create and remember a guaranteed unique package name for this file descriptor.
398// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
399// has no file descriptor.
400func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds79eae332010-10-16 11:33:20 +1100401 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700402 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100403 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700404 }
405 // Install it.
406 pkgNamesInUse[pkg] = true
407 pkg = strings.Map(DotToUnderscore, pkg)
408 if f != nil {
409 uniquePackageName[f.FileDescriptorProto] = pkg
410 }
411 return pkg
412}
413
414// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700415// The package name must agree across all files being generated.
416// It also defines unique package names for all imported files.
417func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700418 // Register the name for this package. It will be the first name
419 // registered so is guaranteed to be unmodified.
420 pkg := g.genFiles[0].originalPackageName()
421 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
422 // Register the proto package name. It might collide with the
423 // name of a package we import.
424 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
David Symonds7d5c8242011-03-14 12:03:50 -0700425 // Verify that we are generating output for a single package.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700426 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700427 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700428 if thisPkg != pkg {
429 g.Fail("inconsistent package names:", thisPkg, pkg)
430 }
431 }
432AllFiles:
433 for _, f := range g.allFiles {
434 for _, genf := range g.genFiles {
435 if f == genf {
436 // In this package already.
437 uniquePackageName[f.FileDescriptorProto] = g.packageName
438 continue AllFiles
439 }
440 }
David Symonds7d5c8242011-03-14 12:03:50 -0700441 // The file is a dependency, so we want to ignore its go_package option
442 // because that is only relevant for its specific generated output.
443 pkg := proto.GetString(f.Package)
444 if pkg == "" {
445 pkg = BaseName(*f.Name)
446 }
447 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700448 }
449}
450
451// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
452// and FileDescriptorProtos into file-referenced objects within the Generator.
453// It also creates the list of files to generate and so should be called before GenerateAllFiles.
454func (g *Generator) WrapTypes() {
455 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
456 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700457 // We must wrap the descriptors before we wrap the enums
458 descs := wrapDescriptors(f)
459 g.buildNestedDescriptors(descs)
460 enums := wrapEnumDescriptors(f, descs)
461 exts := wrapExtensions(f)
462 g.allFiles[i] = &FileDescriptor{
463 FileDescriptorProto: f,
464 desc: descs,
465 enum: enums,
466 ext: exts,
467 }
468 }
469
470 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
471FindFiles:
472 for i, fileName := range g.Request.FileToGenerate {
473 // Search the list. This algorithm is n^2 but n is tiny.
474 for _, file := range g.allFiles {
475 if fileName == proto.GetString(file.Name) {
476 g.genFiles[i] = file
477 continue FindFiles
478 }
479 }
480 g.Fail("could not find file named", fileName)
481 }
482 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
483}
484
485// Scan the descriptors in this file. For each one, build the slice of nested descriptors
486func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
487 for _, desc := range descs {
488 if len(desc.NestedType) != 0 {
489 desc.nested = make([]*Descriptor, len(desc.NestedType))
490 n := 0
491 for _, nest := range descs {
492 if nest.parent == desc {
493 desc.nested[n] = nest
494 n++
495 }
496 }
497 if n != len(desc.NestedType) {
498 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
499 }
500 }
501 }
502}
503
504// Construct the Descriptor and add it to the slice
505func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
506 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
507
508 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
509 for i, field := range desc.Extension {
510 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
511 }
512
David Symondscc7142e2010-11-06 14:37:15 +1100513 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700514}
515
516// Return a slice of all the Descriptors defined within this file
517func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
518 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
519 for _, desc := range file.MessageType {
520 sl = wrapThisDescriptor(sl, desc, nil, file)
521 }
522 return sl
523}
524
525// Wrap this Descriptor, recursively
526func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
527 sl = addDescriptor(sl, desc, parent, file)
528 me := sl[len(sl)-1]
529 for _, nested := range desc.NestedType {
530 sl = wrapThisDescriptor(sl, nested, me, file)
531 }
532 return sl
533}
534
535// Construct the EnumDescriptor and add it to the slice
536func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symondscc7142e2010-11-06 14:37:15 +1100537 return append(sl, &EnumDescriptor{common{File: file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700538}
539
540// Return a slice of all the EnumDescriptors defined within this file
541func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
542 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000543 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700544 for _, enum := range file.EnumType {
545 sl = addEnumDescriptor(sl, enum, nil, file)
546 }
David Symonds5256cf62010-06-27 10:33:42 +1000547 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700548 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000549 for _, enum := range nested.EnumType {
550 sl = addEnumDescriptor(sl, enum, nested, file)
551 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700552 }
553 return sl
554}
555
556// Return a slice of all the top-level ExtensionDescriptors defined within this file.
557func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
558 sl := make([]*ExtensionDescriptor, len(file.Extension))
559 for i, field := range file.Extension {
560 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
561 }
562 return sl
563}
564
Rob Pikec9e7d972010-06-10 10:30:22 -0700565// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700566// The key names for the map come from the input data, which puts a period at the beginning.
567// It should be called after SetPackageNames and before GenerateAllFiles.
568func (g *Generator) BuildTypeNameMap() {
569 g.typeNameToObject = make(map[string]Object)
570 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700571 // The names in this loop are defined by the proto world, not us, so the
572 // package name may be empty. If so, the dotted package name of X will
573 // be ".X"; otherwise it will be ".pkg.X".
574 dottedPkg := "." + proto.GetString(f.Package)
575 if dottedPkg != "." {
576 dottedPkg += "."
577 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700578 for _, enum := range f.enum {
579 name := dottedPkg + dottedSlice(enum.TypeName())
580 g.typeNameToObject[name] = enum
581 }
582 for _, desc := range f.desc {
583 name := dottedPkg + dottedSlice(desc.TypeName())
584 g.typeNameToObject[name] = desc
585 }
586 }
587}
588
589// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
590// returns the descriptor for the message or enum with that name.
591func (g *Generator) ObjectNamed(typeName string) Object {
592 f, ok := g.typeNameToObject[typeName]
593 if !ok {
594 g.Fail("can't find object with type", typeName)
595 }
596 return f
597}
598
599// P prints the arguments to the generated output. It handles strings and int32s, plus
600// handling indirections because they may be *string, etc.
601func (g *Generator) P(str ...interface{}) {
602 g.WriteString(g.indent)
603 for _, v := range str {
604 switch s := v.(type) {
605 case string:
606 g.WriteString(s)
607 case *string:
608 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700609 case bool:
610 g.WriteString(fmt.Sprintf("%t", s))
611 case *bool:
612 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700613 case *int32:
614 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700615 case float64:
616 g.WriteString(fmt.Sprintf("%g", s))
617 case *float64:
618 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700619 default:
620 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
621 }
622 }
623 g.WriteByte('\n')
624}
625
626// In Indents the output one tab stop.
627func (g *Generator) In() { g.indent += "\t" }
628
629// Out unindents the output one tab stop.
630func (g *Generator) Out() {
631 if len(g.indent) > 0 {
632 g.indent = g.indent[1:]
633 }
634}
635
636// GenerateAllFiles generates the output for all the files we're outputting.
637func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700638 // Initialize the plugins
639 for _, p := range plugins {
640 p.Init(g)
641 }
David Symonds31d58a22011-01-20 18:33:21 +1100642 // Generate the output. The generator runs for every file, even the files
643 // that we don't generate output for, so that we can collate the full list
644 // of exported symbols to support public imports.
645 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
646 for _, file := range g.genFiles {
647 genFileMap[file] = true
648 }
649 i := 0
650 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700651 g.Reset()
652 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100653 if _, ok := genFileMap[file]; !ok {
654 continue
655 }
David Symondsb0127532010-11-09 11:10:46 +1100656 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700657 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
658 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100659 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700660 }
661}
662
663// Run all the plugins associated with the file.
664func (g *Generator) runPlugins(file *FileDescriptor) {
665 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700666 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700667 }
668}
669
670
671// FileOf return the FileDescriptor for this FileDescriptorProto.
672func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
673 for _, file := range g.allFiles {
674 if file.FileDescriptorProto == fd {
675 return file
676 }
677 }
678 g.Fail("could not find file in table:", proto.GetString(fd.Name))
679 return nil
680}
681
682// Fill the response protocol buffer with the generated output for all the files we're
683// supposed to generate.
684func (g *Generator) generate(file *FileDescriptor) {
685 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000686 g.usedPackages = make(map[string]bool)
687
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700688 for _, enum := range g.file.enum {
689 g.generateEnum(enum)
690 }
691 for _, desc := range g.file.desc {
692 g.generateMessage(desc)
693 }
694 for _, ext := range g.file.ext {
695 g.generateExtension(ext)
696 }
697 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000698
Rob Pikec9e7d972010-06-10 10:30:22 -0700699 // Run the plugins before the imports so we know which imports are necessary.
700 g.runPlugins(file)
701
David Symondsf90e3382010-05-05 10:53:44 +1000702 // Generate header and imports last, though they appear first in the output.
703 rem := g.Buffer
704 g.Buffer = new(bytes.Buffer)
705 g.generateHeader()
706 g.generateImports()
707 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +1000708
709 // Reformat generated code.
710 fset := token.NewFileSet()
711 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
712 if err != nil {
713 g.Fail("bad Go source code was generated:", err.String())
714 return
715 }
716 g.Reset()
717 _, err = (&printer.Config{printer.TabIndent, 8}).Fprint(g, fset, ast)
718 if err != nil {
719 g.Fail("generated Go source code could not be reformatted:", err.String())
720 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700721}
722
723// Generate the header, including package definition and imports
724func (g *Generator) generateHeader() {
725 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
726 g.P("// DO NOT EDIT!")
727 g.P()
728 g.P("package ", g.file.PackageName())
729 g.P()
730}
731
David Symonds31d58a22011-01-20 18:33:21 +1100732func (g *Generator) fileByName(filename string) *FileDescriptor {
733 for _, fd := range g.allFiles {
734 if proto.GetString(fd.Name) == filename {
735 return fd
736 }
737 }
738 return nil
739}
740
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700741// Generate the header, including package definition and imports
742func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700743 // We almost always need a proto import. Rather than computing when we
744 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100745 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700746 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100747 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100748 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700749 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100750 fd := g.fileByName(s)
751 // Do not import our own package.
752 if fd.PackageName() == g.packageName {
753 continue
754 }
755 filename := goFileName(s)
756 if substitution, ok := g.ImportMap[s]; ok {
757 filename = substitution
758 }
759 filename = g.ImportPrefix + filename
760 if strings.HasSuffix(filename, ".go") {
761 filename = filename[0 : len(filename)-3]
762 }
763 if _, ok := g.usedPackages[fd.PackageName()]; ok {
764 g.P("import ", fd.PackageName(), " ", Quote(filename))
765 } else {
David Symonds3fa055f2011-05-05 15:19:04 -0700766 // TODO: Re-enable this when we are more feature-complete.
767 // For instance, some protos use foreign field extensions, which we don't support.
768 // Until then, this is just annoying spam.
769 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700770 }
771 }
772 g.P()
773 // TODO: may need to worry about uniqueness across plugins
774 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700775 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700776 g.P()
777 }
David Symondscea785b2011-01-07 11:02:30 +1100778 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700779 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100780 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100781 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700782 g.P()
David Symonds31d58a22011-01-20 18:33:21 +1100783
784 // Symbols from public imports.
785 for _, index := range g.file.PublicDependency {
786 fd := g.fileByName(g.file.Dependency[index])
787 g.P("// Types from public import ", *fd.Name)
788 for _, sym := range fd.exported {
789 sym.GenerateAlias(g, fd.PackageName())
790 }
791 }
792 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700793}
794
795// Generate the enum definitions for this EnumDescriptor.
796func (g *Generator) generateEnum(enum *EnumDescriptor) {
797 // The full type name
798 typeName := enum.TypeName()
799 // The full type name, CamelCased.
800 ccTypeName := CamelCaseSlice(typeName)
801 ccPrefix := enum.prefix()
802 g.P("type ", ccTypeName, " int32")
David Symonds31d58a22011-01-20 18:33:21 +1100803 g.file.addExport(enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700804 g.P("const (")
805 g.In()
806 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100807 name := ccPrefix + *e.Name
808 g.P(name, " = ", e.Number)
809 g.file.addExport(constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700810 }
811 g.Out()
812 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100813 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700814 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700815 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700816 for _, e := range enum.Value {
817 duplicate := ""
818 if _, present := generated[*e.Number]; present {
819 duplicate = "// Duplicate value: "
820 }
821 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
822 generated[*e.Number] = true
823 }
824 g.Out()
825 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100826 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700827 g.In()
828 for _, e := range enum.Value {
829 g.P(Quote(*e.Name), ": ", e.Number, ",")
830 }
831 g.Out()
832 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100833
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700834 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
835 g.In()
836 g.P("e := ", ccTypeName, "(x)")
837 g.P("return &e")
838 g.Out()
839 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100840
841 g.P("func (x ", ccTypeName, ") String() string {")
842 g.In()
843 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
844 g.Out()
845 g.P("}")
846
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700847 g.P()
848}
849
850// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
851// identifies details of the field for the protocol buffer marshaling and unmarshaling
852// code. The fields are:
853// wire encoding
854// protocol tag number
855// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100856// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700857// name= the original declared name
858// enum= the name of the enum type if it is an enum-typed field.
859// def= string representation of the default value, if any.
860// The default value must be in a representation that can be used at run-time
861// to generate the default value. Thus bools become 0 and 1, for instance.
862func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
863 optrepreq := ""
864 switch {
865 case isOptional(field):
866 optrepreq = "opt"
867 case isRequired(field):
868 optrepreq = "req"
869 case isRepeated(field):
870 optrepreq = "rep"
871 }
872 defaultValue := proto.GetString(field.DefaultValue)
873 if defaultValue != "" {
874 switch *field.Type {
875 case descriptor.FieldDescriptorProto_TYPE_BOOL:
876 if defaultValue == "true" {
877 defaultValue = "1"
878 } else {
879 defaultValue = "0"
880 }
881 case descriptor.FieldDescriptorProto_TYPE_STRING,
882 descriptor.FieldDescriptorProto_TYPE_BYTES:
883 // Protect frogs.
884 defaultValue = Quote(defaultValue)
885 // Don't need the quotes
886 defaultValue = defaultValue[1 : len(defaultValue)-1]
887 case descriptor.FieldDescriptorProto_TYPE_ENUM:
888 // For enums we need to provide the integer constant.
889 obj := g.ObjectNamed(proto.GetString(field.TypeName))
890 enum, ok := obj.(*EnumDescriptor)
891 if !ok {
892 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
893 }
894 defaultValue = enum.integerValueAsString(defaultValue)
895 }
896 defaultValue = ",def=" + defaultValue
897 }
898 enum := ""
899 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
900 obj := g.ObjectNamed(proto.GetString(field.TypeName))
901 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
902 }
David Symonds5b7775e2010-12-01 10:09:04 +1100903 packed := ""
904 if field.Options != nil && proto.GetBool(field.Options.Packed) {
905 packed = ",packed"
906 }
David Symondse37856c2011-06-22 12:52:53 +1000907 fieldName := proto.GetString(field.Name)
908 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +1000909 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
910 // We must use the type name for groups instead of
911 // the field name to preserve capitalization.
912 // type_name in FieldDescriptorProto is fully-qualified,
913 // but we only want the local part.
914 name = *field.TypeName
915 if i := strings.LastIndex(name, "."); i >= 0 {
916 name = name[i+1:]
917 }
David Symondse37856c2011-06-22 12:52:53 +1000918 }
919 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700920 name = ""
921 } else {
922 name = ",name=" + name
923 }
David Symonds5b7775e2010-12-01 10:09:04 +1100924 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s%s)",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700925 wiretype,
926 proto.GetInt32(field.Number),
927 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100928 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700929 name,
930 enum,
931 defaultValue))
932}
933
934func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
935 switch typ {
936 case descriptor.FieldDescriptorProto_TYPE_GROUP:
937 return false
938 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
939 return false
940 case descriptor.FieldDescriptorProto_TYPE_BYTES:
941 return false
942 }
943 return true
944}
945
946// TypeName is the printed name appropriate for an item. If the object is in the current file,
947// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -0700948// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700949// package name followed by the item name.
950// The result always has an initial capital.
951func (g *Generator) TypeName(obj Object) string {
952 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
953}
954
955// TypeNameWithPackage is like TypeName, but always includes the package
956// name even if the object is in our own package.
957func (g *Generator) TypeNameWithPackage(obj Object) string {
958 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
959}
960
961// GoType returns a string representing the type name, and the wire type
962func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
963 // TODO: Options.
964 switch *field.Type {
965 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
966 typ, wire = "float64", "fixed64"
967 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
968 typ, wire = "float32", "fixed32"
969 case descriptor.FieldDescriptorProto_TYPE_INT64:
970 typ, wire = "int64", "varint"
971 case descriptor.FieldDescriptorProto_TYPE_UINT64:
972 typ, wire = "uint64", "varint"
973 case descriptor.FieldDescriptorProto_TYPE_INT32:
974 typ, wire = "int32", "varint"
975 case descriptor.FieldDescriptorProto_TYPE_UINT32:
976 typ, wire = "uint32", "varint"
977 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
978 typ, wire = "uint64", "fixed64"
979 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
980 typ, wire = "uint32", "fixed32"
981 case descriptor.FieldDescriptorProto_TYPE_BOOL:
982 typ, wire = "bool", "varint"
983 case descriptor.FieldDescriptorProto_TYPE_STRING:
984 typ, wire = "string", "bytes"
985 case descriptor.FieldDescriptorProto_TYPE_GROUP:
986 desc := g.ObjectNamed(proto.GetString(field.TypeName))
987 typ, wire = "*"+g.TypeName(desc), "group"
988 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
989 desc := g.ObjectNamed(proto.GetString(field.TypeName))
990 typ, wire = "*"+g.TypeName(desc), "bytes"
991 case descriptor.FieldDescriptorProto_TYPE_BYTES:
992 typ, wire = "[]byte", "bytes"
993 case descriptor.FieldDescriptorProto_TYPE_ENUM:
994 desc := g.ObjectNamed(proto.GetString(field.TypeName))
995 typ, wire = g.TypeName(desc), "varint"
996 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
997 typ, wire = "int32", "fixed32"
998 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
999 typ, wire = "int64", "fixed64"
1000 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1001 typ, wire = "int32", "zigzag32"
1002 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1003 typ, wire = "int64", "zigzag64"
1004 default:
1005 g.Fail("unknown type for", proto.GetString(field.Name))
1006 }
1007 if isRepeated(field) {
1008 typ = "[]" + typ
1009 } else if needsStar(*field.Type) {
1010 typ = "*" + typ
1011 }
1012 return
1013}
1014
David Symondsf90e3382010-05-05 10:53:44 +10001015func (g *Generator) RecordTypeUse(t string) {
1016 if obj, ok := g.typeNameToObject[t]; ok {
1017 g.usedPackages[obj.PackageName()] = true
1018 }
1019}
1020
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001021// Generate the type and default constant definitions for this Descriptor.
1022func (g *Generator) generateMessage(message *Descriptor) {
1023 // The full type name
1024 typeName := message.TypeName()
1025 // The full type name, CamelCased.
1026 ccTypeName := CamelCaseSlice(typeName)
1027
1028 g.P("type ", ccTypeName, " struct {")
1029 g.In()
1030 for _, field := range message.Field {
1031 fieldname := CamelCase(*field.Name)
1032 typename, wiretype := g.GoType(message, field)
1033 tag := g.goTag(field, wiretype)
1034 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001035 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001036 }
1037 if len(message.ExtensionRange) > 0 {
1038 g.P("XXX_extensions\t\tmap[int32][]byte")
1039 }
1040 g.P("XXX_unrecognized\t[]byte")
1041 g.Out()
1042 g.P("}")
1043
David Symondse37856c2011-06-22 12:52:53 +10001044 // Reset and String functions
1045 g.P("func (this *", ccTypeName, ") Reset() { *this = ", ccTypeName, "{} }")
1046 g.P("func (this *", ccTypeName, ") String() string { return ", g.ProtoPkg, ".CompactTextString(this) }")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001047
1048 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001049 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001050 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001051 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001052 // message_set_wire_format only makes sense when extensions are defined.
1053 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001054 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001055 g.P()
1056 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1057 g.In()
1058 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1059 g.Out()
1060 g.P("}")
1061 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1062 g.In()
1063 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1064 g.Out()
1065 g.P("}")
1066 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1067 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1068 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1069 }
1070
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001071 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001072 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001073 g.In()
1074 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001075 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1076 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001077 }
1078 g.Out()
1079 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001080 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001081 g.In()
1082 g.P("return extRange_", ccTypeName)
1083 g.Out()
1084 g.P("}")
1085 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1086 g.In()
1087 g.P("if this.XXX_extensions == nil {")
1088 g.In()
1089 g.P("this.XXX_extensions = make(map[int32][]byte)")
1090 g.Out()
1091 g.P("}")
1092 g.P("return this.XXX_extensions")
1093 g.Out()
1094 g.P("}")
1095 }
1096
David Symonds31d58a22011-01-20 18:33:21 +11001097 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1098
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001099 // Default constants
1100 for _, field := range message.Field {
1101 def := proto.GetString(field.DefaultValue)
1102 if def == "" {
1103 continue
1104 }
1105 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1106 typename, _ := g.GoType(message, field)
1107 if typename[0] == '*' {
1108 typename = typename[1:]
1109 }
1110 kind := "const "
1111 switch {
1112 case typename == "bool":
1113 case typename == "string":
1114 def = Quote(def)
1115 case typename == "[]byte":
1116 def = "[]byte(" + Quote(def) + ")"
1117 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001118 case def == "inf", def == "-inf", def == "nan":
1119 // These names are known to, and defined by, the protocol language.
1120 switch def {
1121 case "inf":
1122 def = "math.Inf(1)"
1123 case "-inf":
1124 def = "math.Inf(-1)"
1125 case "nan":
1126 def = "math.NaN()"
1127 }
1128 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1129 def = "float32(" + def + ")"
1130 }
1131 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001132 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1133 // Must be an enum. Need to construct the prefixed name.
1134 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1135 enum, ok := obj.(*EnumDescriptor)
1136 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001137 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001138 continue
1139 }
Rob Pike87af39e2010-07-19 10:48:02 -07001140 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001141 }
1142 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001143 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001144 }
1145 g.P()
1146
1147 for _, ext := range message.ext {
1148 g.generateExtension(ext)
1149 }
1150}
1151
1152func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001153 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001154
1155 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1156 field := ext.FieldDescriptorProto
1157 fieldType, wireType := g.GoType(ext.parent, field)
1158 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001159 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001160 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1161 // foreign extension type
1162 g.RecordTypeUse(*n)
1163 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001164
Rob Pikec9e7d972010-06-10 10:30:22 -07001165 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001166 g.In()
1167 g.P("ExtendedType: (", extendedType, ")(nil),")
1168 g.P("ExtensionType: (", fieldType, ")(nil),")
1169 g.P("Field: ", field.Number, ",")
David Symondse37856c2011-06-22 12:52:53 +10001170 g.P(`Name: "`, g.packageName, ".", *field.Name, `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001171 g.P("Tag: ", tag, ",")
1172
1173 g.Out()
1174 g.P("}")
1175 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001176
1177 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001178}
1179
1180func (g *Generator) generateInitFunction() {
1181 g.P("func init() {")
1182 g.In()
1183 for _, enum := range g.file.enum {
1184 g.generateEnumRegistration(enum)
1185 }
David Symondse37856c2011-06-22 12:52:53 +10001186 for _, d := range g.file.desc {
1187 for _, ext := range d.ext {
1188 g.generateExtensionRegistration(ext)
1189 }
1190 }
1191 for _, ext := range g.file.ext {
1192 g.generateExtensionRegistration(ext)
1193 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001194 g.Out()
1195 g.P("}")
1196}
1197
1198func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1199 pkg := g.packageName + "." // We always print the full package name here.
1200 // The full type name
1201 typeName := enum.TypeName()
1202 // The full type name, CamelCased.
1203 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001204 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001205}
1206
David Symondse37856c2011-06-22 12:52:53 +10001207func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
1208 g.P(g.ProtoPkg+".RegisterExtension(", ext.DescName(), ")")
1209}
1210
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001211// And now lots of helper functions.
1212
Rob Pike2c7bafc2010-06-10 16:07:14 -07001213// Is c an ASCII lower-case letter?
1214func isASCIILower(c byte) bool {
1215 return 'a' <= c && c <= 'z'
1216}
1217
1218// Is c an ASCII digit?
1219func isASCIIDigit(c byte) bool {
1220 return '0' <= c && c <= '9'
1221}
1222
1223// CamelCase returns the CamelCased name.
1224// If there is an interior underscore followed by a lower case letter,
1225// drop the underscore and convert the letter to upper case.
1226// There is a remote possibility of this rewrite causing a name collision,
1227// but it's so remote we're prepared to pretend it's nonexistent - since the
1228// C++ generator lowercases names, it's extremely unlikely to have two fields
1229// with different capitalizations.
1230// In short, _my_field_name_2 becomes XMyFieldName2.
1231func CamelCase(s string) string {
1232 if s == "" {
1233 return ""
1234 }
1235 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001236 i := 0
1237 if s[0] == '_' {
1238 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001239 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001240 i++
1241 }
1242 // Invariant: if the next letter is lower case, it must be converted
1243 // to upper case.
1244 // That is, we process a word at a time, where words are marked by _ or
1245 // upper case letter. Digits are treated as words.
1246 for ; i < len(s); i++ {
1247 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001248 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1249 continue // Skip the underscore in s.
1250 }
1251 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001252 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001253 continue
1254 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001255 // Assume we have a letter now - if not, it's a bogus identifier.
1256 // The next word is a sequence of characters that must start upper case.
1257 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001258 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001259 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001260 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001261 // Accept lower case sequence that follows.
1262 for i+1 < len(s) && isASCIILower(s[i+1]) {
1263 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001264 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001265 }
1266 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001267 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001268}
1269
1270// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1271// be joined with "_".
1272func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1273
1274// dottedSlice turns a sliced name into a dotted name.
1275func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1276
1277// Quote returns a Go-source quoted string representation of s.
1278func Quote(s string) string { return fmt.Sprintf("%q", s) }
1279
1280// Given a .proto file name, return the output name for the generated Go program.
1281func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001282 ext := path.Ext(name)
1283 if ext == ".proto" || ext == ".protodevel" {
1284 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001285 }
1286 return name + ".pb.go"
1287}
1288
1289// Is this field optional?
1290func isOptional(field *descriptor.FieldDescriptorProto) bool {
1291 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1292}
1293
1294// Is this field required?
1295func isRequired(field *descriptor.FieldDescriptorProto) bool {
1296 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1297}
1298
1299// Is this field repeated?
1300func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1301 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1302}
1303
1304// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001305// which can be dotted in the input .proto file. It maps dots to underscores.
1306// Because we also get here from package names generated from file names, it also maps
1307// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001308func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001309 switch rune {
1310 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001311 return '_'
1312 }
1313 return rune
1314}
Rob Pikec9e7d972010-06-10 10:30:22 -07001315
1316// BaseName returns the last path element of the name, with the last dotted suffix removed.
1317func BaseName(name string) string {
1318 // First, find the last element
1319 if i := strings.LastIndex(name, "/"); i >= 0 {
1320 name = name[i+1:]
1321 }
1322 // Now drop the suffix
1323 if i := strings.LastIndex(name, "."); i >= 0 {
1324 name = name[0:i]
1325 }
1326 return name
1327}