blob: 94d376868d3a57a9683d4e5bb5aea43e499c4c42 [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
198// FileDescriptor describes an protocol buffer descriptor file (.proto).
199// It includes slices of all the messages and enums defined within it.
200// Those slices are constructed by WrapTypes.
201type FileDescriptor struct {
202 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700203 desc []*Descriptor // All the messages defined in this file.
204 enum []*EnumDescriptor // All the enums defined in this file.
205 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds31d58a22011-01-20 18:33:21 +1100206
207 // The full list of symbols that are exported.
208 // This is used for supporting public imports.
209 exported []Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700210}
211
212// PackageName is the package name we'll use in the generated code to refer to this file.
213func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
214
215// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700216// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700217func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700218 // Does the file have a package clause?
David Symonds7d5c8242011-03-14 12:03:50 -0700219 if pkg := proto.GetString(d.Package); pkg != "" {
Rob Pikec9e7d972010-06-10 10:30:22 -0700220 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700221 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700222 // Use the file base name.
223 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700224}
225
David Symonds31d58a22011-01-20 18:33:21 +1100226func (d *FileDescriptor) addExport(symbol Symbol) {
227 d.exported = append(d.exported, symbol)
228}
229
230// Symbol is an interface representing an exported Go symbol.
231type Symbol interface {
232 // GenerateAlias should generate an appropriate alias
233 // for the symbol from the named package.
234 GenerateAlias(g *Generator, pkg string)
235}
236
237type messageSymbol struct {
238 sym string
239 hasExtensions, isMessageSet bool
240}
241
242func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
243 remoteSym := pkg + "." + ms.sym
244
245 g.P("type ", ms.sym, " ", remoteSym)
246 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
247 if ms.hasExtensions {
248 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
249 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
250 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32][]byte ",
251 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
252 if ms.isMessageSet {
253 g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
254 "{ return (*", remoteSym, ")(this).Marshal() }")
255 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
256 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
257 }
258 }
259}
260
261type enumSymbol string
262
263func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
264 s := string(es)
265 g.P("type ", s, " ", pkg, ".", s)
266 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
267 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
268 g.P("func New", s, "(x int32) *", s, " { e := ", s, "(x); return &e }")
269}
270
271type constOrVarSymbol struct {
272 sym string
273 typ string // either "const" or "var"
274}
275
276func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
277 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
278}
279
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700280// Object is an interface abstracting the abilities shared by enums and messages.
281type Object interface {
282 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
283 TypeName() []string
284}
285
286// Each package name we generate must be unique. The package we're generating
287// gets its own name but every other package must have a unqiue name that does
288// not conflict in the code we generate. These names are chosen globally (although
289// they don't have to be, it simplifies things to do them globally).
290func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
291 s, ok := uniquePackageName[fd]
292 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100293 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700294 }
295 return s
296}
297
298// Generator is the type whose methods generate the output, stored in the associated response structure.
299type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000300 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700301
302 Request *plugin.CodeGeneratorRequest // The input.
303 Response *plugin.CodeGeneratorResponse // The output.
304
Rob Pikec9e7d972010-06-10 10:30:22 -0700305 Param map[string]string // Command-line parameters.
306 ImportPrefix string // String to prefix to imported package file names.
307 ImportMap map[string]string // Mapping from import name to generated name
308
309 ProtoPkg string // The name under which we import the library's package proto.
310
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700311 packageName string // What we're calling ourselves.
312 allFiles []*FileDescriptor // All files in the tree
313 genFiles []*FileDescriptor // Those files we will generate output for.
314 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000315 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700316 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
317 indent string
318}
319
320// New creates a new generator and allocates the request and response protobufs.
321func New() *Generator {
322 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000323 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100324 g.Request = new(plugin.CodeGeneratorRequest)
325 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700326 return g
327}
328
329// Error reports a problem, including an os.Error, and exits the program.
330func (g *Generator) Error(err os.Error, msgs ...string) {
331 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700332 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700333 g.Response.Error = proto.String(s)
334 os.Exit(1)
335}
336
337// Fail reports a problem and exits the program.
338func (g *Generator) Fail(msgs ...string) {
339 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700340 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700341 g.Response.Error = proto.String(s)
342 os.Exit(1)
343}
344
Rob Pikec9e7d972010-06-10 10:30:22 -0700345// CommandLineParameters breaks the comma-separated list of key=value pairs
346// in the parameter (a member of the request protobuf) into a key/value map.
347// It then sets file name mappings defined by those entries.
348func (g *Generator) CommandLineParameters(parameter string) {
349 g.Param = make(map[string]string)
Rob Pike53385442010-06-30 22:22:43 -0700350 for _, p := range strings.Split(parameter, ",", -1) {
Rob Pikec9e7d972010-06-10 10:30:22 -0700351 if i := strings.Index(p, "="); i < 0 {
352 g.Param[p] = ""
353 } else {
354 g.Param[p[0:i]] = p[i+1:]
355 }
356 }
357
358 g.ImportMap = make(map[string]string)
359 for k, v := range g.Param {
360 if k == "import_prefix" {
361 g.ImportPrefix = v
362 } else if len(k) > 0 && k[0] == 'M' {
363 g.ImportMap[k[1:]] = v
364 }
365 }
366}
367
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700368// DefaultPackageName returns the package name printed for the object.
369// If its file is in a different package, it returns the package name we're using for this file, plus ".".
370// Otherwise it returns the empty string.
371func (g *Generator) DefaultPackageName(obj Object) string {
372 pkg := obj.PackageName()
373 if pkg == g.packageName {
374 return ""
375 }
376 return pkg + "."
377}
378
379// For each input file, the unique package name to use, underscored.
380var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700381// Package names already registered. Key is the name from the .proto file;
382// value is the name that appears in the generated code.
383var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700384
Rob Pikec9e7d972010-06-10 10:30:22 -0700385// Create and remember a guaranteed unique package name for this file descriptor.
386// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
387// has no file descriptor.
388func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds79eae332010-10-16 11:33:20 +1100389 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700390 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100391 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700392 }
393 // Install it.
394 pkgNamesInUse[pkg] = true
395 pkg = strings.Map(DotToUnderscore, pkg)
396 if f != nil {
397 uniquePackageName[f.FileDescriptorProto] = pkg
398 }
399 return pkg
400}
401
402// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700403// The package name must agree across all files being generated.
404// It also defines unique package names for all imported files.
405func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700406 // Register the name for this package. It will be the first name
407 // registered so is guaranteed to be unmodified.
408 pkg := g.genFiles[0].originalPackageName()
409 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
410 // Register the proto package name. It might collide with the
411 // name of a package we import.
412 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
David Symonds7d5c8242011-03-14 12:03:50 -0700413 // Verify that we are generating output for a single package.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700414 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700415 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700416 if thisPkg != pkg {
417 g.Fail("inconsistent package names:", thisPkg, pkg)
418 }
419 }
420AllFiles:
421 for _, f := range g.allFiles {
422 for _, genf := range g.genFiles {
423 if f == genf {
424 // In this package already.
425 uniquePackageName[f.FileDescriptorProto] = g.packageName
426 continue AllFiles
427 }
428 }
David Symonds7d5c8242011-03-14 12:03:50 -0700429 // The file is a dependency, so we want to ignore its go_package option
430 // because that is only relevant for its specific generated output.
431 pkg := proto.GetString(f.Package)
432 if pkg == "" {
433 pkg = BaseName(*f.Name)
434 }
435 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700436 }
437}
438
439// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
440// and FileDescriptorProtos into file-referenced objects within the Generator.
441// It also creates the list of files to generate and so should be called before GenerateAllFiles.
442func (g *Generator) WrapTypes() {
443 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
444 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700445 // We must wrap the descriptors before we wrap the enums
446 descs := wrapDescriptors(f)
447 g.buildNestedDescriptors(descs)
448 enums := wrapEnumDescriptors(f, descs)
449 exts := wrapExtensions(f)
450 g.allFiles[i] = &FileDescriptor{
451 FileDescriptorProto: f,
452 desc: descs,
453 enum: enums,
454 ext: exts,
455 }
456 }
457
458 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
459FindFiles:
460 for i, fileName := range g.Request.FileToGenerate {
461 // Search the list. This algorithm is n^2 but n is tiny.
462 for _, file := range g.allFiles {
463 if fileName == proto.GetString(file.Name) {
464 g.genFiles[i] = file
465 continue FindFiles
466 }
467 }
468 g.Fail("could not find file named", fileName)
469 }
470 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
471}
472
473// Scan the descriptors in this file. For each one, build the slice of nested descriptors
474func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
475 for _, desc := range descs {
476 if len(desc.NestedType) != 0 {
477 desc.nested = make([]*Descriptor, len(desc.NestedType))
478 n := 0
479 for _, nest := range descs {
480 if nest.parent == desc {
481 desc.nested[n] = nest
482 n++
483 }
484 }
485 if n != len(desc.NestedType) {
486 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
487 }
488 }
489 }
490}
491
492// Construct the Descriptor and add it to the slice
493func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
494 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
495
496 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
497 for i, field := range desc.Extension {
498 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
499 }
500
David Symondscc7142e2010-11-06 14:37:15 +1100501 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700502}
503
504// Return a slice of all the Descriptors defined within this file
505func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
506 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
507 for _, desc := range file.MessageType {
508 sl = wrapThisDescriptor(sl, desc, nil, file)
509 }
510 return sl
511}
512
513// Wrap this Descriptor, recursively
514func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
515 sl = addDescriptor(sl, desc, parent, file)
516 me := sl[len(sl)-1]
517 for _, nested := range desc.NestedType {
518 sl = wrapThisDescriptor(sl, nested, me, file)
519 }
520 return sl
521}
522
523// Construct the EnumDescriptor and add it to the slice
524func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symondscc7142e2010-11-06 14:37:15 +1100525 return append(sl, &EnumDescriptor{common{File: file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700526}
527
528// Return a slice of all the EnumDescriptors defined within this file
529func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
530 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000531 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700532 for _, enum := range file.EnumType {
533 sl = addEnumDescriptor(sl, enum, nil, file)
534 }
David Symonds5256cf62010-06-27 10:33:42 +1000535 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700536 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000537 for _, enum := range nested.EnumType {
538 sl = addEnumDescriptor(sl, enum, nested, file)
539 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700540 }
541 return sl
542}
543
544// Return a slice of all the top-level ExtensionDescriptors defined within this file.
545func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
546 sl := make([]*ExtensionDescriptor, len(file.Extension))
547 for i, field := range file.Extension {
548 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
549 }
550 return sl
551}
552
Rob Pikec9e7d972010-06-10 10:30:22 -0700553// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700554// The key names for the map come from the input data, which puts a period at the beginning.
555// It should be called after SetPackageNames and before GenerateAllFiles.
556func (g *Generator) BuildTypeNameMap() {
557 g.typeNameToObject = make(map[string]Object)
558 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700559 // The names in this loop are defined by the proto world, not us, so the
560 // package name may be empty. If so, the dotted package name of X will
561 // be ".X"; otherwise it will be ".pkg.X".
562 dottedPkg := "." + proto.GetString(f.Package)
563 if dottedPkg != "." {
564 dottedPkg += "."
565 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700566 for _, enum := range f.enum {
567 name := dottedPkg + dottedSlice(enum.TypeName())
568 g.typeNameToObject[name] = enum
569 }
570 for _, desc := range f.desc {
571 name := dottedPkg + dottedSlice(desc.TypeName())
572 g.typeNameToObject[name] = desc
573 }
574 }
575}
576
577// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
578// returns the descriptor for the message or enum with that name.
579func (g *Generator) ObjectNamed(typeName string) Object {
580 f, ok := g.typeNameToObject[typeName]
581 if !ok {
582 g.Fail("can't find object with type", typeName)
583 }
584 return f
585}
586
587// P prints the arguments to the generated output. It handles strings and int32s, plus
588// handling indirections because they may be *string, etc.
589func (g *Generator) P(str ...interface{}) {
590 g.WriteString(g.indent)
591 for _, v := range str {
592 switch s := v.(type) {
593 case string:
594 g.WriteString(s)
595 case *string:
596 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700597 case bool:
598 g.WriteString(fmt.Sprintf("%t", s))
599 case *bool:
600 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700601 case *int32:
602 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700603 case float64:
604 g.WriteString(fmt.Sprintf("%g", s))
605 case *float64:
606 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700607 default:
608 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
609 }
610 }
611 g.WriteByte('\n')
612}
613
614// In Indents the output one tab stop.
615func (g *Generator) In() { g.indent += "\t" }
616
617// Out unindents the output one tab stop.
618func (g *Generator) Out() {
619 if len(g.indent) > 0 {
620 g.indent = g.indent[1:]
621 }
622}
623
624// GenerateAllFiles generates the output for all the files we're outputting.
625func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700626 // Initialize the plugins
627 for _, p := range plugins {
628 p.Init(g)
629 }
David Symonds31d58a22011-01-20 18:33:21 +1100630 // Generate the output. The generator runs for every file, even the files
631 // that we don't generate output for, so that we can collate the full list
632 // of exported symbols to support public imports.
633 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
634 for _, file := range g.genFiles {
635 genFileMap[file] = true
636 }
637 i := 0
638 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700639 g.Reset()
640 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100641 if _, ok := genFileMap[file]; !ok {
642 continue
643 }
David Symondsb0127532010-11-09 11:10:46 +1100644 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700645 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
646 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100647 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700648 }
649}
650
651// Run all the plugins associated with the file.
652func (g *Generator) runPlugins(file *FileDescriptor) {
653 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700654 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700655 }
656}
657
658
659// FileOf return the FileDescriptor for this FileDescriptorProto.
660func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
661 for _, file := range g.allFiles {
662 if file.FileDescriptorProto == fd {
663 return file
664 }
665 }
666 g.Fail("could not find file in table:", proto.GetString(fd.Name))
667 return nil
668}
669
670// Fill the response protocol buffer with the generated output for all the files we're
671// supposed to generate.
672func (g *Generator) generate(file *FileDescriptor) {
673 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000674 g.usedPackages = make(map[string]bool)
675
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700676 for _, enum := range g.file.enum {
677 g.generateEnum(enum)
678 }
679 for _, desc := range g.file.desc {
680 g.generateMessage(desc)
681 }
682 for _, ext := range g.file.ext {
683 g.generateExtension(ext)
684 }
685 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000686
Rob Pikec9e7d972010-06-10 10:30:22 -0700687 // Run the plugins before the imports so we know which imports are necessary.
688 g.runPlugins(file)
689
David Symondsf90e3382010-05-05 10:53:44 +1000690 // Generate header and imports last, though they appear first in the output.
691 rem := g.Buffer
692 g.Buffer = new(bytes.Buffer)
693 g.generateHeader()
694 g.generateImports()
695 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +1000696
697 // Reformat generated code.
698 fset := token.NewFileSet()
699 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
700 if err != nil {
701 g.Fail("bad Go source code was generated:", err.String())
702 return
703 }
704 g.Reset()
705 _, err = (&printer.Config{printer.TabIndent, 8}).Fprint(g, fset, ast)
706 if err != nil {
707 g.Fail("generated Go source code could not be reformatted:", err.String())
708 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700709}
710
711// Generate the header, including package definition and imports
712func (g *Generator) generateHeader() {
713 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
714 g.P("// DO NOT EDIT!")
715 g.P()
716 g.P("package ", g.file.PackageName())
717 g.P()
718}
719
David Symonds31d58a22011-01-20 18:33:21 +1100720func (g *Generator) fileByName(filename string) *FileDescriptor {
721 for _, fd := range g.allFiles {
722 if proto.GetString(fd.Name) == filename {
723 return fd
724 }
725 }
726 return nil
727}
728
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700729// Generate the header, including package definition and imports
730func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700731 // We almost always need a proto import. Rather than computing when we
732 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100733 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700734 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100735 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100736 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700737 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100738 fd := g.fileByName(s)
739 // Do not import our own package.
740 if fd.PackageName() == g.packageName {
741 continue
742 }
743 filename := goFileName(s)
744 if substitution, ok := g.ImportMap[s]; ok {
745 filename = substitution
746 }
747 filename = g.ImportPrefix + filename
748 if strings.HasSuffix(filename, ".go") {
749 filename = filename[0 : len(filename)-3]
750 }
751 if _, ok := g.usedPackages[fd.PackageName()]; ok {
752 g.P("import ", fd.PackageName(), " ", Quote(filename))
753 } else {
David Symonds3fa055f2011-05-05 15:19:04 -0700754 // TODO: Re-enable this when we are more feature-complete.
755 // For instance, some protos use foreign field extensions, which we don't support.
756 // Until then, this is just annoying spam.
757 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700758 }
759 }
760 g.P()
761 // TODO: may need to worry about uniqueness across plugins
762 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700763 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700764 g.P()
765 }
David Symondscea785b2011-01-07 11:02:30 +1100766 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700767 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100768 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100769 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700770 g.P()
David Symonds31d58a22011-01-20 18:33:21 +1100771
772 // Symbols from public imports.
773 for _, index := range g.file.PublicDependency {
774 fd := g.fileByName(g.file.Dependency[index])
775 g.P("// Types from public import ", *fd.Name)
776 for _, sym := range fd.exported {
777 sym.GenerateAlias(g, fd.PackageName())
778 }
779 }
780 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700781}
782
783// Generate the enum definitions for this EnumDescriptor.
784func (g *Generator) generateEnum(enum *EnumDescriptor) {
785 // The full type name
786 typeName := enum.TypeName()
787 // The full type name, CamelCased.
788 ccTypeName := CamelCaseSlice(typeName)
789 ccPrefix := enum.prefix()
790 g.P("type ", ccTypeName, " int32")
David Symonds31d58a22011-01-20 18:33:21 +1100791 g.file.addExport(enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700792 g.P("const (")
793 g.In()
794 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100795 name := ccPrefix + *e.Name
796 g.P(name, " = ", e.Number)
797 g.file.addExport(constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700798 }
799 g.Out()
800 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100801 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700802 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700803 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700804 for _, e := range enum.Value {
805 duplicate := ""
806 if _, present := generated[*e.Number]; present {
807 duplicate = "// Duplicate value: "
808 }
809 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
810 generated[*e.Number] = true
811 }
812 g.Out()
813 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100814 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700815 g.In()
816 for _, e := range enum.Value {
817 g.P(Quote(*e.Name), ": ", e.Number, ",")
818 }
819 g.Out()
820 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100821
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700822 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
823 g.In()
824 g.P("e := ", ccTypeName, "(x)")
825 g.P("return &e")
826 g.Out()
827 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100828
829 g.P("func (x ", ccTypeName, ") String() string {")
830 g.In()
831 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
832 g.Out()
833 g.P("}")
834
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700835 g.P()
836}
837
838// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
839// identifies details of the field for the protocol buffer marshaling and unmarshaling
840// code. The fields are:
841// wire encoding
842// protocol tag number
843// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100844// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700845// name= the original declared name
846// enum= the name of the enum type if it is an enum-typed field.
847// def= string representation of the default value, if any.
848// The default value must be in a representation that can be used at run-time
849// to generate the default value. Thus bools become 0 and 1, for instance.
850func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
851 optrepreq := ""
852 switch {
853 case isOptional(field):
854 optrepreq = "opt"
855 case isRequired(field):
856 optrepreq = "req"
857 case isRepeated(field):
858 optrepreq = "rep"
859 }
860 defaultValue := proto.GetString(field.DefaultValue)
861 if defaultValue != "" {
862 switch *field.Type {
863 case descriptor.FieldDescriptorProto_TYPE_BOOL:
864 if defaultValue == "true" {
865 defaultValue = "1"
866 } else {
867 defaultValue = "0"
868 }
869 case descriptor.FieldDescriptorProto_TYPE_STRING,
870 descriptor.FieldDescriptorProto_TYPE_BYTES:
871 // Protect frogs.
872 defaultValue = Quote(defaultValue)
873 // Don't need the quotes
874 defaultValue = defaultValue[1 : len(defaultValue)-1]
875 case descriptor.FieldDescriptorProto_TYPE_ENUM:
876 // For enums we need to provide the integer constant.
877 obj := g.ObjectNamed(proto.GetString(field.TypeName))
878 enum, ok := obj.(*EnumDescriptor)
879 if !ok {
880 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
881 }
882 defaultValue = enum.integerValueAsString(defaultValue)
883 }
884 defaultValue = ",def=" + defaultValue
885 }
886 enum := ""
887 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
888 obj := g.ObjectNamed(proto.GetString(field.TypeName))
889 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
890 }
David Symonds5b7775e2010-12-01 10:09:04 +1100891 packed := ""
892 if field.Options != nil && proto.GetBool(field.Options.Packed) {
893 packed = ",packed"
894 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700895 name := proto.GetString(field.Name)
David Symonds9f402812011-04-28 18:08:44 +1000896 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
897 // We must use the type name for groups instead of
898 // the field name to preserve capitalization.
899 // type_name in FieldDescriptorProto is fully-qualified,
900 // but we only want the local part.
901 name = *field.TypeName
902 if i := strings.LastIndex(name, "."); i >= 0 {
903 name = name[i+1:]
904 }
905 name = ",name=" + name
906 } else if name == CamelCase(name) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700907 name = ""
908 } else {
909 name = ",name=" + name
910 }
David Symonds5b7775e2010-12-01 10:09:04 +1100911 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s%s)",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700912 wiretype,
913 proto.GetInt32(field.Number),
914 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100915 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700916 name,
917 enum,
918 defaultValue))
919}
920
921func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
922 switch typ {
923 case descriptor.FieldDescriptorProto_TYPE_GROUP:
924 return false
925 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
926 return false
927 case descriptor.FieldDescriptorProto_TYPE_BYTES:
928 return false
929 }
930 return true
931}
932
933// TypeName is the printed name appropriate for an item. If the object is in the current file,
934// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -0700935// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700936// package name followed by the item name.
937// The result always has an initial capital.
938func (g *Generator) TypeName(obj Object) string {
939 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
940}
941
942// TypeNameWithPackage is like TypeName, but always includes the package
943// name even if the object is in our own package.
944func (g *Generator) TypeNameWithPackage(obj Object) string {
945 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
946}
947
948// GoType returns a string representing the type name, and the wire type
949func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
950 // TODO: Options.
951 switch *field.Type {
952 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
953 typ, wire = "float64", "fixed64"
954 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
955 typ, wire = "float32", "fixed32"
956 case descriptor.FieldDescriptorProto_TYPE_INT64:
957 typ, wire = "int64", "varint"
958 case descriptor.FieldDescriptorProto_TYPE_UINT64:
959 typ, wire = "uint64", "varint"
960 case descriptor.FieldDescriptorProto_TYPE_INT32:
961 typ, wire = "int32", "varint"
962 case descriptor.FieldDescriptorProto_TYPE_UINT32:
963 typ, wire = "uint32", "varint"
964 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
965 typ, wire = "uint64", "fixed64"
966 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
967 typ, wire = "uint32", "fixed32"
968 case descriptor.FieldDescriptorProto_TYPE_BOOL:
969 typ, wire = "bool", "varint"
970 case descriptor.FieldDescriptorProto_TYPE_STRING:
971 typ, wire = "string", "bytes"
972 case descriptor.FieldDescriptorProto_TYPE_GROUP:
973 desc := g.ObjectNamed(proto.GetString(field.TypeName))
974 typ, wire = "*"+g.TypeName(desc), "group"
975 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
976 desc := g.ObjectNamed(proto.GetString(field.TypeName))
977 typ, wire = "*"+g.TypeName(desc), "bytes"
978 case descriptor.FieldDescriptorProto_TYPE_BYTES:
979 typ, wire = "[]byte", "bytes"
980 case descriptor.FieldDescriptorProto_TYPE_ENUM:
981 desc := g.ObjectNamed(proto.GetString(field.TypeName))
982 typ, wire = g.TypeName(desc), "varint"
983 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
984 typ, wire = "int32", "fixed32"
985 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
986 typ, wire = "int64", "fixed64"
987 case descriptor.FieldDescriptorProto_TYPE_SINT32:
988 typ, wire = "int32", "zigzag32"
989 case descriptor.FieldDescriptorProto_TYPE_SINT64:
990 typ, wire = "int64", "zigzag64"
991 default:
992 g.Fail("unknown type for", proto.GetString(field.Name))
993 }
994 if isRepeated(field) {
995 typ = "[]" + typ
996 } else if needsStar(*field.Type) {
997 typ = "*" + typ
998 }
999 return
1000}
1001
David Symondsf90e3382010-05-05 10:53:44 +10001002func (g *Generator) RecordTypeUse(t string) {
1003 if obj, ok := g.typeNameToObject[t]; ok {
1004 g.usedPackages[obj.PackageName()] = true
1005 }
1006}
1007
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001008// Generate the type and default constant definitions for this Descriptor.
1009func (g *Generator) generateMessage(message *Descriptor) {
1010 // The full type name
1011 typeName := message.TypeName()
1012 // The full type name, CamelCased.
1013 ccTypeName := CamelCaseSlice(typeName)
1014
1015 g.P("type ", ccTypeName, " struct {")
1016 g.In()
1017 for _, field := range message.Field {
1018 fieldname := CamelCase(*field.Name)
1019 typename, wiretype := g.GoType(message, field)
1020 tag := g.goTag(field, wiretype)
1021 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001022 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001023 }
1024 if len(message.ExtensionRange) > 0 {
1025 g.P("XXX_extensions\t\tmap[int32][]byte")
1026 }
1027 g.P("XXX_unrecognized\t[]byte")
1028 g.Out()
1029 g.P("}")
1030
Rob Pikec6d8e4a2010-07-28 15:34:32 -07001031 // Reset function
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001032 g.P("func (this *", ccTypeName, ") Reset() {")
1033 g.In()
1034 g.P("*this = ", ccTypeName, "{}")
1035 g.Out()
1036 g.P("}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001037
1038 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001039 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001040 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001041 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001042 // message_set_wire_format only makes sense when extensions are defined.
1043 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001044 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001045 g.P()
1046 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1047 g.In()
1048 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1049 g.Out()
1050 g.P("}")
1051 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1052 g.In()
1053 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1054 g.Out()
1055 g.P("}")
1056 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1057 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1058 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1059 }
1060
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001061 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001062 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001063 g.In()
1064 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001065 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1066 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001067 }
1068 g.Out()
1069 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001070 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001071 g.In()
1072 g.P("return extRange_", ccTypeName)
1073 g.Out()
1074 g.P("}")
1075 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1076 g.In()
1077 g.P("if this.XXX_extensions == nil {")
1078 g.In()
1079 g.P("this.XXX_extensions = make(map[int32][]byte)")
1080 g.Out()
1081 g.P("}")
1082 g.P("return this.XXX_extensions")
1083 g.Out()
1084 g.P("}")
1085 }
1086
David Symonds31d58a22011-01-20 18:33:21 +11001087 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1088
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001089 // Default constants
1090 for _, field := range message.Field {
1091 def := proto.GetString(field.DefaultValue)
1092 if def == "" {
1093 continue
1094 }
1095 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1096 typename, _ := g.GoType(message, field)
1097 if typename[0] == '*' {
1098 typename = typename[1:]
1099 }
1100 kind := "const "
1101 switch {
1102 case typename == "bool":
1103 case typename == "string":
1104 def = Quote(def)
1105 case typename == "[]byte":
1106 def = "[]byte(" + Quote(def) + ")"
1107 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001108 case def == "inf", def == "-inf", def == "nan":
1109 // These names are known to, and defined by, the protocol language.
1110 switch def {
1111 case "inf":
1112 def = "math.Inf(1)"
1113 case "-inf":
1114 def = "math.Inf(-1)"
1115 case "nan":
1116 def = "math.NaN()"
1117 }
1118 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1119 def = "float32(" + def + ")"
1120 }
1121 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001122 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1123 // Must be an enum. Need to construct the prefixed name.
1124 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1125 enum, ok := obj.(*EnumDescriptor)
1126 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001127 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001128 continue
1129 }
Rob Pike87af39e2010-07-19 10:48:02 -07001130 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001131 }
1132 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001133 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001134 }
1135 g.P()
1136
1137 for _, ext := range message.ext {
1138 g.generateExtension(ext)
1139 }
1140}
1141
1142func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
1143 // The full type name
1144 typeName := ext.TypeName()
1145 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
1146 for i, s := range typeName {
1147 typeName[i] = CamelCase(s)
1148 }
1149 ccTypeName := "E_" + strings.Join(typeName, "_")
1150
1151 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1152 field := ext.FieldDescriptorProto
1153 fieldType, wireType := g.GoType(ext.parent, field)
1154 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001155 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001156 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1157 // foreign extension type
1158 g.RecordTypeUse(*n)
1159 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001160
Rob Pikec9e7d972010-06-10 10:30:22 -07001161 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001162 g.In()
1163 g.P("ExtendedType: (", extendedType, ")(nil),")
1164 g.P("ExtensionType: (", fieldType, ")(nil),")
1165 g.P("Field: ", field.Number, ",")
1166 g.P("Tag: ", tag, ",")
1167
1168 g.Out()
1169 g.P("}")
1170 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001171
1172 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001173}
1174
1175func (g *Generator) generateInitFunction() {
1176 g.P("func init() {")
1177 g.In()
1178 for _, enum := range g.file.enum {
1179 g.generateEnumRegistration(enum)
1180 }
1181 g.Out()
1182 g.P("}")
1183}
1184
1185func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1186 pkg := g.packageName + "." // We always print the full package name here.
1187 // The full type name
1188 typeName := enum.TypeName()
1189 // The full type name, CamelCased.
1190 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001191 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001192}
1193
1194// And now lots of helper functions.
1195
Rob Pike2c7bafc2010-06-10 16:07:14 -07001196// Is c an ASCII lower-case letter?
1197func isASCIILower(c byte) bool {
1198 return 'a' <= c && c <= 'z'
1199}
1200
1201// Is c an ASCII digit?
1202func isASCIIDigit(c byte) bool {
1203 return '0' <= c && c <= '9'
1204}
1205
1206// CamelCase returns the CamelCased name.
1207// If there is an interior underscore followed by a lower case letter,
1208// drop the underscore and convert the letter to upper case.
1209// There is a remote possibility of this rewrite causing a name collision,
1210// but it's so remote we're prepared to pretend it's nonexistent - since the
1211// C++ generator lowercases names, it's extremely unlikely to have two fields
1212// with different capitalizations.
1213// In short, _my_field_name_2 becomes XMyFieldName2.
1214func CamelCase(s string) string {
1215 if s == "" {
1216 return ""
1217 }
1218 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001219 i := 0
1220 if s[0] == '_' {
1221 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001222 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001223 i++
1224 }
1225 // Invariant: if the next letter is lower case, it must be converted
1226 // to upper case.
1227 // That is, we process a word at a time, where words are marked by _ or
1228 // upper case letter. Digits are treated as words.
1229 for ; i < len(s); i++ {
1230 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001231 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1232 continue // Skip the underscore in s.
1233 }
1234 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001235 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001236 continue
1237 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001238 // Assume we have a letter now - if not, it's a bogus identifier.
1239 // The next word is a sequence of characters that must start upper case.
1240 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001241 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001242 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001243 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001244 // Accept lower case sequence that follows.
1245 for i+1 < len(s) && isASCIILower(s[i+1]) {
1246 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001247 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001248 }
1249 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001250 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001251}
1252
1253// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1254// be joined with "_".
1255func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1256
1257// dottedSlice turns a sliced name into a dotted name.
1258func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1259
1260// Quote returns a Go-source quoted string representation of s.
1261func Quote(s string) string { return fmt.Sprintf("%q", s) }
1262
1263// Given a .proto file name, return the output name for the generated Go program.
1264func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001265 ext := path.Ext(name)
1266 if ext == ".proto" || ext == ".protodevel" {
1267 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001268 }
1269 return name + ".pb.go"
1270}
1271
1272// Is this field optional?
1273func isOptional(field *descriptor.FieldDescriptorProto) bool {
1274 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1275}
1276
1277// Is this field required?
1278func isRequired(field *descriptor.FieldDescriptorProto) bool {
1279 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1280}
1281
1282// Is this field repeated?
1283func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1284 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1285}
1286
1287// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001288// which can be dotted in the input .proto file. It maps dots to underscores.
1289// Because we also get here from package names generated from file names, it also maps
1290// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001291func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001292 switch rune {
1293 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001294 return '_'
1295 }
1296 return rune
1297}
Rob Pikec9e7d972010-06-10 10:30:22 -07001298
1299// BaseName returns the last path element of the name, with the last dotted suffix removed.
1300func BaseName(name string) string {
1301 // First, find the last element
1302 if i := strings.LastIndex(name, "/"); i >= 0 {
1303 name = name[i+1:]
1304 }
1305 // Now drop the suffix
1306 if i := strings.LastIndex(name, "."); i >= 0 {
1307 name = name[0:i]
1308 }
1309 return name
1310}