blob: 4d0063d813172e4c561458f68cf40d25548b105d [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 Symonds832b2432010-11-11 10:55:27 +110052 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)
893 if name == CamelCase(name) {
894 name = ""
895 } else {
896 name = ",name=" + name
897 }
David Symonds5b7775e2010-12-01 10:09:04 +1100898 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s%s)",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700899 wiretype,
900 proto.GetInt32(field.Number),
901 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100902 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700903 name,
904 enum,
905 defaultValue))
906}
907
908func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
909 switch typ {
910 case descriptor.FieldDescriptorProto_TYPE_GROUP:
911 return false
912 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
913 return false
914 case descriptor.FieldDescriptorProto_TYPE_BYTES:
915 return false
916 }
917 return true
918}
919
920// TypeName is the printed name appropriate for an item. If the object is in the current file,
921// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -0700922// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700923// package name followed by the item name.
924// The result always has an initial capital.
925func (g *Generator) TypeName(obj Object) string {
926 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
927}
928
929// TypeNameWithPackage is like TypeName, but always includes the package
930// name even if the object is in our own package.
931func (g *Generator) TypeNameWithPackage(obj Object) string {
932 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
933}
934
935// GoType returns a string representing the type name, and the wire type
936func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
937 // TODO: Options.
938 switch *field.Type {
939 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
940 typ, wire = "float64", "fixed64"
941 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
942 typ, wire = "float32", "fixed32"
943 case descriptor.FieldDescriptorProto_TYPE_INT64:
944 typ, wire = "int64", "varint"
945 case descriptor.FieldDescriptorProto_TYPE_UINT64:
946 typ, wire = "uint64", "varint"
947 case descriptor.FieldDescriptorProto_TYPE_INT32:
948 typ, wire = "int32", "varint"
949 case descriptor.FieldDescriptorProto_TYPE_UINT32:
950 typ, wire = "uint32", "varint"
951 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
952 typ, wire = "uint64", "fixed64"
953 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
954 typ, wire = "uint32", "fixed32"
955 case descriptor.FieldDescriptorProto_TYPE_BOOL:
956 typ, wire = "bool", "varint"
957 case descriptor.FieldDescriptorProto_TYPE_STRING:
958 typ, wire = "string", "bytes"
959 case descriptor.FieldDescriptorProto_TYPE_GROUP:
960 desc := g.ObjectNamed(proto.GetString(field.TypeName))
961 typ, wire = "*"+g.TypeName(desc), "group"
962 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
963 desc := g.ObjectNamed(proto.GetString(field.TypeName))
964 typ, wire = "*"+g.TypeName(desc), "bytes"
965 case descriptor.FieldDescriptorProto_TYPE_BYTES:
966 typ, wire = "[]byte", "bytes"
967 case descriptor.FieldDescriptorProto_TYPE_ENUM:
968 desc := g.ObjectNamed(proto.GetString(field.TypeName))
969 typ, wire = g.TypeName(desc), "varint"
970 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
971 typ, wire = "int32", "fixed32"
972 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
973 typ, wire = "int64", "fixed64"
974 case descriptor.FieldDescriptorProto_TYPE_SINT32:
975 typ, wire = "int32", "zigzag32"
976 case descriptor.FieldDescriptorProto_TYPE_SINT64:
977 typ, wire = "int64", "zigzag64"
978 default:
979 g.Fail("unknown type for", proto.GetString(field.Name))
980 }
981 if isRepeated(field) {
982 typ = "[]" + typ
983 } else if needsStar(*field.Type) {
984 typ = "*" + typ
985 }
986 return
987}
988
David Symondsf90e3382010-05-05 10:53:44 +1000989func (g *Generator) RecordTypeUse(t string) {
990 if obj, ok := g.typeNameToObject[t]; ok {
991 g.usedPackages[obj.PackageName()] = true
992 }
993}
994
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700995// Generate the type and default constant definitions for this Descriptor.
996func (g *Generator) generateMessage(message *Descriptor) {
997 // The full type name
998 typeName := message.TypeName()
999 // The full type name, CamelCased.
1000 ccTypeName := CamelCaseSlice(typeName)
1001
1002 g.P("type ", ccTypeName, " struct {")
1003 g.In()
1004 for _, field := range message.Field {
1005 fieldname := CamelCase(*field.Name)
1006 typename, wiretype := g.GoType(message, field)
1007 tag := g.goTag(field, wiretype)
1008 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001009 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001010 }
1011 if len(message.ExtensionRange) > 0 {
1012 g.P("XXX_extensions\t\tmap[int32][]byte")
1013 }
1014 g.P("XXX_unrecognized\t[]byte")
1015 g.Out()
1016 g.P("}")
1017
Rob Pikec6d8e4a2010-07-28 15:34:32 -07001018 // Reset function
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001019 g.P("func (this *", ccTypeName, ") Reset() {")
1020 g.In()
1021 g.P("*this = ", ccTypeName, "{}")
1022 g.Out()
1023 g.P("}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001024
1025 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001026 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001027 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001028 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001029 // message_set_wire_format only makes sense when extensions are defined.
1030 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001031 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001032 g.P()
1033 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1034 g.In()
1035 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1036 g.Out()
1037 g.P("}")
1038 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1039 g.In()
1040 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1041 g.Out()
1042 g.P("}")
1043 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1044 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1045 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1046 }
1047
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001048 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001049 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001050 g.In()
1051 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001052 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1053 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001054 }
1055 g.Out()
1056 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001057 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001058 g.In()
1059 g.P("return extRange_", ccTypeName)
1060 g.Out()
1061 g.P("}")
1062 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1063 g.In()
1064 g.P("if this.XXX_extensions == nil {")
1065 g.In()
1066 g.P("this.XXX_extensions = make(map[int32][]byte)")
1067 g.Out()
1068 g.P("}")
1069 g.P("return this.XXX_extensions")
1070 g.Out()
1071 g.P("}")
1072 }
1073
David Symonds31d58a22011-01-20 18:33:21 +11001074 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1075
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001076 // Default constants
1077 for _, field := range message.Field {
1078 def := proto.GetString(field.DefaultValue)
1079 if def == "" {
1080 continue
1081 }
1082 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1083 typename, _ := g.GoType(message, field)
1084 if typename[0] == '*' {
1085 typename = typename[1:]
1086 }
1087 kind := "const "
1088 switch {
1089 case typename == "bool":
1090 case typename == "string":
1091 def = Quote(def)
1092 case typename == "[]byte":
1093 def = "[]byte(" + Quote(def) + ")"
1094 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001095 case def == "inf", def == "-inf", def == "nan":
1096 // These names are known to, and defined by, the protocol language.
1097 switch def {
1098 case "inf":
1099 def = "math.Inf(1)"
1100 case "-inf":
1101 def = "math.Inf(-1)"
1102 case "nan":
1103 def = "math.NaN()"
1104 }
1105 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1106 def = "float32(" + def + ")"
1107 }
1108 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001109 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1110 // Must be an enum. Need to construct the prefixed name.
1111 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1112 enum, ok := obj.(*EnumDescriptor)
1113 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001114 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001115 continue
1116 }
Rob Pike87af39e2010-07-19 10:48:02 -07001117 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001118 }
1119 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001120 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001121 }
1122 g.P()
1123
1124 for _, ext := range message.ext {
1125 g.generateExtension(ext)
1126 }
1127}
1128
1129func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
1130 // The full type name
1131 typeName := ext.TypeName()
1132 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
1133 for i, s := range typeName {
1134 typeName[i] = CamelCase(s)
1135 }
1136 ccTypeName := "E_" + strings.Join(typeName, "_")
1137
1138 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1139 field := ext.FieldDescriptorProto
1140 fieldType, wireType := g.GoType(ext.parent, field)
1141 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001142 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001143
Rob Pikec9e7d972010-06-10 10:30:22 -07001144 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001145 g.In()
1146 g.P("ExtendedType: (", extendedType, ")(nil),")
1147 g.P("ExtensionType: (", fieldType, ")(nil),")
1148 g.P("Field: ", field.Number, ",")
1149 g.P("Tag: ", tag, ",")
1150
1151 g.Out()
1152 g.P("}")
1153 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001154
1155 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001156}
1157
1158func (g *Generator) generateInitFunction() {
1159 g.P("func init() {")
1160 g.In()
1161 for _, enum := range g.file.enum {
1162 g.generateEnumRegistration(enum)
1163 }
1164 g.Out()
1165 g.P("}")
1166}
1167
1168func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1169 pkg := g.packageName + "." // We always print the full package name here.
1170 // The full type name
1171 typeName := enum.TypeName()
1172 // The full type name, CamelCased.
1173 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001174 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001175}
1176
1177// And now lots of helper functions.
1178
Rob Pike2c7bafc2010-06-10 16:07:14 -07001179// Is c an ASCII lower-case letter?
1180func isASCIILower(c byte) bool {
1181 return 'a' <= c && c <= 'z'
1182}
1183
1184// Is c an ASCII digit?
1185func isASCIIDigit(c byte) bool {
1186 return '0' <= c && c <= '9'
1187}
1188
1189// CamelCase returns the CamelCased name.
1190// If there is an interior underscore followed by a lower case letter,
1191// drop the underscore and convert the letter to upper case.
1192// There is a remote possibility of this rewrite causing a name collision,
1193// but it's so remote we're prepared to pretend it's nonexistent - since the
1194// C++ generator lowercases names, it's extremely unlikely to have two fields
1195// with different capitalizations.
1196// In short, _my_field_name_2 becomes XMyFieldName2.
1197func CamelCase(s string) string {
1198 if s == "" {
1199 return ""
1200 }
1201 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001202 i := 0
1203 if s[0] == '_' {
1204 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001205 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001206 i++
1207 }
1208 // Invariant: if the next letter is lower case, it must be converted
1209 // to upper case.
1210 // That is, we process a word at a time, where words are marked by _ or
1211 // upper case letter. Digits are treated as words.
1212 for ; i < len(s); i++ {
1213 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001214 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1215 continue // Skip the underscore in s.
1216 }
1217 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001218 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001219 continue
1220 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001221 // Assume we have a letter now - if not, it's a bogus identifier.
1222 // The next word is a sequence of characters that must start upper case.
1223 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001224 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001225 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001226 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001227 // Accept lower case sequence that follows.
1228 for i+1 < len(s) && isASCIILower(s[i+1]) {
1229 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001230 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001231 }
1232 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001233 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001234}
1235
1236// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1237// be joined with "_".
1238func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1239
1240// dottedSlice turns a sliced name into a dotted name.
1241func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1242
1243// Quote returns a Go-source quoted string representation of s.
1244func Quote(s string) string { return fmt.Sprintf("%q", s) }
1245
1246// Given a .proto file name, return the output name for the generated Go program.
1247func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001248 ext := path.Ext(name)
1249 if ext == ".proto" || ext == ".protodevel" {
1250 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001251 }
1252 return name + ".pb.go"
1253}
1254
1255// Is this field optional?
1256func isOptional(field *descriptor.FieldDescriptorProto) bool {
1257 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1258}
1259
1260// Is this field required?
1261func isRequired(field *descriptor.FieldDescriptorProto) bool {
1262 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1263}
1264
1265// Is this field repeated?
1266func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1267 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1268}
1269
1270// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001271// which can be dotted in the input .proto file. It maps dots to underscores.
1272// Because we also get here from package names generated from file names, it also maps
1273// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001274func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001275 switch rune {
1276 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001277 return '_'
1278 }
1279 return rune
1280}
Rob Pikec9e7d972010-06-10 10:30:22 -07001281
1282// BaseName returns the last path element of the name, with the last dotted suffix removed.
1283func BaseName(name string) string {
1284 // First, find the last element
1285 if i := strings.LastIndex(name, "/"); i >= 0 {
1286 name = name[i+1:]
1287 }
1288 // Now drop the suffix
1289 if i := strings.LastIndex(name, "."); i >= 0 {
1290 name = name[0:i]
1291 }
1292 return name
1293}