blob: 68fcdc706a59b385b020dcb5ba3425db5d54af97 [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 {
David Symonds4decd802011-08-04 11:27:07 +100088 file *descriptor.FileDescriptorProto // File this object comes from.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070089}
90
91// PackageName is name in the package clause in the generated file.
David Symonds4decd802011-08-04 11:27:07 +100092func (c *common) PackageName() string { return uniquePackageOf(c.file) }
93
94func (c *common) File() *descriptor.FileDescriptorProto { return c.file }
Rob Pikeaf82b4e2010-04-30 15:19:25 -070095
96// Descriptor represents a protocol buffer message.
97type Descriptor struct {
98 common
99 *descriptor.DescriptorProto
100 parent *Descriptor // The containing message, if any.
101 nested []*Descriptor // Inner messages, if any.
102 ext []*ExtensionDescriptor // Extensions, if any.
103 typename []string // Cached typename vector.
104}
105
106// TypeName returns the elements of the dotted type name.
107// The package name is not part of this name.
108func (d *Descriptor) TypeName() []string {
109 if d.typename != nil {
110 return d.typename
111 }
112 n := 0
113 for parent := d; parent != nil; parent = parent.parent {
114 n++
115 }
116 s := make([]string, n, n)
117 for parent := d; parent != nil; parent = parent.parent {
118 n--
119 s[n] = proto.GetString(parent.Name)
120 }
121 d.typename = s
122 return s
123}
124
125// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
126// Otherwise it will be the descriptor of the message in which it is defined.
127type EnumDescriptor struct {
128 common
129 *descriptor.EnumDescriptorProto
130 parent *Descriptor // The containing message, if any.
131 typename []string // Cached typename vector.
132}
133
134// TypeName returns the elements of the dotted type name.
135// The package name is not part of this name.
136func (e *EnumDescriptor) TypeName() (s []string) {
137 if e.typename != nil {
138 return e.typename
139 }
140 name := proto.GetString(e.Name)
141 if e.parent == nil {
142 s = make([]string, 1)
143 } else {
144 pname := e.parent.TypeName()
145 s = make([]string, len(pname)+1)
146 copy(s, pname)
147 }
148 s[len(s)-1] = name
149 e.typename = s
150 return s
151}
152
153// Everything but the last element of the full type name, CamelCased.
154// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
155func (e *EnumDescriptor) prefix() string {
156 typeName := e.TypeName()
157 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
158 if e.parent == nil {
159 // If the enum is not part of a message, the prefix is just the type name.
160 ccPrefix = CamelCase(*e.Name) + "_"
161 }
162 return ccPrefix
163}
164
165// The integer value of the named constant in this enumerated type.
166func (e *EnumDescriptor) integerValueAsString(name string) string {
167 for _, c := range e.Value {
168 if proto.GetString(c.Name) == name {
169 return fmt.Sprint(proto.GetInt32(c.Number))
170 }
171 }
David Symonds9d0000e2011-02-03 10:48:14 +1100172 log.Fatal("cannot find value for enum constant")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700173 return ""
174}
175
David Symonds4decd802011-08-04 11:27:07 +1000176// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700177// Otherwise it will be the descriptor of the message in which it is defined.
178type ExtensionDescriptor struct {
179 common
180 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700181 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700182}
183
184// TypeName returns the elements of the dotted type name.
185// The package name is not part of this name.
186func (e *ExtensionDescriptor) TypeName() (s []string) {
187 name := proto.GetString(e.Name)
188 if e.parent == nil {
189 // top-level extension
190 s = make([]string, 1)
191 } else {
192 pname := e.parent.TypeName()
193 s = make([]string, len(pname)+1)
194 copy(s, pname)
195 }
196 s[len(s)-1] = name
197 return s
198}
199
David Symondse37856c2011-06-22 12:52:53 +1000200// DescName returns the variable name used for the generated descriptor.
201func (e *ExtensionDescriptor) DescName() string {
202 // The full type name.
203 typeName := e.TypeName()
204 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
205 for i, s := range typeName {
206 typeName[i] = CamelCase(s)
207 }
208 return "E_" + strings.Join(typeName, "_")
209}
210
David Symonds4decd802011-08-04 11:27:07 +1000211// ImportedDescriptor describes a type that has been publicly imported from another file.
212type ImportedDescriptor struct {
213 common
214 o Object
215}
216
217func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() }
218
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700219// FileDescriptor describes an protocol buffer descriptor file (.proto).
220// It includes slices of all the messages and enums defined within it.
221// Those slices are constructed by WrapTypes.
222type FileDescriptor struct {
223 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700224 desc []*Descriptor // All the messages defined in this file.
225 enum []*EnumDescriptor // All the enums defined in this file.
226 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds4decd802011-08-04 11:27:07 +1000227 imp []*ImportedDescriptor // All types defined in files publicly imported by this file.
David Symonds31d58a22011-01-20 18:33:21 +1100228
229 // The full list of symbols that are exported.
230 // This is used for supporting public imports.
231 exported []Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700232}
233
234// PackageName is the package name we'll use in the generated code to refer to this file.
235func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
236
237// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700238// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700239func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700240 // Does the file have a package clause?
David Symonds7d5c8242011-03-14 12:03:50 -0700241 if pkg := proto.GetString(d.Package); pkg != "" {
Rob Pikec9e7d972010-06-10 10:30:22 -0700242 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700243 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700244 // Use the file base name.
245 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700246}
247
David Symonds31d58a22011-01-20 18:33:21 +1100248func (d *FileDescriptor) addExport(symbol Symbol) {
249 d.exported = append(d.exported, symbol)
250}
251
252// Symbol is an interface representing an exported Go symbol.
253type Symbol interface {
254 // GenerateAlias should generate an appropriate alias
255 // for the symbol from the named package.
256 GenerateAlias(g *Generator, pkg string)
257}
258
259type messageSymbol struct {
260 sym string
261 hasExtensions, isMessageSet bool
262}
263
264func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
265 remoteSym := pkg + "." + ms.sym
266
267 g.P("type ", ms.sym, " ", remoteSym)
268 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
David Symondse37856c2011-06-22 12:52:53 +1000269 g.P("func (this *", ms.sym, ") String() string { return (*", remoteSym, ")(this).String() }")
David Symonds31d58a22011-01-20 18:33:21 +1100270 if ms.hasExtensions {
271 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
272 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
273 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32][]byte ",
274 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
275 if ms.isMessageSet {
276 g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
277 "{ return (*", remoteSym, ")(this).Marshal() }")
278 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
279 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
280 }
281 }
282}
283
284type enumSymbol string
285
286func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
287 s := string(es)
288 g.P("type ", s, " ", pkg, ".", s)
289 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
290 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
291 g.P("func New", s, "(x int32) *", s, " { e := ", s, "(x); return &e }")
292}
293
294type constOrVarSymbol struct {
295 sym string
296 typ string // either "const" or "var"
297}
298
299func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
300 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
301}
302
David Symonds4decd802011-08-04 11:27:07 +1000303// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700304type Object interface {
305 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
306 TypeName() []string
David Symonds4decd802011-08-04 11:27:07 +1000307 File() *descriptor.FileDescriptorProto
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700308}
309
310// Each package name we generate must be unique. The package we're generating
David Symonds4decd802011-08-04 11:27:07 +1000311// gets its own name but every other package must have a unique name that does
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700312// not conflict in the code we generate. These names are chosen globally (although
313// they don't have to be, it simplifies things to do them globally).
314func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
315 s, ok := uniquePackageName[fd]
316 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100317 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700318 }
319 return s
320}
321
322// Generator is the type whose methods generate the output, stored in the associated response structure.
323type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000324 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700325
326 Request *plugin.CodeGeneratorRequest // The input.
327 Response *plugin.CodeGeneratorResponse // The output.
328
Rob Pikec9e7d972010-06-10 10:30:22 -0700329 Param map[string]string // Command-line parameters.
330 ImportPrefix string // String to prefix to imported package file names.
331 ImportMap map[string]string // Mapping from import name to generated name
332
333 ProtoPkg string // The name under which we import the library's package proto.
334
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700335 packageName string // What we're calling ourselves.
336 allFiles []*FileDescriptor // All files in the tree
337 genFiles []*FileDescriptor // Those files we will generate output for.
338 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000339 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700340 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
341 indent string
342}
343
344// New creates a new generator and allocates the request and response protobufs.
345func New() *Generator {
346 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000347 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100348 g.Request = new(plugin.CodeGeneratorRequest)
349 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700350 return g
351}
352
353// Error reports a problem, including an os.Error, and exits the program.
354func (g *Generator) Error(err os.Error, msgs ...string) {
355 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700356 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700357 g.Response.Error = proto.String(s)
358 os.Exit(1)
359}
360
361// Fail reports a problem and exits the program.
362func (g *Generator) Fail(msgs ...string) {
363 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700364 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700365 g.Response.Error = proto.String(s)
366 os.Exit(1)
367}
368
Rob Pikec9e7d972010-06-10 10:30:22 -0700369// CommandLineParameters breaks the comma-separated list of key=value pairs
370// in the parameter (a member of the request protobuf) into a key/value map.
371// It then sets file name mappings defined by those entries.
372func (g *Generator) CommandLineParameters(parameter string) {
373 g.Param = make(map[string]string)
David Symonds8935abf2011-07-04 15:53:16 +1000374 for _, p := range strings.Split(parameter, ",") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700375 if i := strings.Index(p, "="); i < 0 {
376 g.Param[p] = ""
377 } else {
378 g.Param[p[0:i]] = p[i+1:]
379 }
380 }
381
382 g.ImportMap = make(map[string]string)
383 for k, v := range g.Param {
384 if k == "import_prefix" {
385 g.ImportPrefix = v
386 } else if len(k) > 0 && k[0] == 'M' {
387 g.ImportMap[k[1:]] = v
388 }
389 }
390}
391
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700392// DefaultPackageName returns the package name printed for the object.
393// If its file is in a different package, it returns the package name we're using for this file, plus ".".
394// Otherwise it returns the empty string.
395func (g *Generator) DefaultPackageName(obj Object) string {
396 pkg := obj.PackageName()
397 if pkg == g.packageName {
398 return ""
399 }
400 return pkg + "."
401}
402
403// For each input file, the unique package name to use, underscored.
404var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700405// Package names already registered. Key is the name from the .proto file;
406// value is the name that appears in the generated code.
407var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700408
Rob Pikec9e7d972010-06-10 10:30:22 -0700409// Create and remember a guaranteed unique package name for this file descriptor.
410// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
411// has no file descriptor.
412func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds41389812011-07-19 14:57:40 +1000413 // Convert dots to underscores before finding a unique alias.
414 pkg = strings.Map(DotToUnderscore, pkg)
415
David Symonds79eae332010-10-16 11:33:20 +1100416 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700417 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100418 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700419 }
420 // Install it.
421 pkgNamesInUse[pkg] = true
Rob Pikec9e7d972010-06-10 10:30:22 -0700422 if f != nil {
423 uniquePackageName[f.FileDescriptorProto] = pkg
424 }
425 return pkg
426}
427
428// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700429// The package name must agree across all files being generated.
430// It also defines unique package names for all imported files.
431func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700432 // Register the name for this package. It will be the first name
433 // registered so is guaranteed to be unmodified.
434 pkg := g.genFiles[0].originalPackageName()
435 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
436 // Register the proto package name. It might collide with the
437 // name of a package we import.
438 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
David Symonds7d5c8242011-03-14 12:03:50 -0700439 // Verify that we are generating output for a single package.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700440 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700441 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700442 if thisPkg != pkg {
443 g.Fail("inconsistent package names:", thisPkg, pkg)
444 }
445 }
446AllFiles:
447 for _, f := range g.allFiles {
448 for _, genf := range g.genFiles {
449 if f == genf {
450 // In this package already.
451 uniquePackageName[f.FileDescriptorProto] = g.packageName
452 continue AllFiles
453 }
454 }
David Symonds7d5c8242011-03-14 12:03:50 -0700455 // The file is a dependency, so we want to ignore its go_package option
456 // because that is only relevant for its specific generated output.
457 pkg := proto.GetString(f.Package)
458 if pkg == "" {
459 pkg = BaseName(*f.Name)
460 }
461 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700462 }
463}
464
465// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
466// and FileDescriptorProtos into file-referenced objects within the Generator.
467// It also creates the list of files to generate and so should be called before GenerateAllFiles.
468func (g *Generator) WrapTypes() {
469 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
470 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700471 // We must wrap the descriptors before we wrap the enums
472 descs := wrapDescriptors(f)
473 g.buildNestedDescriptors(descs)
474 enums := wrapEnumDescriptors(f, descs)
475 exts := wrapExtensions(f)
David Symonds4decd802011-08-04 11:27:07 +1000476 imps := wrapImported(f, g)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700477 g.allFiles[i] = &FileDescriptor{
478 FileDescriptorProto: f,
479 desc: descs,
480 enum: enums,
481 ext: exts,
David Symonds4decd802011-08-04 11:27:07 +1000482 imp: imps,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700483 }
484 }
485
486 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
487FindFiles:
488 for i, fileName := range g.Request.FileToGenerate {
489 // Search the list. This algorithm is n^2 but n is tiny.
490 for _, file := range g.allFiles {
491 if fileName == proto.GetString(file.Name) {
492 g.genFiles[i] = file
493 continue FindFiles
494 }
495 }
496 g.Fail("could not find file named", fileName)
497 }
498 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
499}
500
501// Scan the descriptors in this file. For each one, build the slice of nested descriptors
502func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
503 for _, desc := range descs {
504 if len(desc.NestedType) != 0 {
505 desc.nested = make([]*Descriptor, len(desc.NestedType))
506 n := 0
507 for _, nest := range descs {
508 if nest.parent == desc {
509 desc.nested[n] = nest
510 n++
511 }
512 }
513 if n != len(desc.NestedType) {
514 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
515 }
516 }
517 }
518}
519
520// Construct the Descriptor and add it to the slice
521func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
David Symonds4decd802011-08-04 11:27:07 +1000522 d := &Descriptor{common{file}, desc, parent, nil, nil, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700523
524 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
525 for i, field := range desc.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000526 d.ext[i] = &ExtensionDescriptor{common{file}, field, d}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700527 }
528
David Symondscc7142e2010-11-06 14:37:15 +1100529 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700530}
531
532// Return a slice of all the Descriptors defined within this file
533func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
534 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
535 for _, desc := range file.MessageType {
536 sl = wrapThisDescriptor(sl, desc, nil, file)
537 }
538 return sl
539}
540
541// Wrap this Descriptor, recursively
542func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
543 sl = addDescriptor(sl, desc, parent, file)
544 me := sl[len(sl)-1]
545 for _, nested := range desc.NestedType {
546 sl = wrapThisDescriptor(sl, nested, me, file)
547 }
548 return sl
549}
550
551// Construct the EnumDescriptor and add it to the slice
552func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symonds4decd802011-08-04 11:27:07 +1000553 return append(sl, &EnumDescriptor{common{file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700554}
555
556// Return a slice of all the EnumDescriptors defined within this file
557func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
558 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000559 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700560 for _, enum := range file.EnumType {
561 sl = addEnumDescriptor(sl, enum, nil, file)
562 }
David Symonds5256cf62010-06-27 10:33:42 +1000563 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700564 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000565 for _, enum := range nested.EnumType {
566 sl = addEnumDescriptor(sl, enum, nested, file)
567 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700568 }
569 return sl
570}
571
572// Return a slice of all the top-level ExtensionDescriptors defined within this file.
573func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
574 sl := make([]*ExtensionDescriptor, len(file.Extension))
575 for i, field := range file.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000576 sl[i] = &ExtensionDescriptor{common{file}, field, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700577 }
578 return sl
579}
580
David Symonds4decd802011-08-04 11:27:07 +1000581// Return a slice of all the types that are publicly imported into this file.
582func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) {
583 for _, index := range file.PublicDependency {
584 df := g.fileByName(file.Dependency[index])
585 for _, d := range df.desc {
586 sl = append(sl, &ImportedDescriptor{common{file}, d})
587 }
588 for _, e := range df.enum {
589 sl = append(sl, &ImportedDescriptor{common{file}, e})
590 }
591 for _, ext := range df.ext {
592 sl = append(sl, &ImportedDescriptor{common{file}, ext})
593 }
594 }
595 return
596}
597
Rob Pikec9e7d972010-06-10 10:30:22 -0700598// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700599// The key names for the map come from the input data, which puts a period at the beginning.
600// It should be called after SetPackageNames and before GenerateAllFiles.
601func (g *Generator) BuildTypeNameMap() {
602 g.typeNameToObject = make(map[string]Object)
603 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700604 // The names in this loop are defined by the proto world, not us, so the
605 // package name may be empty. If so, the dotted package name of X will
606 // be ".X"; otherwise it will be ".pkg.X".
607 dottedPkg := "." + proto.GetString(f.Package)
608 if dottedPkg != "." {
609 dottedPkg += "."
610 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700611 for _, enum := range f.enum {
612 name := dottedPkg + dottedSlice(enum.TypeName())
613 g.typeNameToObject[name] = enum
614 }
615 for _, desc := range f.desc {
616 name := dottedPkg + dottedSlice(desc.TypeName())
617 g.typeNameToObject[name] = desc
618 }
619 }
620}
621
622// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
623// returns the descriptor for the message or enum with that name.
624func (g *Generator) ObjectNamed(typeName string) Object {
David Symonds4decd802011-08-04 11:27:07 +1000625 o, ok := g.typeNameToObject[typeName]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700626 if !ok {
627 g.Fail("can't find object with type", typeName)
628 }
David Symonds4decd802011-08-04 11:27:07 +1000629
630 // If the file of this object isn't a direct dependency of the current file,
631 // or in the current file, then this object has been publicly imported into
632 // a dependency of the current file.
633 // We should return the ImportedDescriptor object for it instead.
634 direct := *o.File().Name == *g.file.Name
635 if !direct {
636 for _, dep := range g.file.Dependency {
637 if *g.fileByName(dep).Name == *o.File().Name {
638 direct = true
639 break
640 }
641 }
642 }
643 if !direct {
644 found := false
645 Loop:
646 for _, dep := range g.file.Dependency {
647 df := g.fileByName(*g.fileByName(dep).Name)
648 for _, td := range df.imp {
649 if td.o == o {
650 // Found it!
651 o = td
652 found = true
653 break Loop
654 }
655 }
656 }
657 if !found {
658 log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name)
659 }
660 }
661
662 return o
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700663}
664
665// P prints the arguments to the generated output. It handles strings and int32s, plus
666// handling indirections because they may be *string, etc.
667func (g *Generator) P(str ...interface{}) {
668 g.WriteString(g.indent)
669 for _, v := range str {
670 switch s := v.(type) {
671 case string:
672 g.WriteString(s)
673 case *string:
674 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700675 case bool:
676 g.WriteString(fmt.Sprintf("%t", s))
677 case *bool:
678 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700679 case *int32:
680 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700681 case float64:
682 g.WriteString(fmt.Sprintf("%g", s))
683 case *float64:
684 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700685 default:
686 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
687 }
688 }
689 g.WriteByte('\n')
690}
691
692// In Indents the output one tab stop.
693func (g *Generator) In() { g.indent += "\t" }
694
695// Out unindents the output one tab stop.
696func (g *Generator) Out() {
697 if len(g.indent) > 0 {
698 g.indent = g.indent[1:]
699 }
700}
701
702// GenerateAllFiles generates the output for all the files we're outputting.
703func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700704 // Initialize the plugins
705 for _, p := range plugins {
706 p.Init(g)
707 }
David Symonds31d58a22011-01-20 18:33:21 +1100708 // Generate the output. The generator runs for every file, even the files
709 // that we don't generate output for, so that we can collate the full list
710 // of exported symbols to support public imports.
711 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
712 for _, file := range g.genFiles {
713 genFileMap[file] = true
714 }
715 i := 0
716 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700717 g.Reset()
718 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100719 if _, ok := genFileMap[file]; !ok {
720 continue
721 }
David Symondsb0127532010-11-09 11:10:46 +1100722 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700723 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
724 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100725 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700726 }
727}
728
729// Run all the plugins associated with the file.
730func (g *Generator) runPlugins(file *FileDescriptor) {
731 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700732 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700733 }
734}
735
736
737// FileOf return the FileDescriptor for this FileDescriptorProto.
738func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
739 for _, file := range g.allFiles {
740 if file.FileDescriptorProto == fd {
741 return file
742 }
743 }
744 g.Fail("could not find file in table:", proto.GetString(fd.Name))
745 return nil
746}
747
748// Fill the response protocol buffer with the generated output for all the files we're
749// supposed to generate.
750func (g *Generator) generate(file *FileDescriptor) {
751 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000752 g.usedPackages = make(map[string]bool)
753
David Symonds4decd802011-08-04 11:27:07 +1000754 for _, td := range g.file.imp {
755 g.generateImported(td)
756 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700757 for _, enum := range g.file.enum {
758 g.generateEnum(enum)
759 }
760 for _, desc := range g.file.desc {
761 g.generateMessage(desc)
762 }
763 for _, ext := range g.file.ext {
764 g.generateExtension(ext)
765 }
766 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000767
Rob Pikec9e7d972010-06-10 10:30:22 -0700768 // Run the plugins before the imports so we know which imports are necessary.
769 g.runPlugins(file)
770
David Symondsf90e3382010-05-05 10:53:44 +1000771 // Generate header and imports last, though they appear first in the output.
772 rem := g.Buffer
773 g.Buffer = new(bytes.Buffer)
774 g.generateHeader()
775 g.generateImports()
776 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +1000777
778 // Reformat generated code.
779 fset := token.NewFileSet()
780 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
781 if err != nil {
782 g.Fail("bad Go source code was generated:", err.String())
783 return
784 }
785 g.Reset()
786 _, err = (&printer.Config{printer.TabIndent, 8}).Fprint(g, fset, ast)
787 if err != nil {
788 g.Fail("generated Go source code could not be reformatted:", err.String())
789 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700790}
791
792// Generate the header, including package definition and imports
793func (g *Generator) generateHeader() {
794 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
795 g.P("// DO NOT EDIT!")
796 g.P()
797 g.P("package ", g.file.PackageName())
798 g.P()
799}
800
David Symonds31d58a22011-01-20 18:33:21 +1100801func (g *Generator) fileByName(filename string) *FileDescriptor {
802 for _, fd := range g.allFiles {
803 if proto.GetString(fd.Name) == filename {
804 return fd
805 }
806 }
807 return nil
808}
809
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700810// Generate the header, including package definition and imports
811func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700812 // We almost always need a proto import. Rather than computing when we
813 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100814 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700815 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100816 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100817 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700818 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100819 fd := g.fileByName(s)
820 // Do not import our own package.
821 if fd.PackageName() == g.packageName {
822 continue
823 }
824 filename := goFileName(s)
825 if substitution, ok := g.ImportMap[s]; ok {
826 filename = substitution
827 }
828 filename = g.ImportPrefix + filename
829 if strings.HasSuffix(filename, ".go") {
830 filename = filename[0 : len(filename)-3]
831 }
832 if _, ok := g.usedPackages[fd.PackageName()]; ok {
833 g.P("import ", fd.PackageName(), " ", Quote(filename))
834 } else {
David Symonds3fa055f2011-05-05 15:19:04 -0700835 // TODO: Re-enable this when we are more feature-complete.
836 // For instance, some protos use foreign field extensions, which we don't support.
837 // Until then, this is just annoying spam.
838 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
David Symonds4decd802011-08-04 11:27:07 +1000839 g.P("// discarding unused import ", fd.PackageName(), " ", Quote(filename))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700840 }
841 }
842 g.P()
843 // TODO: may need to worry about uniqueness across plugins
844 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700845 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700846 g.P()
847 }
David Symondscea785b2011-01-07 11:02:30 +1100848 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700849 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100850 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100851 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700852 g.P()
David Symonds31d58a22011-01-20 18:33:21 +1100853
854 // Symbols from public imports.
David Symonds4decd802011-08-04 11:27:07 +1000855Loop:
David Symonds31d58a22011-01-20 18:33:21 +1100856 for _, index := range g.file.PublicDependency {
David Symonds4decd802011-08-04 11:27:07 +1000857 // Don't generate aliases for public imports of files
858 // that we are generating code for, since those symbols
859 // will already be in this package.
860 filename := g.file.Dependency[index]
861 for _, fd := range g.genFiles {
862 if proto.GetString(fd.Name) == filename {
863 g.P("// Ignoring public import ", filename)
864 continue Loop
865 }
866 }
867
868 fd := g.fileByName(filename)
869
David Symonds31d58a22011-01-20 18:33:21 +1100870 g.P("// Types from public import ", *fd.Name)
871 for _, sym := range fd.exported {
872 sym.GenerateAlias(g, fd.PackageName())
873 }
874 }
875 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700876}
877
David Symonds4decd802011-08-04 11:27:07 +1000878func (g *Generator) generateImported(id *ImportedDescriptor) {
879 tn := id.TypeName()
880 sn := tn[len(tn)-1]
881 g.P("// ", sn, " from public import ", *id.File().Name)
882 g.usedPackages[id.o.PackageName()] = true
883 g.P()
884
885 // TODO: Move the public import symbol generation into here.
886}
887
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700888// Generate the enum definitions for this EnumDescriptor.
889func (g *Generator) generateEnum(enum *EnumDescriptor) {
890 // The full type name
891 typeName := enum.TypeName()
892 // The full type name, CamelCased.
893 ccTypeName := CamelCaseSlice(typeName)
894 ccPrefix := enum.prefix()
895 g.P("type ", ccTypeName, " int32")
David Symonds31d58a22011-01-20 18:33:21 +1100896 g.file.addExport(enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700897 g.P("const (")
898 g.In()
899 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100900 name := ccPrefix + *e.Name
901 g.P(name, " = ", e.Number)
902 g.file.addExport(constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700903 }
904 g.Out()
905 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100906 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700907 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700908 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700909 for _, e := range enum.Value {
910 duplicate := ""
911 if _, present := generated[*e.Number]; present {
912 duplicate = "// Duplicate value: "
913 }
914 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
915 generated[*e.Number] = true
916 }
917 g.Out()
918 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100919 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700920 g.In()
921 for _, e := range enum.Value {
922 g.P(Quote(*e.Name), ": ", e.Number, ",")
923 }
924 g.Out()
925 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100926
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700927 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
928 g.In()
929 g.P("e := ", ccTypeName, "(x)")
930 g.P("return &e")
931 g.Out()
932 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100933
934 g.P("func (x ", ccTypeName, ") String() string {")
935 g.In()
936 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
937 g.Out()
938 g.P("}")
939
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700940 g.P()
941}
942
David Symonds8935abf2011-07-04 15:53:16 +1000943// The tag is a string like "varint,2,opt,name=fieldname,def=7" that
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700944// identifies details of the field for the protocol buffer marshaling and unmarshaling
945// code. The fields are:
946// wire encoding
947// protocol tag number
948// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100949// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700950// name= the original declared name
951// enum= the name of the enum type if it is an enum-typed field.
952// def= string representation of the default value, if any.
953// The default value must be in a representation that can be used at run-time
954// to generate the default value. Thus bools become 0 and 1, for instance.
955func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
956 optrepreq := ""
957 switch {
958 case isOptional(field):
959 optrepreq = "opt"
960 case isRequired(field):
961 optrepreq = "req"
962 case isRepeated(field):
963 optrepreq = "rep"
964 }
965 defaultValue := proto.GetString(field.DefaultValue)
966 if defaultValue != "" {
967 switch *field.Type {
968 case descriptor.FieldDescriptorProto_TYPE_BOOL:
969 if defaultValue == "true" {
970 defaultValue = "1"
971 } else {
972 defaultValue = "0"
973 }
974 case descriptor.FieldDescriptorProto_TYPE_STRING,
975 descriptor.FieldDescriptorProto_TYPE_BYTES:
976 // Protect frogs.
977 defaultValue = Quote(defaultValue)
978 // Don't need the quotes
979 defaultValue = defaultValue[1 : len(defaultValue)-1]
980 case descriptor.FieldDescriptorProto_TYPE_ENUM:
981 // For enums we need to provide the integer constant.
982 obj := g.ObjectNamed(proto.GetString(field.TypeName))
983 enum, ok := obj.(*EnumDescriptor)
984 if !ok {
985 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
986 }
987 defaultValue = enum.integerValueAsString(defaultValue)
988 }
989 defaultValue = ",def=" + defaultValue
990 }
991 enum := ""
992 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
David Symonds4decd802011-08-04 11:27:07 +1000993 // We avoid using obj.PackageName(), because we want to use the
994 // original (proto-world) package name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700995 obj := g.ObjectNamed(proto.GetString(field.TypeName))
David Symonds4decd802011-08-04 11:27:07 +1000996 enum = ",enum="
997 if pkg := proto.GetString(obj.File().Package); pkg != "" {
998 enum += pkg + "."
999 }
1000 enum += CamelCaseSlice(obj.TypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001001 }
David Symonds5b7775e2010-12-01 10:09:04 +11001002 packed := ""
1003 if field.Options != nil && proto.GetBool(field.Options.Packed) {
1004 packed = ",packed"
1005 }
David Symondse37856c2011-06-22 12:52:53 +10001006 fieldName := proto.GetString(field.Name)
1007 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +10001008 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
1009 // We must use the type name for groups instead of
1010 // the field name to preserve capitalization.
1011 // type_name in FieldDescriptorProto is fully-qualified,
1012 // but we only want the local part.
1013 name = *field.TypeName
1014 if i := strings.LastIndex(name, "."); i >= 0 {
1015 name = name[i+1:]
1016 }
David Symondse37856c2011-06-22 12:52:53 +10001017 }
1018 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001019 name = ""
1020 } else {
1021 name = ",name=" + name
1022 }
David Symonds8935abf2011-07-04 15:53:16 +10001023 return Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001024 wiretype,
1025 proto.GetInt32(field.Number),
1026 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +11001027 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001028 name,
1029 enum,
1030 defaultValue))
1031}
1032
1033func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
1034 switch typ {
1035 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1036 return false
1037 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1038 return false
1039 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1040 return false
1041 }
1042 return true
1043}
1044
1045// TypeName is the printed name appropriate for an item. If the object is in the current file,
1046// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -07001047// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001048// package name followed by the item name.
1049// The result always has an initial capital.
1050func (g *Generator) TypeName(obj Object) string {
1051 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
1052}
1053
1054// TypeNameWithPackage is like TypeName, but always includes the package
1055// name even if the object is in our own package.
1056func (g *Generator) TypeNameWithPackage(obj Object) string {
1057 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
1058}
1059
1060// GoType returns a string representing the type name, and the wire type
1061func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
1062 // TODO: Options.
1063 switch *field.Type {
1064 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
1065 typ, wire = "float64", "fixed64"
1066 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
1067 typ, wire = "float32", "fixed32"
1068 case descriptor.FieldDescriptorProto_TYPE_INT64:
1069 typ, wire = "int64", "varint"
1070 case descriptor.FieldDescriptorProto_TYPE_UINT64:
1071 typ, wire = "uint64", "varint"
1072 case descriptor.FieldDescriptorProto_TYPE_INT32:
1073 typ, wire = "int32", "varint"
1074 case descriptor.FieldDescriptorProto_TYPE_UINT32:
1075 typ, wire = "uint32", "varint"
1076 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
1077 typ, wire = "uint64", "fixed64"
1078 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
1079 typ, wire = "uint32", "fixed32"
1080 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1081 typ, wire = "bool", "varint"
1082 case descriptor.FieldDescriptorProto_TYPE_STRING:
1083 typ, wire = "string", "bytes"
1084 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1085 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1086 typ, wire = "*"+g.TypeName(desc), "group"
1087 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1088 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1089 typ, wire = "*"+g.TypeName(desc), "bytes"
1090 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1091 typ, wire = "[]byte", "bytes"
1092 case descriptor.FieldDescriptorProto_TYPE_ENUM:
1093 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1094 typ, wire = g.TypeName(desc), "varint"
1095 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
1096 typ, wire = "int32", "fixed32"
1097 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
1098 typ, wire = "int64", "fixed64"
1099 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1100 typ, wire = "int32", "zigzag32"
1101 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1102 typ, wire = "int64", "zigzag64"
1103 default:
1104 g.Fail("unknown type for", proto.GetString(field.Name))
1105 }
1106 if isRepeated(field) {
1107 typ = "[]" + typ
1108 } else if needsStar(*field.Type) {
1109 typ = "*" + typ
1110 }
1111 return
1112}
1113
David Symondsf90e3382010-05-05 10:53:44 +10001114func (g *Generator) RecordTypeUse(t string) {
1115 if obj, ok := g.typeNameToObject[t]; ok {
David Symonds4decd802011-08-04 11:27:07 +10001116 // Call ObjectNamed to get the true object to record the use.
1117 obj = g.ObjectNamed(t)
David Symondsf90e3382010-05-05 10:53:44 +10001118 g.usedPackages[obj.PackageName()] = true
1119 }
1120}
1121
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001122// Generate the type and default constant definitions for this Descriptor.
1123func (g *Generator) generateMessage(message *Descriptor) {
1124 // The full type name
1125 typeName := message.TypeName()
1126 // The full type name, CamelCased.
1127 ccTypeName := CamelCaseSlice(typeName)
1128
1129 g.P("type ", ccTypeName, " struct {")
1130 g.In()
1131 for _, field := range message.Field {
1132 fieldname := CamelCase(*field.Name)
1133 typename, wiretype := g.GoType(message, field)
David Symonds8935abf2011-07-04 15:53:16 +10001134 tag := "`protobuf:" + g.goTag(field, wiretype) + "`"
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001135 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001136 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001137 }
1138 if len(message.ExtensionRange) > 0 {
1139 g.P("XXX_extensions\t\tmap[int32][]byte")
1140 }
1141 g.P("XXX_unrecognized\t[]byte")
1142 g.Out()
1143 g.P("}")
1144
David Symondse37856c2011-06-22 12:52:53 +10001145 // Reset and String functions
1146 g.P("func (this *", ccTypeName, ") Reset() { *this = ", ccTypeName, "{} }")
1147 g.P("func (this *", ccTypeName, ") String() string { return ", g.ProtoPkg, ".CompactTextString(this) }")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001148
1149 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001150 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001151 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001152 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001153 // message_set_wire_format only makes sense when extensions are defined.
1154 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001155 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001156 g.P()
1157 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1158 g.In()
1159 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1160 g.Out()
1161 g.P("}")
1162 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1163 g.In()
1164 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1165 g.Out()
1166 g.P("}")
1167 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1168 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1169 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1170 }
1171
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001172 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001173 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001174 g.In()
1175 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001176 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1177 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001178 }
1179 g.Out()
1180 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001181 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001182 g.In()
1183 g.P("return extRange_", ccTypeName)
1184 g.Out()
1185 g.P("}")
1186 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1187 g.In()
1188 g.P("if this.XXX_extensions == nil {")
1189 g.In()
1190 g.P("this.XXX_extensions = make(map[int32][]byte)")
1191 g.Out()
1192 g.P("}")
1193 g.P("return this.XXX_extensions")
1194 g.Out()
1195 g.P("}")
1196 }
1197
David Symonds31d58a22011-01-20 18:33:21 +11001198 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1199
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001200 // Default constants
1201 for _, field := range message.Field {
1202 def := proto.GetString(field.DefaultValue)
1203 if def == "" {
1204 continue
1205 }
1206 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1207 typename, _ := g.GoType(message, field)
1208 if typename[0] == '*' {
1209 typename = typename[1:]
1210 }
1211 kind := "const "
1212 switch {
1213 case typename == "bool":
1214 case typename == "string":
1215 def = Quote(def)
1216 case typename == "[]byte":
1217 def = "[]byte(" + Quote(def) + ")"
1218 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001219 case def == "inf", def == "-inf", def == "nan":
1220 // These names are known to, and defined by, the protocol language.
1221 switch def {
1222 case "inf":
1223 def = "math.Inf(1)"
1224 case "-inf":
1225 def = "math.Inf(-1)"
1226 case "nan":
1227 def = "math.NaN()"
1228 }
1229 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1230 def = "float32(" + def + ")"
1231 }
1232 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001233 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1234 // Must be an enum. Need to construct the prefixed name.
1235 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1236 enum, ok := obj.(*EnumDescriptor)
1237 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001238 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001239 continue
1240 }
Rob Pike87af39e2010-07-19 10:48:02 -07001241 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001242 }
1243 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001244 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001245 }
1246 g.P()
1247
1248 for _, ext := range message.ext {
1249 g.generateExtension(ext)
1250 }
1251}
1252
1253func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001254 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001255
1256 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1257 field := ext.FieldDescriptorProto
1258 fieldType, wireType := g.GoType(ext.parent, field)
1259 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001260 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001261 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1262 // foreign extension type
1263 g.RecordTypeUse(*n)
1264 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001265
Rob Pikec9e7d972010-06-10 10:30:22 -07001266 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001267 g.In()
1268 g.P("ExtendedType: (", extendedType, ")(nil),")
1269 g.P("ExtensionType: (", fieldType, ")(nil),")
1270 g.P("Field: ", field.Number, ",")
David Symondse37856c2011-06-22 12:52:53 +10001271 g.P(`Name: "`, g.packageName, ".", *field.Name, `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001272 g.P("Tag: ", tag, ",")
1273
1274 g.Out()
1275 g.P("}")
1276 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001277
1278 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001279}
1280
1281func (g *Generator) generateInitFunction() {
1282 g.P("func init() {")
1283 g.In()
1284 for _, enum := range g.file.enum {
1285 g.generateEnumRegistration(enum)
1286 }
David Symondse37856c2011-06-22 12:52:53 +10001287 for _, d := range g.file.desc {
1288 for _, ext := range d.ext {
1289 g.generateExtensionRegistration(ext)
1290 }
1291 }
1292 for _, ext := range g.file.ext {
1293 g.generateExtensionRegistration(ext)
1294 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001295 g.Out()
1296 g.P("}")
1297}
1298
1299func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
David Symonds4decd802011-08-04 11:27:07 +10001300 // // We always print the full (proto-world) package name here.
1301 pkg := proto.GetString(enum.File().Package)
1302 if pkg != "" {
1303 pkg += "."
1304 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001305 // The full type name
1306 typeName := enum.TypeName()
1307 // The full type name, CamelCased.
1308 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001309 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001310}
1311
David Symondse37856c2011-06-22 12:52:53 +10001312func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
1313 g.P(g.ProtoPkg+".RegisterExtension(", ext.DescName(), ")")
1314}
1315
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001316// And now lots of helper functions.
1317
Rob Pike2c7bafc2010-06-10 16:07:14 -07001318// Is c an ASCII lower-case letter?
1319func isASCIILower(c byte) bool {
1320 return 'a' <= c && c <= 'z'
1321}
1322
1323// Is c an ASCII digit?
1324func isASCIIDigit(c byte) bool {
1325 return '0' <= c && c <= '9'
1326}
1327
1328// CamelCase returns the CamelCased name.
1329// If there is an interior underscore followed by a lower case letter,
1330// drop the underscore and convert the letter to upper case.
1331// There is a remote possibility of this rewrite causing a name collision,
1332// but it's so remote we're prepared to pretend it's nonexistent - since the
1333// C++ generator lowercases names, it's extremely unlikely to have two fields
1334// with different capitalizations.
1335// In short, _my_field_name_2 becomes XMyFieldName2.
1336func CamelCase(s string) string {
1337 if s == "" {
1338 return ""
1339 }
1340 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001341 i := 0
1342 if s[0] == '_' {
1343 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001344 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001345 i++
1346 }
1347 // Invariant: if the next letter is lower case, it must be converted
1348 // to upper case.
1349 // That is, we process a word at a time, where words are marked by _ or
1350 // upper case letter. Digits are treated as words.
1351 for ; i < len(s); i++ {
1352 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001353 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1354 continue // Skip the underscore in s.
1355 }
1356 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001357 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001358 continue
1359 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001360 // Assume we have a letter now - if not, it's a bogus identifier.
1361 // The next word is a sequence of characters that must start upper case.
1362 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001363 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001364 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001365 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001366 // Accept lower case sequence that follows.
1367 for i+1 < len(s) && isASCIILower(s[i+1]) {
1368 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001369 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001370 }
1371 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001372 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001373}
1374
1375// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1376// be joined with "_".
1377func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1378
1379// dottedSlice turns a sliced name into a dotted name.
1380func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1381
1382// Quote returns a Go-source quoted string representation of s.
1383func Quote(s string) string { return fmt.Sprintf("%q", s) }
1384
1385// Given a .proto file name, return the output name for the generated Go program.
1386func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001387 ext := path.Ext(name)
1388 if ext == ".proto" || ext == ".protodevel" {
1389 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001390 }
1391 return name + ".pb.go"
1392}
1393
1394// Is this field optional?
1395func isOptional(field *descriptor.FieldDescriptorProto) bool {
1396 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1397}
1398
1399// Is this field required?
1400func isRequired(field *descriptor.FieldDescriptorProto) bool {
1401 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1402}
1403
1404// Is this field repeated?
1405func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1406 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1407}
1408
1409// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001410// which can be dotted in the input .proto file. It maps dots to underscores.
1411// Because we also get here from package names generated from file names, it also maps
1412// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001413func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001414 switch rune {
1415 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001416 return '_'
1417 }
1418 return rune
1419}
Rob Pikec9e7d972010-06-10 10:30:22 -07001420
1421// BaseName returns the last path element of the name, with the last dotted suffix removed.
1422func BaseName(name string) string {
1423 // First, find the last element
1424 if i := strings.LastIndex(name, "/"); i >= 0 {
1425 name = name[i+1:]
1426 }
1427 // Now drop the suffix
1428 if i := strings.LastIndex(name, "."); i >= 0 {
1429 name = name[0:i]
1430 }
1431 return name
1432}