blob: 2d9365821f147f05e13caf812e4a43c11f1d1dfe [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 {
754 log.Println("protoc-gen-go: discarding unused import:", filename)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700755 }
756 }
757 g.P()
758 // TODO: may need to worry about uniqueness across plugins
759 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700760 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700761 g.P()
762 }
David Symondscea785b2011-01-07 11:02:30 +1100763 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700764 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100765 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100766 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700767 g.P()
David Symonds31d58a22011-01-20 18:33:21 +1100768
769 // Symbols from public imports.
770 for _, index := range g.file.PublicDependency {
771 fd := g.fileByName(g.file.Dependency[index])
772 g.P("// Types from public import ", *fd.Name)
773 for _, sym := range fd.exported {
774 sym.GenerateAlias(g, fd.PackageName())
775 }
776 }
777 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700778}
779
780// Generate the enum definitions for this EnumDescriptor.
781func (g *Generator) generateEnum(enum *EnumDescriptor) {
782 // The full type name
783 typeName := enum.TypeName()
784 // The full type name, CamelCased.
785 ccTypeName := CamelCaseSlice(typeName)
786 ccPrefix := enum.prefix()
787 g.P("type ", ccTypeName, " int32")
David Symonds31d58a22011-01-20 18:33:21 +1100788 g.file.addExport(enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700789 g.P("const (")
790 g.In()
791 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100792 name := ccPrefix + *e.Name
793 g.P(name, " = ", e.Number)
794 g.file.addExport(constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700795 }
796 g.Out()
797 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100798 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700799 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700800 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700801 for _, e := range enum.Value {
802 duplicate := ""
803 if _, present := generated[*e.Number]; present {
804 duplicate = "// Duplicate value: "
805 }
806 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
807 generated[*e.Number] = true
808 }
809 g.Out()
810 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100811 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700812 g.In()
813 for _, e := range enum.Value {
814 g.P(Quote(*e.Name), ": ", e.Number, ",")
815 }
816 g.Out()
817 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100818
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700819 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
820 g.In()
821 g.P("e := ", ccTypeName, "(x)")
822 g.P("return &e")
823 g.Out()
824 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100825
826 g.P("func (x ", ccTypeName, ") String() string {")
827 g.In()
828 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
829 g.Out()
830 g.P("}")
831
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700832 g.P()
833}
834
835// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
836// identifies details of the field for the protocol buffer marshaling and unmarshaling
837// code. The fields are:
838// wire encoding
839// protocol tag number
840// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100841// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700842// name= the original declared name
843// enum= the name of the enum type if it is an enum-typed field.
844// def= string representation of the default value, if any.
845// The default value must be in a representation that can be used at run-time
846// to generate the default value. Thus bools become 0 and 1, for instance.
847func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
848 optrepreq := ""
849 switch {
850 case isOptional(field):
851 optrepreq = "opt"
852 case isRequired(field):
853 optrepreq = "req"
854 case isRepeated(field):
855 optrepreq = "rep"
856 }
857 defaultValue := proto.GetString(field.DefaultValue)
858 if defaultValue != "" {
859 switch *field.Type {
860 case descriptor.FieldDescriptorProto_TYPE_BOOL:
861 if defaultValue == "true" {
862 defaultValue = "1"
863 } else {
864 defaultValue = "0"
865 }
866 case descriptor.FieldDescriptorProto_TYPE_STRING,
867 descriptor.FieldDescriptorProto_TYPE_BYTES:
868 // Protect frogs.
869 defaultValue = Quote(defaultValue)
870 // Don't need the quotes
871 defaultValue = defaultValue[1 : len(defaultValue)-1]
872 case descriptor.FieldDescriptorProto_TYPE_ENUM:
873 // For enums we need to provide the integer constant.
874 obj := g.ObjectNamed(proto.GetString(field.TypeName))
875 enum, ok := obj.(*EnumDescriptor)
876 if !ok {
877 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
878 }
879 defaultValue = enum.integerValueAsString(defaultValue)
880 }
881 defaultValue = ",def=" + defaultValue
882 }
883 enum := ""
884 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
885 obj := g.ObjectNamed(proto.GetString(field.TypeName))
886 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
887 }
David Symonds5b7775e2010-12-01 10:09:04 +1100888 packed := ""
889 if field.Options != nil && proto.GetBool(field.Options.Packed) {
890 packed = ",packed"
891 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700892 name := proto.GetString(field.Name)
David Symonds9f402812011-04-28 18:08:44 +1000893 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
894 // We must use the type name for groups instead of
895 // the field name to preserve capitalization.
896 // type_name in FieldDescriptorProto is fully-qualified,
897 // but we only want the local part.
898 name = *field.TypeName
899 if i := strings.LastIndex(name, "."); i >= 0 {
900 name = name[i+1:]
901 }
902 name = ",name=" + name
903 } else if name == CamelCase(name) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700904 name = ""
905 } else {
906 name = ",name=" + name
907 }
David Symonds5b7775e2010-12-01 10:09:04 +1100908 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s%s)",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700909 wiretype,
910 proto.GetInt32(field.Number),
911 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100912 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700913 name,
914 enum,
915 defaultValue))
916}
917
918func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
919 switch typ {
920 case descriptor.FieldDescriptorProto_TYPE_GROUP:
921 return false
922 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
923 return false
924 case descriptor.FieldDescriptorProto_TYPE_BYTES:
925 return false
926 }
927 return true
928}
929
930// TypeName is the printed name appropriate for an item. If the object is in the current file,
931// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -0700932// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700933// package name followed by the item name.
934// The result always has an initial capital.
935func (g *Generator) TypeName(obj Object) string {
936 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
937}
938
939// TypeNameWithPackage is like TypeName, but always includes the package
940// name even if the object is in our own package.
941func (g *Generator) TypeNameWithPackage(obj Object) string {
942 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
943}
944
945// GoType returns a string representing the type name, and the wire type
946func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
947 // TODO: Options.
948 switch *field.Type {
949 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
950 typ, wire = "float64", "fixed64"
951 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
952 typ, wire = "float32", "fixed32"
953 case descriptor.FieldDescriptorProto_TYPE_INT64:
954 typ, wire = "int64", "varint"
955 case descriptor.FieldDescriptorProto_TYPE_UINT64:
956 typ, wire = "uint64", "varint"
957 case descriptor.FieldDescriptorProto_TYPE_INT32:
958 typ, wire = "int32", "varint"
959 case descriptor.FieldDescriptorProto_TYPE_UINT32:
960 typ, wire = "uint32", "varint"
961 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
962 typ, wire = "uint64", "fixed64"
963 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
964 typ, wire = "uint32", "fixed32"
965 case descriptor.FieldDescriptorProto_TYPE_BOOL:
966 typ, wire = "bool", "varint"
967 case descriptor.FieldDescriptorProto_TYPE_STRING:
968 typ, wire = "string", "bytes"
969 case descriptor.FieldDescriptorProto_TYPE_GROUP:
970 desc := g.ObjectNamed(proto.GetString(field.TypeName))
971 typ, wire = "*"+g.TypeName(desc), "group"
972 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
973 desc := g.ObjectNamed(proto.GetString(field.TypeName))
974 typ, wire = "*"+g.TypeName(desc), "bytes"
975 case descriptor.FieldDescriptorProto_TYPE_BYTES:
976 typ, wire = "[]byte", "bytes"
977 case descriptor.FieldDescriptorProto_TYPE_ENUM:
978 desc := g.ObjectNamed(proto.GetString(field.TypeName))
979 typ, wire = g.TypeName(desc), "varint"
980 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
981 typ, wire = "int32", "fixed32"
982 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
983 typ, wire = "int64", "fixed64"
984 case descriptor.FieldDescriptorProto_TYPE_SINT32:
985 typ, wire = "int32", "zigzag32"
986 case descriptor.FieldDescriptorProto_TYPE_SINT64:
987 typ, wire = "int64", "zigzag64"
988 default:
989 g.Fail("unknown type for", proto.GetString(field.Name))
990 }
991 if isRepeated(field) {
992 typ = "[]" + typ
993 } else if needsStar(*field.Type) {
994 typ = "*" + typ
995 }
996 return
997}
998
David Symondsf90e3382010-05-05 10:53:44 +1000999func (g *Generator) RecordTypeUse(t string) {
1000 if obj, ok := g.typeNameToObject[t]; ok {
1001 g.usedPackages[obj.PackageName()] = true
1002 }
1003}
1004
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001005// Generate the type and default constant definitions for this Descriptor.
1006func (g *Generator) generateMessage(message *Descriptor) {
1007 // The full type name
1008 typeName := message.TypeName()
1009 // The full type name, CamelCased.
1010 ccTypeName := CamelCaseSlice(typeName)
1011
1012 g.P("type ", ccTypeName, " struct {")
1013 g.In()
1014 for _, field := range message.Field {
1015 fieldname := CamelCase(*field.Name)
1016 typename, wiretype := g.GoType(message, field)
1017 tag := g.goTag(field, wiretype)
1018 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001019 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001020 }
1021 if len(message.ExtensionRange) > 0 {
1022 g.P("XXX_extensions\t\tmap[int32][]byte")
1023 }
1024 g.P("XXX_unrecognized\t[]byte")
1025 g.Out()
1026 g.P("}")
1027
Rob Pikec6d8e4a2010-07-28 15:34:32 -07001028 // Reset function
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001029 g.P("func (this *", ccTypeName, ") Reset() {")
1030 g.In()
1031 g.P("*this = ", ccTypeName, "{}")
1032 g.Out()
1033 g.P("}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001034
1035 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001036 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001037 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001038 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001039 // message_set_wire_format only makes sense when extensions are defined.
1040 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001041 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001042 g.P()
1043 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1044 g.In()
1045 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1046 g.Out()
1047 g.P("}")
1048 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1049 g.In()
1050 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1051 g.Out()
1052 g.P("}")
1053 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1054 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1055 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1056 }
1057
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001058 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001059 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001060 g.In()
1061 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001062 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1063 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001064 }
1065 g.Out()
1066 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001067 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001068 g.In()
1069 g.P("return extRange_", ccTypeName)
1070 g.Out()
1071 g.P("}")
1072 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1073 g.In()
1074 g.P("if this.XXX_extensions == nil {")
1075 g.In()
1076 g.P("this.XXX_extensions = make(map[int32][]byte)")
1077 g.Out()
1078 g.P("}")
1079 g.P("return this.XXX_extensions")
1080 g.Out()
1081 g.P("}")
1082 }
1083
David Symonds31d58a22011-01-20 18:33:21 +11001084 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1085
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001086 // Default constants
1087 for _, field := range message.Field {
1088 def := proto.GetString(field.DefaultValue)
1089 if def == "" {
1090 continue
1091 }
1092 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1093 typename, _ := g.GoType(message, field)
1094 if typename[0] == '*' {
1095 typename = typename[1:]
1096 }
1097 kind := "const "
1098 switch {
1099 case typename == "bool":
1100 case typename == "string":
1101 def = Quote(def)
1102 case typename == "[]byte":
1103 def = "[]byte(" + Quote(def) + ")"
1104 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001105 case def == "inf", def == "-inf", def == "nan":
1106 // These names are known to, and defined by, the protocol language.
1107 switch def {
1108 case "inf":
1109 def = "math.Inf(1)"
1110 case "-inf":
1111 def = "math.Inf(-1)"
1112 case "nan":
1113 def = "math.NaN()"
1114 }
1115 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1116 def = "float32(" + def + ")"
1117 }
1118 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001119 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1120 // Must be an enum. Need to construct the prefixed name.
1121 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1122 enum, ok := obj.(*EnumDescriptor)
1123 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001124 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001125 continue
1126 }
Rob Pike87af39e2010-07-19 10:48:02 -07001127 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001128 }
1129 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001130 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001131 }
1132 g.P()
1133
1134 for _, ext := range message.ext {
1135 g.generateExtension(ext)
1136 }
1137}
1138
1139func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
1140 // The full type name
1141 typeName := ext.TypeName()
1142 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
1143 for i, s := range typeName {
1144 typeName[i] = CamelCase(s)
1145 }
1146 ccTypeName := "E_" + strings.Join(typeName, "_")
1147
1148 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1149 field := ext.FieldDescriptorProto
1150 fieldType, wireType := g.GoType(ext.parent, field)
1151 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001152 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001153 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1154 // foreign extension type
1155 g.RecordTypeUse(*n)
1156 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001157
Rob Pikec9e7d972010-06-10 10:30:22 -07001158 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001159 g.In()
1160 g.P("ExtendedType: (", extendedType, ")(nil),")
1161 g.P("ExtensionType: (", fieldType, ")(nil),")
1162 g.P("Field: ", field.Number, ",")
1163 g.P("Tag: ", tag, ",")
1164
1165 g.Out()
1166 g.P("}")
1167 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001168
1169 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001170}
1171
1172func (g *Generator) generateInitFunction() {
1173 g.P("func init() {")
1174 g.In()
1175 for _, enum := range g.file.enum {
1176 g.generateEnumRegistration(enum)
1177 }
1178 g.Out()
1179 g.P("}")
1180}
1181
1182func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1183 pkg := g.packageName + "." // We always print the full package name here.
1184 // The full type name
1185 typeName := enum.TypeName()
1186 // The full type name, CamelCased.
1187 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001188 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001189}
1190
1191// And now lots of helper functions.
1192
Rob Pike2c7bafc2010-06-10 16:07:14 -07001193// Is c an ASCII lower-case letter?
1194func isASCIILower(c byte) bool {
1195 return 'a' <= c && c <= 'z'
1196}
1197
1198// Is c an ASCII digit?
1199func isASCIIDigit(c byte) bool {
1200 return '0' <= c && c <= '9'
1201}
1202
1203// CamelCase returns the CamelCased name.
1204// If there is an interior underscore followed by a lower case letter,
1205// drop the underscore and convert the letter to upper case.
1206// There is a remote possibility of this rewrite causing a name collision,
1207// but it's so remote we're prepared to pretend it's nonexistent - since the
1208// C++ generator lowercases names, it's extremely unlikely to have two fields
1209// with different capitalizations.
1210// In short, _my_field_name_2 becomes XMyFieldName2.
1211func CamelCase(s string) string {
1212 if s == "" {
1213 return ""
1214 }
1215 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001216 i := 0
1217 if s[0] == '_' {
1218 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001219 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001220 i++
1221 }
1222 // Invariant: if the next letter is lower case, it must be converted
1223 // to upper case.
1224 // That is, we process a word at a time, where words are marked by _ or
1225 // upper case letter. Digits are treated as words.
1226 for ; i < len(s); i++ {
1227 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001228 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1229 continue // Skip the underscore in s.
1230 }
1231 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001232 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001233 continue
1234 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001235 // Assume we have a letter now - if not, it's a bogus identifier.
1236 // The next word is a sequence of characters that must start upper case.
1237 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001238 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001239 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001240 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001241 // Accept lower case sequence that follows.
1242 for i+1 < len(s) && isASCIILower(s[i+1]) {
1243 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001244 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001245 }
1246 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001247 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001248}
1249
1250// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1251// be joined with "_".
1252func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1253
1254// dottedSlice turns a sliced name into a dotted name.
1255func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1256
1257// Quote returns a Go-source quoted string representation of s.
1258func Quote(s string) string { return fmt.Sprintf("%q", s) }
1259
1260// Given a .proto file name, return the output name for the generated Go program.
1261func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001262 ext := path.Ext(name)
1263 if ext == ".proto" || ext == ".protodevel" {
1264 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001265 }
1266 return name + ".pb.go"
1267}
1268
1269// Is this field optional?
1270func isOptional(field *descriptor.FieldDescriptorProto) bool {
1271 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1272}
1273
1274// Is this field required?
1275func isRequired(field *descriptor.FieldDescriptorProto) bool {
1276 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1277}
1278
1279// Is this field repeated?
1280func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1281 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1282}
1283
1284// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001285// which can be dotted in the input .proto file. It maps dots to underscores.
1286// Because we also get here from package names generated from file names, it also maps
1287// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001288func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001289 switch rune {
1290 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001291 return '_'
1292 }
1293 return rune
1294}
Rob Pikec9e7d972010-06-10 10:30:22 -07001295
1296// BaseName returns the last path element of the name, with the last dotted suffix removed.
1297func BaseName(name string) string {
1298 // First, find the last element
1299 if i := strings.LastIndex(name, "/"); i >= 0 {
1300 name = name[i+1:]
1301 }
1302 // Now drop the suffix
1303 if i := strings.LastIndex(name, "."); i >= 0 {
1304 name = name[0:i]
1305 }
1306 return name
1307}