blob: c572df3a23379fe04b6da61e176530a0b407f932 [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
David Symondsb2a00c82011-08-04 16:52:58 +1000229 // The full list of symbols that are exported,
230 // as a map from the exported object to its symbols.
David Symonds31d58a22011-01-20 18:33:21 +1100231 // This is used for supporting public imports.
David Symondsb2a00c82011-08-04 16:52:58 +1000232 exported map[Object][]Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700233}
234
235// PackageName is the package name we'll use in the generated code to refer to this file.
236func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
237
238// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700239// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700240func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700241 // Does the file have a package clause?
David Symonds7d5c8242011-03-14 12:03:50 -0700242 if pkg := proto.GetString(d.Package); pkg != "" {
Rob Pikec9e7d972010-06-10 10:30:22 -0700243 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700244 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700245 // Use the file base name.
246 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700247}
248
David Symondsb2a00c82011-08-04 16:52:58 +1000249func (d *FileDescriptor) addExport(obj Object, symbol Symbol) {
250 d.exported[obj] = append(d.exported[obj], symbol)
David Symonds31d58a22011-01-20 18:33:21 +1100251}
252
253// Symbol is an interface representing an exported Go symbol.
254type Symbol interface {
255 // GenerateAlias should generate an appropriate alias
256 // for the symbol from the named package.
257 GenerateAlias(g *Generator, pkg string)
258}
259
260type messageSymbol struct {
261 sym string
262 hasExtensions, isMessageSet bool
263}
264
265func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
266 remoteSym := pkg + "." + ms.sym
267
268 g.P("type ", ms.sym, " ", remoteSym)
269 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
David Symondse37856c2011-06-22 12:52:53 +1000270 g.P("func (this *", ms.sym, ") String() string { return (*", remoteSym, ")(this).String() }")
David Symonds31d58a22011-01-20 18:33:21 +1100271 if ms.hasExtensions {
272 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
273 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
274 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32][]byte ",
275 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
276 if ms.isMessageSet {
277 g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
278 "{ return (*", remoteSym, ")(this).Marshal() }")
279 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
280 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
281 }
282 }
283}
284
285type enumSymbol string
286
287func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
288 s := string(es)
289 g.P("type ", s, " ", pkg, ".", s)
290 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
291 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
292 g.P("func New", s, "(x int32) *", s, " { e := ", s, "(x); return &e }")
293}
294
295type constOrVarSymbol struct {
296 sym string
297 typ string // either "const" or "var"
298}
299
300func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
301 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
302}
303
David Symonds4decd802011-08-04 11:27:07 +1000304// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700305type Object interface {
306 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
307 TypeName() []string
David Symonds4decd802011-08-04 11:27:07 +1000308 File() *descriptor.FileDescriptorProto
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700309}
310
311// Each package name we generate must be unique. The package we're generating
David Symonds4decd802011-08-04 11:27:07 +1000312// gets its own name but every other package must have a unique name that does
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700313// not conflict in the code we generate. These names are chosen globally (although
314// they don't have to be, it simplifies things to do them globally).
315func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
316 s, ok := uniquePackageName[fd]
317 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100318 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700319 }
320 return s
321}
322
323// Generator is the type whose methods generate the output, stored in the associated response structure.
324type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000325 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700326
327 Request *plugin.CodeGeneratorRequest // The input.
328 Response *plugin.CodeGeneratorResponse // The output.
329
Rob Pikec9e7d972010-06-10 10:30:22 -0700330 Param map[string]string // Command-line parameters.
331 ImportPrefix string // String to prefix to imported package file names.
332 ImportMap map[string]string // Mapping from import name to generated name
333
334 ProtoPkg string // The name under which we import the library's package proto.
335
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700336 packageName string // What we're calling ourselves.
337 allFiles []*FileDescriptor // All files in the tree
338 genFiles []*FileDescriptor // Those files we will generate output for.
339 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000340 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700341 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
342 indent string
343}
344
345// New creates a new generator and allocates the request and response protobufs.
346func New() *Generator {
347 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000348 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100349 g.Request = new(plugin.CodeGeneratorRequest)
350 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700351 return g
352}
353
354// Error reports a problem, including an os.Error, and exits the program.
355func (g *Generator) Error(err os.Error, msgs ...string) {
356 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700357 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700358 g.Response.Error = proto.String(s)
359 os.Exit(1)
360}
361
362// Fail reports a problem and exits the program.
363func (g *Generator) Fail(msgs ...string) {
364 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700365 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700366 g.Response.Error = proto.String(s)
367 os.Exit(1)
368}
369
Rob Pikec9e7d972010-06-10 10:30:22 -0700370// CommandLineParameters breaks the comma-separated list of key=value pairs
371// in the parameter (a member of the request protobuf) into a key/value map.
372// It then sets file name mappings defined by those entries.
373func (g *Generator) CommandLineParameters(parameter string) {
374 g.Param = make(map[string]string)
David Symonds8935abf2011-07-04 15:53:16 +1000375 for _, p := range strings.Split(parameter, ",") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700376 if i := strings.Index(p, "="); i < 0 {
377 g.Param[p] = ""
378 } else {
379 g.Param[p[0:i]] = p[i+1:]
380 }
381 }
382
383 g.ImportMap = make(map[string]string)
384 for k, v := range g.Param {
385 if k == "import_prefix" {
386 g.ImportPrefix = v
387 } else if len(k) > 0 && k[0] == 'M' {
388 g.ImportMap[k[1:]] = v
389 }
390 }
391}
392
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700393// DefaultPackageName returns the package name printed for the object.
394// If its file is in a different package, it returns the package name we're using for this file, plus ".".
395// Otherwise it returns the empty string.
396func (g *Generator) DefaultPackageName(obj Object) string {
397 pkg := obj.PackageName()
398 if pkg == g.packageName {
399 return ""
400 }
401 return pkg + "."
402}
403
404// For each input file, the unique package name to use, underscored.
405var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700406// Package names already registered. Key is the name from the .proto file;
407// value is the name that appears in the generated code.
408var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700409
Rob Pikec9e7d972010-06-10 10:30:22 -0700410// Create and remember a guaranteed unique package name for this file descriptor.
411// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
412// has no file descriptor.
413func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds41389812011-07-19 14:57:40 +1000414 // Convert dots to underscores before finding a unique alias.
415 pkg = strings.Map(DotToUnderscore, pkg)
416
David Symonds79eae332010-10-16 11:33:20 +1100417 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700418 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100419 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700420 }
421 // Install it.
422 pkgNamesInUse[pkg] = true
Rob Pikec9e7d972010-06-10 10:30:22 -0700423 if f != nil {
424 uniquePackageName[f.FileDescriptorProto] = pkg
425 }
426 return pkg
427}
428
429// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700430// The package name must agree across all files being generated.
431// It also defines unique package names for all imported files.
432func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700433 // Register the name for this package. It will be the first name
434 // registered so is guaranteed to be unmodified.
435 pkg := g.genFiles[0].originalPackageName()
436 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
437 // Register the proto package name. It might collide with the
438 // name of a package we import.
439 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
David Symonds7d5c8242011-03-14 12:03:50 -0700440 // Verify that we are generating output for a single package.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700441 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700442 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700443 if thisPkg != pkg {
444 g.Fail("inconsistent package names:", thisPkg, pkg)
445 }
446 }
447AllFiles:
448 for _, f := range g.allFiles {
449 for _, genf := range g.genFiles {
450 if f == genf {
451 // In this package already.
452 uniquePackageName[f.FileDescriptorProto] = g.packageName
453 continue AllFiles
454 }
455 }
David Symonds7d5c8242011-03-14 12:03:50 -0700456 // The file is a dependency, so we want to ignore its go_package option
457 // because that is only relevant for its specific generated output.
458 pkg := proto.GetString(f.Package)
459 if pkg == "" {
460 pkg = BaseName(*f.Name)
461 }
462 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700463 }
464}
465
466// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
467// and FileDescriptorProtos into file-referenced objects within the Generator.
468// It also creates the list of files to generate and so should be called before GenerateAllFiles.
469func (g *Generator) WrapTypes() {
470 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
471 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700472 // We must wrap the descriptors before we wrap the enums
473 descs := wrapDescriptors(f)
474 g.buildNestedDescriptors(descs)
475 enums := wrapEnumDescriptors(f, descs)
476 exts := wrapExtensions(f)
David Symonds4decd802011-08-04 11:27:07 +1000477 imps := wrapImported(f, g)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700478 g.allFiles[i] = &FileDescriptor{
479 FileDescriptorProto: f,
480 desc: descs,
481 enum: enums,
482 ext: exts,
David Symonds4decd802011-08-04 11:27:07 +1000483 imp: imps,
David Symondsb2a00c82011-08-04 16:52:58 +1000484 exported: make(map[Object][]Symbol),
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700485 }
486 }
487
488 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
489FindFiles:
490 for i, fileName := range g.Request.FileToGenerate {
491 // Search the list. This algorithm is n^2 but n is tiny.
492 for _, file := range g.allFiles {
493 if fileName == proto.GetString(file.Name) {
494 g.genFiles[i] = file
495 continue FindFiles
496 }
497 }
498 g.Fail("could not find file named", fileName)
499 }
500 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
501}
502
503// Scan the descriptors in this file. For each one, build the slice of nested descriptors
504func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
505 for _, desc := range descs {
506 if len(desc.NestedType) != 0 {
507 desc.nested = make([]*Descriptor, len(desc.NestedType))
508 n := 0
509 for _, nest := range descs {
510 if nest.parent == desc {
511 desc.nested[n] = nest
512 n++
513 }
514 }
515 if n != len(desc.NestedType) {
516 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
517 }
518 }
519 }
520}
521
522// Construct the Descriptor and add it to the slice
523func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
David Symonds4decd802011-08-04 11:27:07 +1000524 d := &Descriptor{common{file}, desc, parent, nil, nil, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700525
526 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
527 for i, field := range desc.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000528 d.ext[i] = &ExtensionDescriptor{common{file}, field, d}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700529 }
530
David Symondscc7142e2010-11-06 14:37:15 +1100531 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700532}
533
534// Return a slice of all the Descriptors defined within this file
535func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
536 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
537 for _, desc := range file.MessageType {
538 sl = wrapThisDescriptor(sl, desc, nil, file)
539 }
540 return sl
541}
542
543// Wrap this Descriptor, recursively
544func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
545 sl = addDescriptor(sl, desc, parent, file)
546 me := sl[len(sl)-1]
547 for _, nested := range desc.NestedType {
548 sl = wrapThisDescriptor(sl, nested, me, file)
549 }
550 return sl
551}
552
553// Construct the EnumDescriptor and add it to the slice
554func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symonds4decd802011-08-04 11:27:07 +1000555 return append(sl, &EnumDescriptor{common{file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700556}
557
558// Return a slice of all the EnumDescriptors defined within this file
559func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
560 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000561 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700562 for _, enum := range file.EnumType {
563 sl = addEnumDescriptor(sl, enum, nil, file)
564 }
David Symonds5256cf62010-06-27 10:33:42 +1000565 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700566 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000567 for _, enum := range nested.EnumType {
568 sl = addEnumDescriptor(sl, enum, nested, file)
569 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700570 }
571 return sl
572}
573
574// Return a slice of all the top-level ExtensionDescriptors defined within this file.
575func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
576 sl := make([]*ExtensionDescriptor, len(file.Extension))
577 for i, field := range file.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000578 sl[i] = &ExtensionDescriptor{common{file}, field, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700579 }
580 return sl
581}
582
David Symonds4decd802011-08-04 11:27:07 +1000583// Return a slice of all the types that are publicly imported into this file.
584func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) {
585 for _, index := range file.PublicDependency {
586 df := g.fileByName(file.Dependency[index])
587 for _, d := range df.desc {
588 sl = append(sl, &ImportedDescriptor{common{file}, d})
589 }
590 for _, e := range df.enum {
591 sl = append(sl, &ImportedDescriptor{common{file}, e})
592 }
593 for _, ext := range df.ext {
594 sl = append(sl, &ImportedDescriptor{common{file}, ext})
595 }
596 }
597 return
598}
599
Rob Pikec9e7d972010-06-10 10:30:22 -0700600// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700601// The key names for the map come from the input data, which puts a period at the beginning.
602// It should be called after SetPackageNames and before GenerateAllFiles.
603func (g *Generator) BuildTypeNameMap() {
604 g.typeNameToObject = make(map[string]Object)
605 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700606 // The names in this loop are defined by the proto world, not us, so the
607 // package name may be empty. If so, the dotted package name of X will
608 // be ".X"; otherwise it will be ".pkg.X".
609 dottedPkg := "." + proto.GetString(f.Package)
610 if dottedPkg != "." {
611 dottedPkg += "."
612 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700613 for _, enum := range f.enum {
614 name := dottedPkg + dottedSlice(enum.TypeName())
615 g.typeNameToObject[name] = enum
616 }
617 for _, desc := range f.desc {
618 name := dottedPkg + dottedSlice(desc.TypeName())
619 g.typeNameToObject[name] = desc
620 }
621 }
622}
623
624// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
625// returns the descriptor for the message or enum with that name.
626func (g *Generator) ObjectNamed(typeName string) Object {
David Symonds4decd802011-08-04 11:27:07 +1000627 o, ok := g.typeNameToObject[typeName]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700628 if !ok {
629 g.Fail("can't find object with type", typeName)
630 }
David Symonds4decd802011-08-04 11:27:07 +1000631
632 // If the file of this object isn't a direct dependency of the current file,
633 // or in the current file, then this object has been publicly imported into
634 // a dependency of the current file.
635 // We should return the ImportedDescriptor object for it instead.
636 direct := *o.File().Name == *g.file.Name
637 if !direct {
638 for _, dep := range g.file.Dependency {
639 if *g.fileByName(dep).Name == *o.File().Name {
640 direct = true
641 break
642 }
643 }
644 }
645 if !direct {
646 found := false
647 Loop:
648 for _, dep := range g.file.Dependency {
649 df := g.fileByName(*g.fileByName(dep).Name)
650 for _, td := range df.imp {
651 if td.o == o {
652 // Found it!
653 o = td
654 found = true
655 break Loop
656 }
657 }
658 }
659 if !found {
660 log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name)
661 }
662 }
663
664 return o
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700665}
666
667// P prints the arguments to the generated output. It handles strings and int32s, plus
668// handling indirections because they may be *string, etc.
669func (g *Generator) P(str ...interface{}) {
670 g.WriteString(g.indent)
671 for _, v := range str {
672 switch s := v.(type) {
673 case string:
674 g.WriteString(s)
675 case *string:
676 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700677 case bool:
678 g.WriteString(fmt.Sprintf("%t", s))
679 case *bool:
680 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700681 case *int32:
682 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700683 case float64:
684 g.WriteString(fmt.Sprintf("%g", s))
685 case *float64:
686 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700687 default:
688 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
689 }
690 }
691 g.WriteByte('\n')
692}
693
694// In Indents the output one tab stop.
695func (g *Generator) In() { g.indent += "\t" }
696
697// Out unindents the output one tab stop.
698func (g *Generator) Out() {
699 if len(g.indent) > 0 {
700 g.indent = g.indent[1:]
701 }
702}
703
704// GenerateAllFiles generates the output for all the files we're outputting.
705func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700706 // Initialize the plugins
707 for _, p := range plugins {
708 p.Init(g)
709 }
David Symonds31d58a22011-01-20 18:33:21 +1100710 // Generate the output. The generator runs for every file, even the files
711 // that we don't generate output for, so that we can collate the full list
712 // of exported symbols to support public imports.
713 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
714 for _, file := range g.genFiles {
715 genFileMap[file] = true
716 }
717 i := 0
718 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700719 g.Reset()
720 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100721 if _, ok := genFileMap[file]; !ok {
722 continue
723 }
David Symondsb0127532010-11-09 11:10:46 +1100724 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700725 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
726 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100727 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700728 }
729}
730
731// Run all the plugins associated with the file.
732func (g *Generator) runPlugins(file *FileDescriptor) {
733 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700734 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700735 }
736}
737
738
739// FileOf return the FileDescriptor for this FileDescriptorProto.
740func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
741 for _, file := range g.allFiles {
742 if file.FileDescriptorProto == fd {
743 return file
744 }
745 }
746 g.Fail("could not find file in table:", proto.GetString(fd.Name))
747 return nil
748}
749
750// Fill the response protocol buffer with the generated output for all the files we're
751// supposed to generate.
752func (g *Generator) generate(file *FileDescriptor) {
753 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000754 g.usedPackages = make(map[string]bool)
755
David Symonds4decd802011-08-04 11:27:07 +1000756 for _, td := range g.file.imp {
757 g.generateImported(td)
758 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700759 for _, enum := range g.file.enum {
760 g.generateEnum(enum)
761 }
762 for _, desc := range g.file.desc {
763 g.generateMessage(desc)
764 }
765 for _, ext := range g.file.ext {
766 g.generateExtension(ext)
767 }
768 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000769
Rob Pikec9e7d972010-06-10 10:30:22 -0700770 // Run the plugins before the imports so we know which imports are necessary.
771 g.runPlugins(file)
772
David Symondsf90e3382010-05-05 10:53:44 +1000773 // Generate header and imports last, though they appear first in the output.
774 rem := g.Buffer
775 g.Buffer = new(bytes.Buffer)
776 g.generateHeader()
777 g.generateImports()
778 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +1000779
780 // Reformat generated code.
781 fset := token.NewFileSet()
782 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
783 if err != nil {
784 g.Fail("bad Go source code was generated:", err.String())
785 return
786 }
787 g.Reset()
David Symonds2ed81c42011-08-12 17:06:51 +1000788 _, err = (&printer.Config{printer.TabIndent | printer.UseSpaces, 8}).Fprint(g, fset, ast)
David Symondsb1d55a02011-04-08 09:55:06 +1000789 if err != nil {
790 g.Fail("generated Go source code could not be reformatted:", err.String())
791 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700792}
793
794// Generate the header, including package definition and imports
795func (g *Generator) generateHeader() {
796 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
797 g.P("// DO NOT EDIT!")
798 g.P()
799 g.P("package ", g.file.PackageName())
800 g.P()
801}
802
David Symonds31d58a22011-01-20 18:33:21 +1100803func (g *Generator) fileByName(filename string) *FileDescriptor {
804 for _, fd := range g.allFiles {
805 if proto.GetString(fd.Name) == filename {
806 return fd
807 }
808 }
809 return nil
810}
811
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700812// Generate the header, including package definition and imports
813func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700814 // We almost always need a proto import. Rather than computing when we
815 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100816 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700817 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100818 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100819 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700820 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100821 fd := g.fileByName(s)
822 // Do not import our own package.
823 if fd.PackageName() == g.packageName {
824 continue
825 }
826 filename := goFileName(s)
827 if substitution, ok := g.ImportMap[s]; ok {
828 filename = substitution
829 }
830 filename = g.ImportPrefix + filename
831 if strings.HasSuffix(filename, ".go") {
832 filename = filename[0 : len(filename)-3]
833 }
834 if _, ok := g.usedPackages[fd.PackageName()]; ok {
835 g.P("import ", fd.PackageName(), " ", Quote(filename))
836 } else {
David Symonds3fa055f2011-05-05 15:19:04 -0700837 // TODO: Re-enable this when we are more feature-complete.
838 // For instance, some protos use foreign field extensions, which we don't support.
839 // Until then, this is just annoying spam.
840 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
David Symonds4decd802011-08-04 11:27:07 +1000841 g.P("// discarding unused import ", fd.PackageName(), " ", Quote(filename))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700842 }
843 }
844 g.P()
845 // TODO: may need to worry about uniqueness across plugins
846 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700847 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700848 g.P()
849 }
David Symondscea785b2011-01-07 11:02:30 +1100850 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700851 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100852 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100853 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700854 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700855}
856
David Symonds4decd802011-08-04 11:27:07 +1000857func (g *Generator) generateImported(id *ImportedDescriptor) {
David Symondsb2a00c82011-08-04 16:52:58 +1000858 // Don't generate public import symbols for files that we are generating
859 // code for, since those symbols will already be in this package.
860 // We can't simply avoid creating the ImportedDescriptor objects,
861 // because g.genFiles isn't populated at that stage.
David Symonds4decd802011-08-04 11:27:07 +1000862 tn := id.TypeName()
863 sn := tn[len(tn)-1]
David Symondsb2a00c82011-08-04 16:52:58 +1000864 df := g.FileOf(id.o.File())
865 filename := *df.Name
866 for _, fd := range g.genFiles {
867 if *fd.Name == filename {
868 g.P("// Ignoring public import of ", sn, " from ", filename)
869 g.P()
870 return
871 }
872 }
873 g.P("// ", sn, " from public import ", filename)
874 g.usedPackages[df.PackageName()] = true
David Symonds4decd802011-08-04 11:27:07 +1000875
David Symondsb2a00c82011-08-04 16:52:58 +1000876 for _, sym := range df.exported[id.o] {
877 sym.GenerateAlias(g, df.PackageName())
878 }
879
880 g.P()
David Symonds4decd802011-08-04 11:27:07 +1000881}
882
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700883// Generate the enum definitions for this EnumDescriptor.
884func (g *Generator) generateEnum(enum *EnumDescriptor) {
885 // The full type name
886 typeName := enum.TypeName()
887 // The full type name, CamelCased.
888 ccTypeName := CamelCaseSlice(typeName)
889 ccPrefix := enum.prefix()
890 g.P("type ", ccTypeName, " int32")
David Symondsb2a00c82011-08-04 16:52:58 +1000891 g.file.addExport(enum, enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700892 g.P("const (")
893 g.In()
894 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100895 name := ccPrefix + *e.Name
896 g.P(name, " = ", e.Number)
David Symondsb2a00c82011-08-04 16:52:58 +1000897 g.file.addExport(enum, constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700898 }
899 g.Out()
900 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100901 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700902 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700903 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700904 for _, e := range enum.Value {
905 duplicate := ""
906 if _, present := generated[*e.Number]; present {
907 duplicate = "// Duplicate value: "
908 }
909 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
910 generated[*e.Number] = true
911 }
912 g.Out()
913 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100914 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700915 g.In()
916 for _, e := range enum.Value {
917 g.P(Quote(*e.Name), ": ", e.Number, ",")
918 }
919 g.Out()
920 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100921
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700922 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
923 g.In()
924 g.P("e := ", ccTypeName, "(x)")
925 g.P("return &e")
926 g.Out()
927 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100928
929 g.P("func (x ", ccTypeName, ") String() string {")
930 g.In()
931 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
932 g.Out()
933 g.P("}")
934
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700935 g.P()
936}
937
David Symonds8935abf2011-07-04 15:53:16 +1000938// The tag is a string like "varint,2,opt,name=fieldname,def=7" that
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700939// identifies details of the field for the protocol buffer marshaling and unmarshaling
940// code. The fields are:
941// wire encoding
942// protocol tag number
943// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100944// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700945// name= the original declared name
946// enum= the name of the enum type if it is an enum-typed field.
947// def= string representation of the default value, if any.
948// The default value must be in a representation that can be used at run-time
949// to generate the default value. Thus bools become 0 and 1, for instance.
950func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
951 optrepreq := ""
952 switch {
953 case isOptional(field):
954 optrepreq = "opt"
955 case isRequired(field):
956 optrepreq = "req"
957 case isRepeated(field):
958 optrepreq = "rep"
959 }
960 defaultValue := proto.GetString(field.DefaultValue)
961 if defaultValue != "" {
962 switch *field.Type {
963 case descriptor.FieldDescriptorProto_TYPE_BOOL:
964 if defaultValue == "true" {
965 defaultValue = "1"
966 } else {
967 defaultValue = "0"
968 }
969 case descriptor.FieldDescriptorProto_TYPE_STRING,
970 descriptor.FieldDescriptorProto_TYPE_BYTES:
971 // Protect frogs.
972 defaultValue = Quote(defaultValue)
973 // Don't need the quotes
974 defaultValue = defaultValue[1 : len(defaultValue)-1]
975 case descriptor.FieldDescriptorProto_TYPE_ENUM:
976 // For enums we need to provide the integer constant.
977 obj := g.ObjectNamed(proto.GetString(field.TypeName))
978 enum, ok := obj.(*EnumDescriptor)
979 if !ok {
980 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
981 }
982 defaultValue = enum.integerValueAsString(defaultValue)
983 }
984 defaultValue = ",def=" + defaultValue
985 }
986 enum := ""
987 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
David Symonds4decd802011-08-04 11:27:07 +1000988 // We avoid using obj.PackageName(), because we want to use the
989 // original (proto-world) package name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700990 obj := g.ObjectNamed(proto.GetString(field.TypeName))
David Symonds4decd802011-08-04 11:27:07 +1000991 enum = ",enum="
992 if pkg := proto.GetString(obj.File().Package); pkg != "" {
993 enum += pkg + "."
994 }
995 enum += CamelCaseSlice(obj.TypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700996 }
David Symonds5b7775e2010-12-01 10:09:04 +1100997 packed := ""
998 if field.Options != nil && proto.GetBool(field.Options.Packed) {
999 packed = ",packed"
1000 }
David Symondse37856c2011-06-22 12:52:53 +10001001 fieldName := proto.GetString(field.Name)
1002 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +10001003 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
1004 // We must use the type name for groups instead of
1005 // the field name to preserve capitalization.
1006 // type_name in FieldDescriptorProto is fully-qualified,
1007 // but we only want the local part.
1008 name = *field.TypeName
1009 if i := strings.LastIndex(name, "."); i >= 0 {
1010 name = name[i+1:]
1011 }
David Symondse37856c2011-06-22 12:52:53 +10001012 }
1013 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001014 name = ""
1015 } else {
1016 name = ",name=" + name
1017 }
David Symonds8935abf2011-07-04 15:53:16 +10001018 return Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001019 wiretype,
1020 proto.GetInt32(field.Number),
1021 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +11001022 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001023 name,
1024 enum,
1025 defaultValue))
1026}
1027
1028func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
1029 switch typ {
1030 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1031 return false
1032 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1033 return false
1034 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1035 return false
1036 }
1037 return true
1038}
1039
1040// TypeName is the printed name appropriate for an item. If the object is in the current file,
1041// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -07001042// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001043// package name followed by the item name.
1044// The result always has an initial capital.
1045func (g *Generator) TypeName(obj Object) string {
1046 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
1047}
1048
1049// TypeNameWithPackage is like TypeName, but always includes the package
1050// name even if the object is in our own package.
1051func (g *Generator) TypeNameWithPackage(obj Object) string {
1052 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
1053}
1054
1055// GoType returns a string representing the type name, and the wire type
1056func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
1057 // TODO: Options.
1058 switch *field.Type {
1059 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
1060 typ, wire = "float64", "fixed64"
1061 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
1062 typ, wire = "float32", "fixed32"
1063 case descriptor.FieldDescriptorProto_TYPE_INT64:
1064 typ, wire = "int64", "varint"
1065 case descriptor.FieldDescriptorProto_TYPE_UINT64:
1066 typ, wire = "uint64", "varint"
1067 case descriptor.FieldDescriptorProto_TYPE_INT32:
1068 typ, wire = "int32", "varint"
1069 case descriptor.FieldDescriptorProto_TYPE_UINT32:
1070 typ, wire = "uint32", "varint"
1071 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
1072 typ, wire = "uint64", "fixed64"
1073 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
1074 typ, wire = "uint32", "fixed32"
1075 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1076 typ, wire = "bool", "varint"
1077 case descriptor.FieldDescriptorProto_TYPE_STRING:
1078 typ, wire = "string", "bytes"
1079 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1080 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1081 typ, wire = "*"+g.TypeName(desc), "group"
1082 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1083 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1084 typ, wire = "*"+g.TypeName(desc), "bytes"
1085 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1086 typ, wire = "[]byte", "bytes"
1087 case descriptor.FieldDescriptorProto_TYPE_ENUM:
1088 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1089 typ, wire = g.TypeName(desc), "varint"
1090 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
1091 typ, wire = "int32", "fixed32"
1092 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
1093 typ, wire = "int64", "fixed64"
1094 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1095 typ, wire = "int32", "zigzag32"
1096 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1097 typ, wire = "int64", "zigzag64"
1098 default:
1099 g.Fail("unknown type for", proto.GetString(field.Name))
1100 }
1101 if isRepeated(field) {
1102 typ = "[]" + typ
1103 } else if needsStar(*field.Type) {
1104 typ = "*" + typ
1105 }
1106 return
1107}
1108
David Symondsf90e3382010-05-05 10:53:44 +10001109func (g *Generator) RecordTypeUse(t string) {
1110 if obj, ok := g.typeNameToObject[t]; ok {
David Symonds4decd802011-08-04 11:27:07 +10001111 // Call ObjectNamed to get the true object to record the use.
1112 obj = g.ObjectNamed(t)
David Symondsf90e3382010-05-05 10:53:44 +10001113 g.usedPackages[obj.PackageName()] = true
1114 }
1115}
1116
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001117// Generate the type and default constant definitions for this Descriptor.
1118func (g *Generator) generateMessage(message *Descriptor) {
1119 // The full type name
1120 typeName := message.TypeName()
1121 // The full type name, CamelCased.
1122 ccTypeName := CamelCaseSlice(typeName)
1123
1124 g.P("type ", ccTypeName, " struct {")
1125 g.In()
1126 for _, field := range message.Field {
1127 fieldname := CamelCase(*field.Name)
1128 typename, wiretype := g.GoType(message, field)
David Symondsb2a00c82011-08-04 16:52:58 +10001129 jsonName := *field.Name
1130 tag := fmt.Sprintf("`protobuf:%s json:%q`", g.goTag(field, wiretype), jsonName)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001131 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001132 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001133 }
1134 if len(message.ExtensionRange) > 0 {
1135 g.P("XXX_extensions\t\tmap[int32][]byte")
1136 }
1137 g.P("XXX_unrecognized\t[]byte")
1138 g.Out()
1139 g.P("}")
1140
David Symondse37856c2011-06-22 12:52:53 +10001141 // Reset and String functions
1142 g.P("func (this *", ccTypeName, ") Reset() { *this = ", ccTypeName, "{} }")
1143 g.P("func (this *", ccTypeName, ") String() string { return ", g.ProtoPkg, ".CompactTextString(this) }")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001144
1145 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001146 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001147 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001148 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001149 // message_set_wire_format only makes sense when extensions are defined.
1150 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001151 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001152 g.P()
1153 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1154 g.In()
1155 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1156 g.Out()
1157 g.P("}")
1158 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1159 g.In()
1160 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1161 g.Out()
1162 g.P("}")
1163 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1164 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1165 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1166 }
1167
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001168 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001169 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001170 g.In()
1171 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001172 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1173 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001174 }
1175 g.Out()
1176 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001177 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001178 g.In()
1179 g.P("return extRange_", ccTypeName)
1180 g.Out()
1181 g.P("}")
1182 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1183 g.In()
1184 g.P("if this.XXX_extensions == nil {")
1185 g.In()
1186 g.P("this.XXX_extensions = make(map[int32][]byte)")
1187 g.Out()
1188 g.P("}")
1189 g.P("return this.XXX_extensions")
1190 g.Out()
1191 g.P("}")
1192 }
1193
David Symondsb2a00c82011-08-04 16:52:58 +10001194 g.file.addExport(message, messageSymbol{ccTypeName, hasExtensions, isMessageSet})
David Symonds31d58a22011-01-20 18:33:21 +11001195
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001196 // Default constants
1197 for _, field := range message.Field {
1198 def := proto.GetString(field.DefaultValue)
1199 if def == "" {
1200 continue
1201 }
1202 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1203 typename, _ := g.GoType(message, field)
1204 if typename[0] == '*' {
1205 typename = typename[1:]
1206 }
1207 kind := "const "
1208 switch {
1209 case typename == "bool":
1210 case typename == "string":
1211 def = Quote(def)
1212 case typename == "[]byte":
1213 def = "[]byte(" + Quote(def) + ")"
1214 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001215 case def == "inf", def == "-inf", def == "nan":
1216 // These names are known to, and defined by, the protocol language.
1217 switch def {
1218 case "inf":
1219 def = "math.Inf(1)"
1220 case "-inf":
1221 def = "math.Inf(-1)"
1222 case "nan":
1223 def = "math.NaN()"
1224 }
1225 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1226 def = "float32(" + def + ")"
1227 }
1228 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001229 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1230 // Must be an enum. Need to construct the prefixed name.
1231 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1232 enum, ok := obj.(*EnumDescriptor)
1233 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001234 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001235 continue
1236 }
Rob Pike87af39e2010-07-19 10:48:02 -07001237 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001238 }
1239 g.P(kind, fieldname, " ", typename, " = ", def)
David Symondsb2a00c82011-08-04 16:52:58 +10001240 g.file.addExport(message, constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001241 }
1242 g.P()
1243
1244 for _, ext := range message.ext {
1245 g.generateExtension(ext)
1246 }
1247}
1248
1249func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001250 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001251
1252 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1253 field := ext.FieldDescriptorProto
1254 fieldType, wireType := g.GoType(ext.parent, field)
1255 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001256 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001257 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1258 // foreign extension type
1259 g.RecordTypeUse(*n)
1260 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001261
Rob Pikec9e7d972010-06-10 10:30:22 -07001262 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001263 g.In()
1264 g.P("ExtendedType: (", extendedType, ")(nil),")
1265 g.P("ExtensionType: (", fieldType, ")(nil),")
1266 g.P("Field: ", field.Number, ",")
David Symondse37856c2011-06-22 12:52:53 +10001267 g.P(`Name: "`, g.packageName, ".", *field.Name, `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001268 g.P("Tag: ", tag, ",")
1269
1270 g.Out()
1271 g.P("}")
1272 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001273
David Symondsb2a00c82011-08-04 16:52:58 +10001274 g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001275}
1276
1277func (g *Generator) generateInitFunction() {
1278 g.P("func init() {")
1279 g.In()
1280 for _, enum := range g.file.enum {
1281 g.generateEnumRegistration(enum)
1282 }
David Symondse37856c2011-06-22 12:52:53 +10001283 for _, d := range g.file.desc {
1284 for _, ext := range d.ext {
1285 g.generateExtensionRegistration(ext)
1286 }
1287 }
1288 for _, ext := range g.file.ext {
1289 g.generateExtensionRegistration(ext)
1290 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001291 g.Out()
1292 g.P("}")
1293}
1294
1295func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
David Symonds4decd802011-08-04 11:27:07 +10001296 // // We always print the full (proto-world) package name here.
1297 pkg := proto.GetString(enum.File().Package)
1298 if pkg != "" {
1299 pkg += "."
1300 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001301 // The full type name
1302 typeName := enum.TypeName()
1303 // The full type name, CamelCased.
1304 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001305 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001306}
1307
David Symondse37856c2011-06-22 12:52:53 +10001308func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
1309 g.P(g.ProtoPkg+".RegisterExtension(", ext.DescName(), ")")
1310}
1311
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001312// And now lots of helper functions.
1313
Rob Pike2c7bafc2010-06-10 16:07:14 -07001314// Is c an ASCII lower-case letter?
1315func isASCIILower(c byte) bool {
1316 return 'a' <= c && c <= 'z'
1317}
1318
1319// Is c an ASCII digit?
1320func isASCIIDigit(c byte) bool {
1321 return '0' <= c && c <= '9'
1322}
1323
1324// CamelCase returns the CamelCased name.
1325// If there is an interior underscore followed by a lower case letter,
1326// drop the underscore and convert the letter to upper case.
1327// There is a remote possibility of this rewrite causing a name collision,
1328// but it's so remote we're prepared to pretend it's nonexistent - since the
1329// C++ generator lowercases names, it's extremely unlikely to have two fields
1330// with different capitalizations.
1331// In short, _my_field_name_2 becomes XMyFieldName2.
1332func CamelCase(s string) string {
1333 if s == "" {
1334 return ""
1335 }
1336 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001337 i := 0
1338 if s[0] == '_' {
1339 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001340 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001341 i++
1342 }
1343 // Invariant: if the next letter is lower case, it must be converted
1344 // to upper case.
1345 // That is, we process a word at a time, where words are marked by _ or
1346 // upper case letter. Digits are treated as words.
1347 for ; i < len(s); i++ {
1348 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001349 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1350 continue // Skip the underscore in s.
1351 }
1352 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001353 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001354 continue
1355 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001356 // Assume we have a letter now - if not, it's a bogus identifier.
1357 // The next word is a sequence of characters that must start upper case.
1358 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001359 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001360 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001361 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001362 // Accept lower case sequence that follows.
1363 for i+1 < len(s) && isASCIILower(s[i+1]) {
1364 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001365 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001366 }
1367 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001368 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001369}
1370
1371// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1372// be joined with "_".
1373func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1374
1375// dottedSlice turns a sliced name into a dotted name.
1376func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1377
1378// Quote returns a Go-source quoted string representation of s.
1379func Quote(s string) string { return fmt.Sprintf("%q", s) }
1380
1381// Given a .proto file name, return the output name for the generated Go program.
1382func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001383 ext := path.Ext(name)
1384 if ext == ".proto" || ext == ".protodevel" {
1385 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001386 }
1387 return name + ".pb.go"
1388}
1389
1390// Is this field optional?
1391func isOptional(field *descriptor.FieldDescriptorProto) bool {
1392 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1393}
1394
1395// Is this field required?
1396func isRequired(field *descriptor.FieldDescriptorProto) bool {
1397 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1398}
1399
1400// Is this field repeated?
1401func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1402 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1403}
1404
1405// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001406// which can be dotted in the input .proto file. It maps dots to underscores.
1407// Because we also get here from package names generated from file names, it also maps
1408// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001409func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001410 switch rune {
1411 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001412 return '_'
1413 }
1414 return rune
1415}
Rob Pikec9e7d972010-06-10 10:30:22 -07001416
1417// BaseName returns the last path element of the name, with the last dotted suffix removed.
1418func BaseName(name string) string {
1419 // First, find the last element
1420 if i := strings.LastIndex(name, "/"); i >= 0 {
1421 name = name[i+1:]
1422 }
1423 // Now drop the suffix
1424 if i := strings.LastIndex(name, "."); i >= 0 {
1425 name = name[0:i]
1426 }
1427 return name
1428}