blob: f4f4eddaf4f4ba2317c0bf89e71ae84001d541a2 [file] [log] [blame]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32/*
33 The code generator for the plugin for the Google protocol buffer compiler.
34 It generates Go code from the protocol buffer description files read by the
35 main routine.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070036*/
37package generator
38
39import (
40 "bytes"
41 "fmt"
David Symondsb1d55a02011-04-08 09:55:06 +100042 "go/parser"
43 "go/printer"
44 "go/token"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070045 "log"
46 "os"
Rob Pike87af39e2010-07-19 10:48:02 -070047 "path"
David Symonds79eae332010-10-16 11:33:20 +110048 "strconv"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070049 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070050
51 "goprotobuf.googlecode.com/hg/proto"
David Symonds9f402812011-04-28 18:08:44 +100052 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070053 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
54)
55
56// A Plugin provides functionality to add to the output during Go code generation,
57// such as to produce RPC stubs.
58type Plugin interface {
59 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070060 Name() string
61 // Init is called once after data structures are built but before
62 // code generation begins.
63 Init(g *Generator)
64 // Generate produces the code generated by the plugin for this file,
65 // except for the imports, by calling the generator's methods P, In, and Out.
66 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070067 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070068 // It is called after Generate.
69 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070070}
71
72var plugins []Plugin
73
74// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
75// It is typically called during initialization.
76func RegisterPlugin(p Plugin) {
David Symondscc7142e2010-11-06 14:37:15 +110077 plugins = append(plugins, p)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070078}
79
80// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
81// a pointer to the FileDescriptorProto that represents it. These types achieve that
82// wrapping by placing each Proto inside a struct with the pointer to its File. The
83// structs have the same names as their contents, with "Proto" removed.
84// FileDescriptor is used to store the things that it points to.
85
86// The file and package name method are common to messages and enums.
87type common struct {
88 File *descriptor.FileDescriptorProto // File this object comes from.
89}
90
91// PackageName is name in the package clause in the generated file.
92func (c *common) PackageName() string { return uniquePackageOf(c.File) }
93
94// Descriptor represents a protocol buffer message.
95type Descriptor struct {
96 common
97 *descriptor.DescriptorProto
98 parent *Descriptor // The containing message, if any.
99 nested []*Descriptor // Inner messages, if any.
100 ext []*ExtensionDescriptor // Extensions, if any.
101 typename []string // Cached typename vector.
102}
103
104// TypeName returns the elements of the dotted type name.
105// The package name is not part of this name.
106func (d *Descriptor) TypeName() []string {
107 if d.typename != nil {
108 return d.typename
109 }
110 n := 0
111 for parent := d; parent != nil; parent = parent.parent {
112 n++
113 }
114 s := make([]string, n, n)
115 for parent := d; parent != nil; parent = parent.parent {
116 n--
117 s[n] = proto.GetString(parent.Name)
118 }
119 d.typename = s
120 return s
121}
122
123// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
124// Otherwise it will be the descriptor of the message in which it is defined.
125type EnumDescriptor struct {
126 common
127 *descriptor.EnumDescriptorProto
128 parent *Descriptor // The containing message, if any.
129 typename []string // Cached typename vector.
130}
131
132// TypeName returns the elements of the dotted type name.
133// The package name is not part of this name.
134func (e *EnumDescriptor) TypeName() (s []string) {
135 if e.typename != nil {
136 return e.typename
137 }
138 name := proto.GetString(e.Name)
139 if e.parent == nil {
140 s = make([]string, 1)
141 } else {
142 pname := e.parent.TypeName()
143 s = make([]string, len(pname)+1)
144 copy(s, pname)
145 }
146 s[len(s)-1] = name
147 e.typename = s
148 return s
149}
150
151// Everything but the last element of the full type name, CamelCased.
152// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
153func (e *EnumDescriptor) prefix() string {
154 typeName := e.TypeName()
155 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
156 if e.parent == nil {
157 // If the enum is not part of a message, the prefix is just the type name.
158 ccPrefix = CamelCase(*e.Name) + "_"
159 }
160 return ccPrefix
161}
162
163// The integer value of the named constant in this enumerated type.
164func (e *EnumDescriptor) integerValueAsString(name string) string {
165 for _, c := range e.Value {
166 if proto.GetString(c.Name) == name {
167 return fmt.Sprint(proto.GetInt32(c.Number))
168 }
169 }
David Symonds9d0000e2011-02-03 10:48:14 +1100170 log.Fatal("cannot find value for enum constant")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700171 return ""
172}
173
174// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
175// Otherwise it will be the descriptor of the message in which it is defined.
176type ExtensionDescriptor struct {
177 common
178 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700179 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700180}
181
182// TypeName returns the elements of the dotted type name.
183// The package name is not part of this name.
184func (e *ExtensionDescriptor) TypeName() (s []string) {
185 name := proto.GetString(e.Name)
186 if e.parent == nil {
187 // top-level extension
188 s = make([]string, 1)
189 } else {
190 pname := e.parent.TypeName()
191 s = make([]string, len(pname)+1)
192 copy(s, pname)
193 }
194 s[len(s)-1] = name
195 return s
196}
197
David Symondse37856c2011-06-22 12:52:53 +1000198// DescName returns the variable name used for the generated descriptor.
199func (e *ExtensionDescriptor) DescName() string {
200 // The full type name.
201 typeName := e.TypeName()
202 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
203 for i, s := range typeName {
204 typeName[i] = CamelCase(s)
205 }
206 return "E_" + strings.Join(typeName, "_")
207}
208
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700209// FileDescriptor describes an protocol buffer descriptor file (.proto).
210// It includes slices of all the messages and enums defined within it.
211// Those slices are constructed by WrapTypes.
212type FileDescriptor struct {
213 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700214 desc []*Descriptor // All the messages defined in this file.
215 enum []*EnumDescriptor // All the enums defined in this file.
216 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds31d58a22011-01-20 18:33:21 +1100217
218 // The full list of symbols that are exported.
219 // This is used for supporting public imports.
220 exported []Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700221}
222
223// PackageName is the package name we'll use in the generated code to refer to this file.
224func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
225
226// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700227// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700228func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700229 // Does the file have a package clause?
David Symonds7d5c8242011-03-14 12:03:50 -0700230 if pkg := proto.GetString(d.Package); pkg != "" {
Rob Pikec9e7d972010-06-10 10:30:22 -0700231 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700232 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700233 // Use the file base name.
234 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700235}
236
David Symonds31d58a22011-01-20 18:33:21 +1100237func (d *FileDescriptor) addExport(symbol Symbol) {
238 d.exported = append(d.exported, symbol)
239}
240
241// Symbol is an interface representing an exported Go symbol.
242type Symbol interface {
243 // GenerateAlias should generate an appropriate alias
244 // for the symbol from the named package.
245 GenerateAlias(g *Generator, pkg string)
246}
247
248type messageSymbol struct {
249 sym string
250 hasExtensions, isMessageSet bool
251}
252
253func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
254 remoteSym := pkg + "." + ms.sym
255
256 g.P("type ", ms.sym, " ", remoteSym)
257 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
David Symondse37856c2011-06-22 12:52:53 +1000258 g.P("func (this *", ms.sym, ") String() string { return (*", remoteSym, ")(this).String() }")
David Symonds31d58a22011-01-20 18:33:21 +1100259 if ms.hasExtensions {
260 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
261 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
262 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32][]byte ",
263 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
264 if ms.isMessageSet {
265 g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
266 "{ return (*", remoteSym, ")(this).Marshal() }")
267 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
268 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
269 }
270 }
271}
272
273type enumSymbol string
274
275func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
276 s := string(es)
277 g.P("type ", s, " ", pkg, ".", s)
278 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
279 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
280 g.P("func New", s, "(x int32) *", s, " { e := ", s, "(x); return &e }")
281}
282
283type constOrVarSymbol struct {
284 sym string
285 typ string // either "const" or "var"
286}
287
288func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
289 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
290}
291
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700292// Object is an interface abstracting the abilities shared by enums and messages.
293type Object interface {
294 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
295 TypeName() []string
296}
297
298// Each package name we generate must be unique. The package we're generating
299// gets its own name but every other package must have a unqiue name that does
300// not conflict in the code we generate. These names are chosen globally (although
301// they don't have to be, it simplifies things to do them globally).
302func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
303 s, ok := uniquePackageName[fd]
304 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100305 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700306 }
307 return s
308}
309
310// Generator is the type whose methods generate the output, stored in the associated response structure.
311type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000312 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700313
314 Request *plugin.CodeGeneratorRequest // The input.
315 Response *plugin.CodeGeneratorResponse // The output.
316
Rob Pikec9e7d972010-06-10 10:30:22 -0700317 Param map[string]string // Command-line parameters.
318 ImportPrefix string // String to prefix to imported package file names.
319 ImportMap map[string]string // Mapping from import name to generated name
320
321 ProtoPkg string // The name under which we import the library's package proto.
322
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700323 packageName string // What we're calling ourselves.
324 allFiles []*FileDescriptor // All files in the tree
325 genFiles []*FileDescriptor // Those files we will generate output for.
326 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000327 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700328 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
329 indent string
330}
331
332// New creates a new generator and allocates the request and response protobufs.
333func New() *Generator {
334 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000335 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100336 g.Request = new(plugin.CodeGeneratorRequest)
337 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700338 return g
339}
340
341// Error reports a problem, including an os.Error, and exits the program.
342func (g *Generator) Error(err os.Error, msgs ...string) {
343 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700344 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700345 g.Response.Error = proto.String(s)
346 os.Exit(1)
347}
348
349// Fail reports a problem and exits the program.
350func (g *Generator) Fail(msgs ...string) {
351 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700352 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700353 g.Response.Error = proto.String(s)
354 os.Exit(1)
355}
356
Rob Pikec9e7d972010-06-10 10:30:22 -0700357// CommandLineParameters breaks the comma-separated list of key=value pairs
358// in the parameter (a member of the request protobuf) into a key/value map.
359// It then sets file name mappings defined by those entries.
360func (g *Generator) CommandLineParameters(parameter string) {
361 g.Param = make(map[string]string)
David Symonds8935abf2011-07-04 15:53:16 +1000362 for _, p := range strings.Split(parameter, ",") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700363 if i := strings.Index(p, "="); i < 0 {
364 g.Param[p] = ""
365 } else {
366 g.Param[p[0:i]] = p[i+1:]
367 }
368 }
369
370 g.ImportMap = make(map[string]string)
371 for k, v := range g.Param {
372 if k == "import_prefix" {
373 g.ImportPrefix = v
374 } else if len(k) > 0 && k[0] == 'M' {
375 g.ImportMap[k[1:]] = v
376 }
377 }
378}
379
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700380// DefaultPackageName returns the package name printed for the object.
381// If its file is in a different package, it returns the package name we're using for this file, plus ".".
382// Otherwise it returns the empty string.
383func (g *Generator) DefaultPackageName(obj Object) string {
384 pkg := obj.PackageName()
385 if pkg == g.packageName {
386 return ""
387 }
388 return pkg + "."
389}
390
391// For each input file, the unique package name to use, underscored.
392var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700393// Package names already registered. Key is the name from the .proto file;
394// value is the name that appears in the generated code.
395var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700396
Rob Pikec9e7d972010-06-10 10:30:22 -0700397// Create and remember a guaranteed unique package name for this file descriptor.
398// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
399// has no file descriptor.
400func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds41389812011-07-19 14:57:40 +1000401 // Convert dots to underscores before finding a unique alias.
402 pkg = strings.Map(DotToUnderscore, pkg)
403
David Symonds79eae332010-10-16 11:33:20 +1100404 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700405 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100406 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700407 }
408 // Install it.
409 pkgNamesInUse[pkg] = true
Rob Pikec9e7d972010-06-10 10:30:22 -0700410 if f != nil {
411 uniquePackageName[f.FileDescriptorProto] = pkg
412 }
413 return pkg
414}
415
416// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700417// The package name must agree across all files being generated.
418// It also defines unique package names for all imported files.
419func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700420 // Register the name for this package. It will be the first name
421 // registered so is guaranteed to be unmodified.
422 pkg := g.genFiles[0].originalPackageName()
423 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
424 // Register the proto package name. It might collide with the
425 // name of a package we import.
426 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
David Symonds7d5c8242011-03-14 12:03:50 -0700427 // Verify that we are generating output for a single package.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700428 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700429 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700430 if thisPkg != pkg {
431 g.Fail("inconsistent package names:", thisPkg, pkg)
432 }
433 }
434AllFiles:
435 for _, f := range g.allFiles {
436 for _, genf := range g.genFiles {
437 if f == genf {
438 // In this package already.
439 uniquePackageName[f.FileDescriptorProto] = g.packageName
440 continue AllFiles
441 }
442 }
David Symonds7d5c8242011-03-14 12:03:50 -0700443 // The file is a dependency, so we want to ignore its go_package option
444 // because that is only relevant for its specific generated output.
445 pkg := proto.GetString(f.Package)
446 if pkg == "" {
447 pkg = BaseName(*f.Name)
448 }
449 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700450 }
451}
452
453// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
454// and FileDescriptorProtos into file-referenced objects within the Generator.
455// It also creates the list of files to generate and so should be called before GenerateAllFiles.
456func (g *Generator) WrapTypes() {
457 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
458 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700459 // We must wrap the descriptors before we wrap the enums
460 descs := wrapDescriptors(f)
461 g.buildNestedDescriptors(descs)
462 enums := wrapEnumDescriptors(f, descs)
463 exts := wrapExtensions(f)
464 g.allFiles[i] = &FileDescriptor{
465 FileDescriptorProto: f,
466 desc: descs,
467 enum: enums,
468 ext: exts,
469 }
470 }
471
472 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
473FindFiles:
474 for i, fileName := range g.Request.FileToGenerate {
475 // Search the list. This algorithm is n^2 but n is tiny.
476 for _, file := range g.allFiles {
477 if fileName == proto.GetString(file.Name) {
478 g.genFiles[i] = file
479 continue FindFiles
480 }
481 }
482 g.Fail("could not find file named", fileName)
483 }
484 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
485}
486
487// Scan the descriptors in this file. For each one, build the slice of nested descriptors
488func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
489 for _, desc := range descs {
490 if len(desc.NestedType) != 0 {
491 desc.nested = make([]*Descriptor, len(desc.NestedType))
492 n := 0
493 for _, nest := range descs {
494 if nest.parent == desc {
495 desc.nested[n] = nest
496 n++
497 }
498 }
499 if n != len(desc.NestedType) {
500 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
501 }
502 }
503 }
504}
505
506// Construct the Descriptor and add it to the slice
507func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
508 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
509
510 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
511 for i, field := range desc.Extension {
512 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
513 }
514
David Symondscc7142e2010-11-06 14:37:15 +1100515 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700516}
517
518// Return a slice of all the Descriptors defined within this file
519func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
520 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
521 for _, desc := range file.MessageType {
522 sl = wrapThisDescriptor(sl, desc, nil, file)
523 }
524 return sl
525}
526
527// Wrap this Descriptor, recursively
528func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
529 sl = addDescriptor(sl, desc, parent, file)
530 me := sl[len(sl)-1]
531 for _, nested := range desc.NestedType {
532 sl = wrapThisDescriptor(sl, nested, me, file)
533 }
534 return sl
535}
536
537// Construct the EnumDescriptor and add it to the slice
538func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symondscc7142e2010-11-06 14:37:15 +1100539 return append(sl, &EnumDescriptor{common{File: file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700540}
541
542// Return a slice of all the EnumDescriptors defined within this file
543func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
544 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000545 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700546 for _, enum := range file.EnumType {
547 sl = addEnumDescriptor(sl, enum, nil, file)
548 }
David Symonds5256cf62010-06-27 10:33:42 +1000549 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700550 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000551 for _, enum := range nested.EnumType {
552 sl = addEnumDescriptor(sl, enum, nested, file)
553 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700554 }
555 return sl
556}
557
558// Return a slice of all the top-level ExtensionDescriptors defined within this file.
559func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
560 sl := make([]*ExtensionDescriptor, len(file.Extension))
561 for i, field := range file.Extension {
562 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
563 }
564 return sl
565}
566
Rob Pikec9e7d972010-06-10 10:30:22 -0700567// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700568// The key names for the map come from the input data, which puts a period at the beginning.
569// It should be called after SetPackageNames and before GenerateAllFiles.
570func (g *Generator) BuildTypeNameMap() {
571 g.typeNameToObject = make(map[string]Object)
572 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700573 // The names in this loop are defined by the proto world, not us, so the
574 // package name may be empty. If so, the dotted package name of X will
575 // be ".X"; otherwise it will be ".pkg.X".
576 dottedPkg := "." + proto.GetString(f.Package)
577 if dottedPkg != "." {
578 dottedPkg += "."
579 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700580 for _, enum := range f.enum {
581 name := dottedPkg + dottedSlice(enum.TypeName())
582 g.typeNameToObject[name] = enum
583 }
584 for _, desc := range f.desc {
585 name := dottedPkg + dottedSlice(desc.TypeName())
586 g.typeNameToObject[name] = desc
587 }
588 }
589}
590
591// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
592// returns the descriptor for the message or enum with that name.
593func (g *Generator) ObjectNamed(typeName string) Object {
594 f, ok := g.typeNameToObject[typeName]
595 if !ok {
596 g.Fail("can't find object with type", typeName)
597 }
598 return f
599}
600
601// P prints the arguments to the generated output. It handles strings and int32s, plus
602// handling indirections because they may be *string, etc.
603func (g *Generator) P(str ...interface{}) {
604 g.WriteString(g.indent)
605 for _, v := range str {
606 switch s := v.(type) {
607 case string:
608 g.WriteString(s)
609 case *string:
610 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700611 case bool:
612 g.WriteString(fmt.Sprintf("%t", s))
613 case *bool:
614 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700615 case *int32:
616 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700617 case float64:
618 g.WriteString(fmt.Sprintf("%g", s))
619 case *float64:
620 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700621 default:
622 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
623 }
624 }
625 g.WriteByte('\n')
626}
627
628// In Indents the output one tab stop.
629func (g *Generator) In() { g.indent += "\t" }
630
631// Out unindents the output one tab stop.
632func (g *Generator) Out() {
633 if len(g.indent) > 0 {
634 g.indent = g.indent[1:]
635 }
636}
637
638// GenerateAllFiles generates the output for all the files we're outputting.
639func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700640 // Initialize the plugins
641 for _, p := range plugins {
642 p.Init(g)
643 }
David Symonds31d58a22011-01-20 18:33:21 +1100644 // Generate the output. The generator runs for every file, even the files
645 // that we don't generate output for, so that we can collate the full list
646 // of exported symbols to support public imports.
647 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
648 for _, file := range g.genFiles {
649 genFileMap[file] = true
650 }
651 i := 0
652 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700653 g.Reset()
654 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100655 if _, ok := genFileMap[file]; !ok {
656 continue
657 }
David Symondsb0127532010-11-09 11:10:46 +1100658 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700659 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
660 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100661 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700662 }
663}
664
665// Run all the plugins associated with the file.
666func (g *Generator) runPlugins(file *FileDescriptor) {
667 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700668 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700669 }
670}
671
672
673// FileOf return the FileDescriptor for this FileDescriptorProto.
674func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
675 for _, file := range g.allFiles {
676 if file.FileDescriptorProto == fd {
677 return file
678 }
679 }
680 g.Fail("could not find file in table:", proto.GetString(fd.Name))
681 return nil
682}
683
684// Fill the response protocol buffer with the generated output for all the files we're
685// supposed to generate.
686func (g *Generator) generate(file *FileDescriptor) {
687 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000688 g.usedPackages = make(map[string]bool)
689
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700690 for _, enum := range g.file.enum {
691 g.generateEnum(enum)
692 }
693 for _, desc := range g.file.desc {
694 g.generateMessage(desc)
695 }
696 for _, ext := range g.file.ext {
697 g.generateExtension(ext)
698 }
699 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000700
Rob Pikec9e7d972010-06-10 10:30:22 -0700701 // Run the plugins before the imports so we know which imports are necessary.
702 g.runPlugins(file)
703
David Symondsf90e3382010-05-05 10:53:44 +1000704 // Generate header and imports last, though they appear first in the output.
705 rem := g.Buffer
706 g.Buffer = new(bytes.Buffer)
707 g.generateHeader()
708 g.generateImports()
709 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +1000710
711 // Reformat generated code.
712 fset := token.NewFileSet()
713 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
714 if err != nil {
715 g.Fail("bad Go source code was generated:", err.String())
716 return
717 }
718 g.Reset()
719 _, err = (&printer.Config{printer.TabIndent, 8}).Fprint(g, fset, ast)
720 if err != nil {
721 g.Fail("generated Go source code could not be reformatted:", err.String())
722 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700723}
724
725// Generate the header, including package definition and imports
726func (g *Generator) generateHeader() {
727 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
728 g.P("// DO NOT EDIT!")
729 g.P()
730 g.P("package ", g.file.PackageName())
731 g.P()
732}
733
David Symonds31d58a22011-01-20 18:33:21 +1100734func (g *Generator) fileByName(filename string) *FileDescriptor {
735 for _, fd := range g.allFiles {
736 if proto.GetString(fd.Name) == filename {
737 return fd
738 }
739 }
740 return nil
741}
742
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700743// Generate the header, including package definition and imports
744func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700745 // We almost always need a proto import. Rather than computing when we
746 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100747 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700748 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100749 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100750 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700751 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100752 fd := g.fileByName(s)
753 // Do not import our own package.
754 if fd.PackageName() == g.packageName {
755 continue
756 }
757 filename := goFileName(s)
758 if substitution, ok := g.ImportMap[s]; ok {
759 filename = substitution
760 }
761 filename = g.ImportPrefix + filename
762 if strings.HasSuffix(filename, ".go") {
763 filename = filename[0 : len(filename)-3]
764 }
765 if _, ok := g.usedPackages[fd.PackageName()]; ok {
766 g.P("import ", fd.PackageName(), " ", Quote(filename))
767 } else {
David Symonds3fa055f2011-05-05 15:19:04 -0700768 // TODO: Re-enable this when we are more feature-complete.
769 // For instance, some protos use foreign field extensions, which we don't support.
770 // Until then, this is just annoying spam.
771 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700772 }
773 }
774 g.P()
775 // TODO: may need to worry about uniqueness across plugins
776 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700777 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700778 g.P()
779 }
David Symondscea785b2011-01-07 11:02:30 +1100780 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700781 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100782 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100783 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700784 g.P()
David Symonds31d58a22011-01-20 18:33:21 +1100785
786 // Symbols from public imports.
787 for _, index := range g.file.PublicDependency {
788 fd := g.fileByName(g.file.Dependency[index])
789 g.P("// Types from public import ", *fd.Name)
790 for _, sym := range fd.exported {
791 sym.GenerateAlias(g, fd.PackageName())
792 }
793 }
794 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700795}
796
797// Generate the enum definitions for this EnumDescriptor.
798func (g *Generator) generateEnum(enum *EnumDescriptor) {
799 // The full type name
800 typeName := enum.TypeName()
801 // The full type name, CamelCased.
802 ccTypeName := CamelCaseSlice(typeName)
803 ccPrefix := enum.prefix()
804 g.P("type ", ccTypeName, " int32")
David Symonds31d58a22011-01-20 18:33:21 +1100805 g.file.addExport(enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700806 g.P("const (")
807 g.In()
808 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100809 name := ccPrefix + *e.Name
810 g.P(name, " = ", e.Number)
811 g.file.addExport(constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700812 }
813 g.Out()
814 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100815 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700816 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700817 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700818 for _, e := range enum.Value {
819 duplicate := ""
820 if _, present := generated[*e.Number]; present {
821 duplicate = "// Duplicate value: "
822 }
823 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
824 generated[*e.Number] = true
825 }
826 g.Out()
827 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100828 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700829 g.In()
830 for _, e := range enum.Value {
831 g.P(Quote(*e.Name), ": ", e.Number, ",")
832 }
833 g.Out()
834 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100835
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700836 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
837 g.In()
838 g.P("e := ", ccTypeName, "(x)")
839 g.P("return &e")
840 g.Out()
841 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100842
843 g.P("func (x ", ccTypeName, ") String() string {")
844 g.In()
845 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
846 g.Out()
847 g.P("}")
848
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700849 g.P()
850}
851
David Symonds8935abf2011-07-04 15:53:16 +1000852// The tag is a string like "varint,2,opt,name=fieldname,def=7" that
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700853// identifies details of the field for the protocol buffer marshaling and unmarshaling
854// code. The fields are:
855// wire encoding
856// protocol tag number
857// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100858// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700859// name= the original declared name
860// enum= the name of the enum type if it is an enum-typed field.
861// def= string representation of the default value, if any.
862// The default value must be in a representation that can be used at run-time
863// to generate the default value. Thus bools become 0 and 1, for instance.
864func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
865 optrepreq := ""
866 switch {
867 case isOptional(field):
868 optrepreq = "opt"
869 case isRequired(field):
870 optrepreq = "req"
871 case isRepeated(field):
872 optrepreq = "rep"
873 }
874 defaultValue := proto.GetString(field.DefaultValue)
875 if defaultValue != "" {
876 switch *field.Type {
877 case descriptor.FieldDescriptorProto_TYPE_BOOL:
878 if defaultValue == "true" {
879 defaultValue = "1"
880 } else {
881 defaultValue = "0"
882 }
883 case descriptor.FieldDescriptorProto_TYPE_STRING,
884 descriptor.FieldDescriptorProto_TYPE_BYTES:
885 // Protect frogs.
886 defaultValue = Quote(defaultValue)
887 // Don't need the quotes
888 defaultValue = defaultValue[1 : len(defaultValue)-1]
889 case descriptor.FieldDescriptorProto_TYPE_ENUM:
890 // For enums we need to provide the integer constant.
891 obj := g.ObjectNamed(proto.GetString(field.TypeName))
892 enum, ok := obj.(*EnumDescriptor)
893 if !ok {
894 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
895 }
896 defaultValue = enum.integerValueAsString(defaultValue)
897 }
898 defaultValue = ",def=" + defaultValue
899 }
900 enum := ""
901 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
902 obj := g.ObjectNamed(proto.GetString(field.TypeName))
903 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
904 }
David Symonds5b7775e2010-12-01 10:09:04 +1100905 packed := ""
906 if field.Options != nil && proto.GetBool(field.Options.Packed) {
907 packed = ",packed"
908 }
David Symondse37856c2011-06-22 12:52:53 +1000909 fieldName := proto.GetString(field.Name)
910 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +1000911 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
912 // We must use the type name for groups instead of
913 // the field name to preserve capitalization.
914 // type_name in FieldDescriptorProto is fully-qualified,
915 // but we only want the local part.
916 name = *field.TypeName
917 if i := strings.LastIndex(name, "."); i >= 0 {
918 name = name[i+1:]
919 }
David Symondse37856c2011-06-22 12:52:53 +1000920 }
921 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700922 name = ""
923 } else {
924 name = ",name=" + name
925 }
David Symonds8935abf2011-07-04 15:53:16 +1000926 return Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700927 wiretype,
928 proto.GetInt32(field.Number),
929 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100930 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700931 name,
932 enum,
933 defaultValue))
934}
935
936func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
937 switch typ {
938 case descriptor.FieldDescriptorProto_TYPE_GROUP:
939 return false
940 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
941 return false
942 case descriptor.FieldDescriptorProto_TYPE_BYTES:
943 return false
944 }
945 return true
946}
947
948// TypeName is the printed name appropriate for an item. If the object is in the current file,
949// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -0700950// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700951// package name followed by the item name.
952// The result always has an initial capital.
953func (g *Generator) TypeName(obj Object) string {
954 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
955}
956
957// TypeNameWithPackage is like TypeName, but always includes the package
958// name even if the object is in our own package.
959func (g *Generator) TypeNameWithPackage(obj Object) string {
960 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
961}
962
963// GoType returns a string representing the type name, and the wire type
964func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
965 // TODO: Options.
966 switch *field.Type {
967 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
968 typ, wire = "float64", "fixed64"
969 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
970 typ, wire = "float32", "fixed32"
971 case descriptor.FieldDescriptorProto_TYPE_INT64:
972 typ, wire = "int64", "varint"
973 case descriptor.FieldDescriptorProto_TYPE_UINT64:
974 typ, wire = "uint64", "varint"
975 case descriptor.FieldDescriptorProto_TYPE_INT32:
976 typ, wire = "int32", "varint"
977 case descriptor.FieldDescriptorProto_TYPE_UINT32:
978 typ, wire = "uint32", "varint"
979 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
980 typ, wire = "uint64", "fixed64"
981 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
982 typ, wire = "uint32", "fixed32"
983 case descriptor.FieldDescriptorProto_TYPE_BOOL:
984 typ, wire = "bool", "varint"
985 case descriptor.FieldDescriptorProto_TYPE_STRING:
986 typ, wire = "string", "bytes"
987 case descriptor.FieldDescriptorProto_TYPE_GROUP:
988 desc := g.ObjectNamed(proto.GetString(field.TypeName))
989 typ, wire = "*"+g.TypeName(desc), "group"
990 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
991 desc := g.ObjectNamed(proto.GetString(field.TypeName))
992 typ, wire = "*"+g.TypeName(desc), "bytes"
993 case descriptor.FieldDescriptorProto_TYPE_BYTES:
994 typ, wire = "[]byte", "bytes"
995 case descriptor.FieldDescriptorProto_TYPE_ENUM:
996 desc := g.ObjectNamed(proto.GetString(field.TypeName))
997 typ, wire = g.TypeName(desc), "varint"
998 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
999 typ, wire = "int32", "fixed32"
1000 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
1001 typ, wire = "int64", "fixed64"
1002 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1003 typ, wire = "int32", "zigzag32"
1004 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1005 typ, wire = "int64", "zigzag64"
1006 default:
1007 g.Fail("unknown type for", proto.GetString(field.Name))
1008 }
1009 if isRepeated(field) {
1010 typ = "[]" + typ
1011 } else if needsStar(*field.Type) {
1012 typ = "*" + typ
1013 }
1014 return
1015}
1016
David Symondsf90e3382010-05-05 10:53:44 +10001017func (g *Generator) RecordTypeUse(t string) {
1018 if obj, ok := g.typeNameToObject[t]; ok {
1019 g.usedPackages[obj.PackageName()] = true
1020 }
1021}
1022
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001023// Generate the type and default constant definitions for this Descriptor.
1024func (g *Generator) generateMessage(message *Descriptor) {
1025 // The full type name
1026 typeName := message.TypeName()
1027 // The full type name, CamelCased.
1028 ccTypeName := CamelCaseSlice(typeName)
1029
1030 g.P("type ", ccTypeName, " struct {")
1031 g.In()
1032 for _, field := range message.Field {
1033 fieldname := CamelCase(*field.Name)
1034 typename, wiretype := g.GoType(message, field)
David Symonds8935abf2011-07-04 15:53:16 +10001035 tag := "`protobuf:" + g.goTag(field, wiretype) + "`"
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001036 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001037 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001038 }
1039 if len(message.ExtensionRange) > 0 {
1040 g.P("XXX_extensions\t\tmap[int32][]byte")
1041 }
1042 g.P("XXX_unrecognized\t[]byte")
1043 g.Out()
1044 g.P("}")
1045
David Symondse37856c2011-06-22 12:52:53 +10001046 // Reset and String functions
1047 g.P("func (this *", ccTypeName, ") Reset() { *this = ", ccTypeName, "{} }")
1048 g.P("func (this *", ccTypeName, ") String() string { return ", g.ProtoPkg, ".CompactTextString(this) }")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001049
1050 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001051 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001052 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001053 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001054 // message_set_wire_format only makes sense when extensions are defined.
1055 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001056 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001057 g.P()
1058 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1059 g.In()
1060 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1061 g.Out()
1062 g.P("}")
1063 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1064 g.In()
1065 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1066 g.Out()
1067 g.P("}")
1068 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1069 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1070 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1071 }
1072
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001073 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001074 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001075 g.In()
1076 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001077 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1078 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001079 }
1080 g.Out()
1081 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001082 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001083 g.In()
1084 g.P("return extRange_", ccTypeName)
1085 g.Out()
1086 g.P("}")
1087 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1088 g.In()
1089 g.P("if this.XXX_extensions == nil {")
1090 g.In()
1091 g.P("this.XXX_extensions = make(map[int32][]byte)")
1092 g.Out()
1093 g.P("}")
1094 g.P("return this.XXX_extensions")
1095 g.Out()
1096 g.P("}")
1097 }
1098
David Symonds31d58a22011-01-20 18:33:21 +11001099 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1100
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001101 // Default constants
1102 for _, field := range message.Field {
1103 def := proto.GetString(field.DefaultValue)
1104 if def == "" {
1105 continue
1106 }
1107 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1108 typename, _ := g.GoType(message, field)
1109 if typename[0] == '*' {
1110 typename = typename[1:]
1111 }
1112 kind := "const "
1113 switch {
1114 case typename == "bool":
1115 case typename == "string":
1116 def = Quote(def)
1117 case typename == "[]byte":
1118 def = "[]byte(" + Quote(def) + ")"
1119 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001120 case def == "inf", def == "-inf", def == "nan":
1121 // These names are known to, and defined by, the protocol language.
1122 switch def {
1123 case "inf":
1124 def = "math.Inf(1)"
1125 case "-inf":
1126 def = "math.Inf(-1)"
1127 case "nan":
1128 def = "math.NaN()"
1129 }
1130 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1131 def = "float32(" + def + ")"
1132 }
1133 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001134 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1135 // Must be an enum. Need to construct the prefixed name.
1136 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1137 enum, ok := obj.(*EnumDescriptor)
1138 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001139 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001140 continue
1141 }
Rob Pike87af39e2010-07-19 10:48:02 -07001142 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001143 }
1144 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001145 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001146 }
1147 g.P()
1148
1149 for _, ext := range message.ext {
1150 g.generateExtension(ext)
1151 }
1152}
1153
1154func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001155 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001156
1157 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1158 field := ext.FieldDescriptorProto
1159 fieldType, wireType := g.GoType(ext.parent, field)
1160 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001161 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001162 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1163 // foreign extension type
1164 g.RecordTypeUse(*n)
1165 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001166
Rob Pikec9e7d972010-06-10 10:30:22 -07001167 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001168 g.In()
1169 g.P("ExtendedType: (", extendedType, ")(nil),")
1170 g.P("ExtensionType: (", fieldType, ")(nil),")
1171 g.P("Field: ", field.Number, ",")
David Symondse37856c2011-06-22 12:52:53 +10001172 g.P(`Name: "`, g.packageName, ".", *field.Name, `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001173 g.P("Tag: ", tag, ",")
1174
1175 g.Out()
1176 g.P("}")
1177 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001178
1179 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001180}
1181
1182func (g *Generator) generateInitFunction() {
1183 g.P("func init() {")
1184 g.In()
1185 for _, enum := range g.file.enum {
1186 g.generateEnumRegistration(enum)
1187 }
David Symondse37856c2011-06-22 12:52:53 +10001188 for _, d := range g.file.desc {
1189 for _, ext := range d.ext {
1190 g.generateExtensionRegistration(ext)
1191 }
1192 }
1193 for _, ext := range g.file.ext {
1194 g.generateExtensionRegistration(ext)
1195 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001196 g.Out()
1197 g.P("}")
1198}
1199
1200func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1201 pkg := g.packageName + "." // We always print the full package name here.
1202 // The full type name
1203 typeName := enum.TypeName()
1204 // The full type name, CamelCased.
1205 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001206 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001207}
1208
David Symondse37856c2011-06-22 12:52:53 +10001209func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
1210 g.P(g.ProtoPkg+".RegisterExtension(", ext.DescName(), ")")
1211}
1212
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001213// And now lots of helper functions.
1214
Rob Pike2c7bafc2010-06-10 16:07:14 -07001215// Is c an ASCII lower-case letter?
1216func isASCIILower(c byte) bool {
1217 return 'a' <= c && c <= 'z'
1218}
1219
1220// Is c an ASCII digit?
1221func isASCIIDigit(c byte) bool {
1222 return '0' <= c && c <= '9'
1223}
1224
1225// CamelCase returns the CamelCased name.
1226// If there is an interior underscore followed by a lower case letter,
1227// drop the underscore and convert the letter to upper case.
1228// There is a remote possibility of this rewrite causing a name collision,
1229// but it's so remote we're prepared to pretend it's nonexistent - since the
1230// C++ generator lowercases names, it's extremely unlikely to have two fields
1231// with different capitalizations.
1232// In short, _my_field_name_2 becomes XMyFieldName2.
1233func CamelCase(s string) string {
1234 if s == "" {
1235 return ""
1236 }
1237 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001238 i := 0
1239 if s[0] == '_' {
1240 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001241 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001242 i++
1243 }
1244 // Invariant: if the next letter is lower case, it must be converted
1245 // to upper case.
1246 // That is, we process a word at a time, where words are marked by _ or
1247 // upper case letter. Digits are treated as words.
1248 for ; i < len(s); i++ {
1249 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001250 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1251 continue // Skip the underscore in s.
1252 }
1253 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001254 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001255 continue
1256 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001257 // Assume we have a letter now - if not, it's a bogus identifier.
1258 // The next word is a sequence of characters that must start upper case.
1259 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001260 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001261 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001262 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001263 // Accept lower case sequence that follows.
1264 for i+1 < len(s) && isASCIILower(s[i+1]) {
1265 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001266 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001267 }
1268 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001269 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001270}
1271
1272// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1273// be joined with "_".
1274func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1275
1276// dottedSlice turns a sliced name into a dotted name.
1277func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1278
1279// Quote returns a Go-source quoted string representation of s.
1280func Quote(s string) string { return fmt.Sprintf("%q", s) }
1281
1282// Given a .proto file name, return the output name for the generated Go program.
1283func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001284 ext := path.Ext(name)
1285 if ext == ".proto" || ext == ".protodevel" {
1286 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001287 }
1288 return name + ".pb.go"
1289}
1290
1291// Is this field optional?
1292func isOptional(field *descriptor.FieldDescriptorProto) bool {
1293 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1294}
1295
1296// Is this field required?
1297func isRequired(field *descriptor.FieldDescriptorProto) bool {
1298 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1299}
1300
1301// Is this field repeated?
1302func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1303 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1304}
1305
1306// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001307// which can be dotted in the input .proto file. It maps dots to underscores.
1308// Because we also get here from package names generated from file names, it also maps
1309// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001310func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001311 switch rune {
1312 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001313 return '_'
1314 }
1315 return rune
1316}
Rob Pikec9e7d972010-06-10 10:30:22 -07001317
1318// BaseName returns the last path element of the name, with the last dotted suffix removed.
1319func BaseName(name string) string {
1320 // First, find the last element
1321 if i := strings.LastIndex(name, "/"); i >= 0 {
1322 name = name[i+1:]
1323 }
1324 // Now drop the suffix
1325 if i := strings.LastIndex(name, "."); i >= 0 {
1326 name = name[0:i]
1327 }
1328 return name
1329}