blob: 7c2a39ec7364873afc699e87fdd5c38116f65a1a [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"
42 "log"
43 "os"
Rob Pike87af39e2010-07-19 10:48:02 -070044 "path"
David Symonds79eae332010-10-16 11:33:20 +110045 "strconv"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070046 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070047
48 "goprotobuf.googlecode.com/hg/proto"
David Symonds832b2432010-11-11 10:55:27 +110049 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070050 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
51)
52
53// A Plugin provides functionality to add to the output during Go code generation,
54// such as to produce RPC stubs.
55type Plugin interface {
56 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070057 Name() string
58 // Init is called once after data structures are built but before
59 // code generation begins.
60 Init(g *Generator)
61 // Generate produces the code generated by the plugin for this file,
62 // except for the imports, by calling the generator's methods P, In, and Out.
63 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070064 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070065 // It is called after Generate.
66 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070067}
68
69var plugins []Plugin
70
71// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
72// It is typically called during initialization.
73func RegisterPlugin(p Plugin) {
David Symondscc7142e2010-11-06 14:37:15 +110074 plugins = append(plugins, p)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070075}
76
77// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
78// a pointer to the FileDescriptorProto that represents it. These types achieve that
79// wrapping by placing each Proto inside a struct with the pointer to its File. The
80// structs have the same names as their contents, with "Proto" removed.
81// FileDescriptor is used to store the things that it points to.
82
83// The file and package name method are common to messages and enums.
84type common struct {
85 File *descriptor.FileDescriptorProto // File this object comes from.
86}
87
88// PackageName is name in the package clause in the generated file.
89func (c *common) PackageName() string { return uniquePackageOf(c.File) }
90
91// Descriptor represents a protocol buffer message.
92type Descriptor struct {
93 common
94 *descriptor.DescriptorProto
95 parent *Descriptor // The containing message, if any.
96 nested []*Descriptor // Inner messages, if any.
97 ext []*ExtensionDescriptor // Extensions, if any.
98 typename []string // Cached typename vector.
99}
100
101// TypeName returns the elements of the dotted type name.
102// The package name is not part of this name.
103func (d *Descriptor) TypeName() []string {
104 if d.typename != nil {
105 return d.typename
106 }
107 n := 0
108 for parent := d; parent != nil; parent = parent.parent {
109 n++
110 }
111 s := make([]string, n, n)
112 for parent := d; parent != nil; parent = parent.parent {
113 n--
114 s[n] = proto.GetString(parent.Name)
115 }
116 d.typename = s
117 return s
118}
119
120// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
121// Otherwise it will be the descriptor of the message in which it is defined.
122type EnumDescriptor struct {
123 common
124 *descriptor.EnumDescriptorProto
125 parent *Descriptor // The containing message, if any.
126 typename []string // Cached typename vector.
127}
128
129// TypeName returns the elements of the dotted type name.
130// The package name is not part of this name.
131func (e *EnumDescriptor) TypeName() (s []string) {
132 if e.typename != nil {
133 return e.typename
134 }
135 name := proto.GetString(e.Name)
136 if e.parent == nil {
137 s = make([]string, 1)
138 } else {
139 pname := e.parent.TypeName()
140 s = make([]string, len(pname)+1)
141 copy(s, pname)
142 }
143 s[len(s)-1] = name
144 e.typename = s
145 return s
146}
147
148// Everything but the last element of the full type name, CamelCased.
149// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
150func (e *EnumDescriptor) prefix() string {
151 typeName := e.TypeName()
152 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
153 if e.parent == nil {
154 // If the enum is not part of a message, the prefix is just the type name.
155 ccPrefix = CamelCase(*e.Name) + "_"
156 }
157 return ccPrefix
158}
159
160// The integer value of the named constant in this enumerated type.
161func (e *EnumDescriptor) integerValueAsString(name string) string {
162 for _, c := range e.Value {
163 if proto.GetString(c.Name) == name {
164 return fmt.Sprint(proto.GetInt32(c.Number))
165 }
166 }
David Symonds9d0000e2011-02-03 10:48:14 +1100167 log.Fatal("cannot find value for enum constant")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700168 return ""
169}
170
171// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
172// Otherwise it will be the descriptor of the message in which it is defined.
173type ExtensionDescriptor struct {
174 common
175 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700176 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700177}
178
179// TypeName returns the elements of the dotted type name.
180// The package name is not part of this name.
181func (e *ExtensionDescriptor) TypeName() (s []string) {
182 name := proto.GetString(e.Name)
183 if e.parent == nil {
184 // top-level extension
185 s = make([]string, 1)
186 } else {
187 pname := e.parent.TypeName()
188 s = make([]string, len(pname)+1)
189 copy(s, pname)
190 }
191 s[len(s)-1] = name
192 return s
193}
194
195// FileDescriptor describes an protocol buffer descriptor file (.proto).
196// It includes slices of all the messages and enums defined within it.
197// Those slices are constructed by WrapTypes.
198type FileDescriptor struct {
199 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700200 desc []*Descriptor // All the messages defined in this file.
201 enum []*EnumDescriptor // All the enums defined in this file.
202 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds31d58a22011-01-20 18:33:21 +1100203
204 // The full list of symbols that are exported.
205 // This is used for supporting public imports.
206 exported []Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700207}
208
209// PackageName is the package name we'll use in the generated code to refer to this file.
210func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
211
212// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700213// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700214func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700215 // Does the file have a package clause?
David Symonds7d5c8242011-03-14 12:03:50 -0700216 if pkg := proto.GetString(d.Package); pkg != "" {
Rob Pikec9e7d972010-06-10 10:30:22 -0700217 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700218 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700219 // Use the file base name.
220 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700221}
222
David Symonds31d58a22011-01-20 18:33:21 +1100223func (d *FileDescriptor) addExport(symbol Symbol) {
224 d.exported = append(d.exported, symbol)
225}
226
227// Symbol is an interface representing an exported Go symbol.
228type Symbol interface {
229 // GenerateAlias should generate an appropriate alias
230 // for the symbol from the named package.
231 GenerateAlias(g *Generator, pkg string)
232}
233
234type messageSymbol struct {
235 sym string
236 hasExtensions, isMessageSet bool
237}
238
239func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
240 remoteSym := pkg + "." + ms.sym
241
242 g.P("type ", ms.sym, " ", remoteSym)
243 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
244 if ms.hasExtensions {
245 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
246 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
247 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32][]byte ",
248 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
249 if ms.isMessageSet {
250 g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
251 "{ return (*", remoteSym, ")(this).Marshal() }")
252 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
253 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
254 }
255 }
256}
257
258type enumSymbol string
259
260func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
261 s := string(es)
262 g.P("type ", s, " ", pkg, ".", s)
263 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
264 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
265 g.P("func New", s, "(x int32) *", s, " { e := ", s, "(x); return &e }")
266}
267
268type constOrVarSymbol struct {
269 sym string
270 typ string // either "const" or "var"
271}
272
273func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
274 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
275}
276
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700277// Object is an interface abstracting the abilities shared by enums and messages.
278type Object interface {
279 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
280 TypeName() []string
281}
282
283// Each package name we generate must be unique. The package we're generating
284// gets its own name but every other package must have a unqiue name that does
285// not conflict in the code we generate. These names are chosen globally (although
286// they don't have to be, it simplifies things to do them globally).
287func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
288 s, ok := uniquePackageName[fd]
289 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100290 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700291 }
292 return s
293}
294
295// Generator is the type whose methods generate the output, stored in the associated response structure.
296type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000297 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700298
299 Request *plugin.CodeGeneratorRequest // The input.
300 Response *plugin.CodeGeneratorResponse // The output.
301
Rob Pikec9e7d972010-06-10 10:30:22 -0700302 Param map[string]string // Command-line parameters.
303 ImportPrefix string // String to prefix to imported package file names.
304 ImportMap map[string]string // Mapping from import name to generated name
305
306 ProtoPkg string // The name under which we import the library's package proto.
307
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700308 packageName string // What we're calling ourselves.
309 allFiles []*FileDescriptor // All files in the tree
310 genFiles []*FileDescriptor // Those files we will generate output for.
311 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000312 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700313 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
314 indent string
315}
316
317// New creates a new generator and allocates the request and response protobufs.
318func New() *Generator {
319 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000320 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100321 g.Request = new(plugin.CodeGeneratorRequest)
322 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700323 return g
324}
325
326// Error reports a problem, including an os.Error, and exits the program.
327func (g *Generator) Error(err os.Error, msgs ...string) {
328 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700329 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700330 g.Response.Error = proto.String(s)
331 os.Exit(1)
332}
333
334// Fail reports a problem and exits the program.
335func (g *Generator) Fail(msgs ...string) {
336 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700337 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700338 g.Response.Error = proto.String(s)
339 os.Exit(1)
340}
341
Rob Pikec9e7d972010-06-10 10:30:22 -0700342// CommandLineParameters breaks the comma-separated list of key=value pairs
343// in the parameter (a member of the request protobuf) into a key/value map.
344// It then sets file name mappings defined by those entries.
345func (g *Generator) CommandLineParameters(parameter string) {
346 g.Param = make(map[string]string)
Rob Pike53385442010-06-30 22:22:43 -0700347 for _, p := range strings.Split(parameter, ",", -1) {
Rob Pikec9e7d972010-06-10 10:30:22 -0700348 if i := strings.Index(p, "="); i < 0 {
349 g.Param[p] = ""
350 } else {
351 g.Param[p[0:i]] = p[i+1:]
352 }
353 }
354
355 g.ImportMap = make(map[string]string)
356 for k, v := range g.Param {
357 if k == "import_prefix" {
358 g.ImportPrefix = v
359 } else if len(k) > 0 && k[0] == 'M' {
360 g.ImportMap[k[1:]] = v
361 }
362 }
363}
364
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700365// DefaultPackageName returns the package name printed for the object.
366// If its file is in a different package, it returns the package name we're using for this file, plus ".".
367// Otherwise it returns the empty string.
368func (g *Generator) DefaultPackageName(obj Object) string {
369 pkg := obj.PackageName()
370 if pkg == g.packageName {
371 return ""
372 }
373 return pkg + "."
374}
375
376// For each input file, the unique package name to use, underscored.
377var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700378// Package names already registered. Key is the name from the .proto file;
379// value is the name that appears in the generated code.
380var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700381
Rob Pikec9e7d972010-06-10 10:30:22 -0700382// Create and remember a guaranteed unique package name for this file descriptor.
383// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
384// has no file descriptor.
385func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds79eae332010-10-16 11:33:20 +1100386 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700387 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100388 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700389 }
390 // Install it.
391 pkgNamesInUse[pkg] = true
392 pkg = strings.Map(DotToUnderscore, pkg)
393 if f != nil {
394 uniquePackageName[f.FileDescriptorProto] = pkg
395 }
396 return pkg
397}
398
399// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700400// The package name must agree across all files being generated.
401// It also defines unique package names for all imported files.
402func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700403 // Register the name for this package. It will be the first name
404 // registered so is guaranteed to be unmodified.
405 pkg := g.genFiles[0].originalPackageName()
406 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
407 // Register the proto package name. It might collide with the
408 // name of a package we import.
409 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
David Symonds7d5c8242011-03-14 12:03:50 -0700410 // Verify that we are generating output for a single package.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700411 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700412 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700413 if thisPkg != pkg {
414 g.Fail("inconsistent package names:", thisPkg, pkg)
415 }
416 }
417AllFiles:
418 for _, f := range g.allFiles {
419 for _, genf := range g.genFiles {
420 if f == genf {
421 // In this package already.
422 uniquePackageName[f.FileDescriptorProto] = g.packageName
423 continue AllFiles
424 }
425 }
David Symonds7d5c8242011-03-14 12:03:50 -0700426 // The file is a dependency, so we want to ignore its go_package option
427 // because that is only relevant for its specific generated output.
428 pkg := proto.GetString(f.Package)
429 if pkg == "" {
430 pkg = BaseName(*f.Name)
431 }
432 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700433 }
434}
435
436// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
437// and FileDescriptorProtos into file-referenced objects within the Generator.
438// It also creates the list of files to generate and so should be called before GenerateAllFiles.
439func (g *Generator) WrapTypes() {
440 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
441 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700442 // We must wrap the descriptors before we wrap the enums
443 descs := wrapDescriptors(f)
444 g.buildNestedDescriptors(descs)
445 enums := wrapEnumDescriptors(f, descs)
446 exts := wrapExtensions(f)
447 g.allFiles[i] = &FileDescriptor{
448 FileDescriptorProto: f,
449 desc: descs,
450 enum: enums,
451 ext: exts,
452 }
453 }
454
455 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
456FindFiles:
457 for i, fileName := range g.Request.FileToGenerate {
458 // Search the list. This algorithm is n^2 but n is tiny.
459 for _, file := range g.allFiles {
460 if fileName == proto.GetString(file.Name) {
461 g.genFiles[i] = file
462 continue FindFiles
463 }
464 }
465 g.Fail("could not find file named", fileName)
466 }
467 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
468}
469
470// Scan the descriptors in this file. For each one, build the slice of nested descriptors
471func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
472 for _, desc := range descs {
473 if len(desc.NestedType) != 0 {
474 desc.nested = make([]*Descriptor, len(desc.NestedType))
475 n := 0
476 for _, nest := range descs {
477 if nest.parent == desc {
478 desc.nested[n] = nest
479 n++
480 }
481 }
482 if n != len(desc.NestedType) {
483 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
484 }
485 }
486 }
487}
488
489// Construct the Descriptor and add it to the slice
490func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
491 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
492
493 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
494 for i, field := range desc.Extension {
495 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
496 }
497
David Symondscc7142e2010-11-06 14:37:15 +1100498 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700499}
500
501// Return a slice of all the Descriptors defined within this file
502func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
503 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
504 for _, desc := range file.MessageType {
505 sl = wrapThisDescriptor(sl, desc, nil, file)
506 }
507 return sl
508}
509
510// Wrap this Descriptor, recursively
511func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
512 sl = addDescriptor(sl, desc, parent, file)
513 me := sl[len(sl)-1]
514 for _, nested := range desc.NestedType {
515 sl = wrapThisDescriptor(sl, nested, me, file)
516 }
517 return sl
518}
519
520// Construct the EnumDescriptor and add it to the slice
521func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symondscc7142e2010-11-06 14:37:15 +1100522 return append(sl, &EnumDescriptor{common{File: file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700523}
524
525// Return a slice of all the EnumDescriptors defined within this file
526func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
527 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000528 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700529 for _, enum := range file.EnumType {
530 sl = addEnumDescriptor(sl, enum, nil, file)
531 }
David Symonds5256cf62010-06-27 10:33:42 +1000532 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700533 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000534 for _, enum := range nested.EnumType {
535 sl = addEnumDescriptor(sl, enum, nested, file)
536 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700537 }
538 return sl
539}
540
541// Return a slice of all the top-level ExtensionDescriptors defined within this file.
542func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
543 sl := make([]*ExtensionDescriptor, len(file.Extension))
544 for i, field := range file.Extension {
545 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
546 }
547 return sl
548}
549
Rob Pikec9e7d972010-06-10 10:30:22 -0700550// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700551// The key names for the map come from the input data, which puts a period at the beginning.
552// It should be called after SetPackageNames and before GenerateAllFiles.
553func (g *Generator) BuildTypeNameMap() {
554 g.typeNameToObject = make(map[string]Object)
555 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700556 // The names in this loop are defined by the proto world, not us, so the
557 // package name may be empty. If so, the dotted package name of X will
558 // be ".X"; otherwise it will be ".pkg.X".
559 dottedPkg := "." + proto.GetString(f.Package)
560 if dottedPkg != "." {
561 dottedPkg += "."
562 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700563 for _, enum := range f.enum {
564 name := dottedPkg + dottedSlice(enum.TypeName())
565 g.typeNameToObject[name] = enum
566 }
567 for _, desc := range f.desc {
568 name := dottedPkg + dottedSlice(desc.TypeName())
569 g.typeNameToObject[name] = desc
570 }
571 }
572}
573
574// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
575// returns the descriptor for the message or enum with that name.
576func (g *Generator) ObjectNamed(typeName string) Object {
577 f, ok := g.typeNameToObject[typeName]
578 if !ok {
579 g.Fail("can't find object with type", typeName)
580 }
581 return f
582}
583
584// P prints the arguments to the generated output. It handles strings and int32s, plus
585// handling indirections because they may be *string, etc.
586func (g *Generator) P(str ...interface{}) {
587 g.WriteString(g.indent)
588 for _, v := range str {
589 switch s := v.(type) {
590 case string:
591 g.WriteString(s)
592 case *string:
593 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700594 case bool:
595 g.WriteString(fmt.Sprintf("%t", s))
596 case *bool:
597 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700598 case *int32:
599 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700600 case float64:
601 g.WriteString(fmt.Sprintf("%g", s))
602 case *float64:
603 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700604 default:
605 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
606 }
607 }
608 g.WriteByte('\n')
609}
610
611// In Indents the output one tab stop.
612func (g *Generator) In() { g.indent += "\t" }
613
614// Out unindents the output one tab stop.
615func (g *Generator) Out() {
616 if len(g.indent) > 0 {
617 g.indent = g.indent[1:]
618 }
619}
620
621// GenerateAllFiles generates the output for all the files we're outputting.
622func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700623 // Initialize the plugins
624 for _, p := range plugins {
625 p.Init(g)
626 }
David Symonds31d58a22011-01-20 18:33:21 +1100627 // Generate the output. The generator runs for every file, even the files
628 // that we don't generate output for, so that we can collate the full list
629 // of exported symbols to support public imports.
630 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
631 for _, file := range g.genFiles {
632 genFileMap[file] = true
633 }
634 i := 0
635 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700636 g.Reset()
637 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100638 if _, ok := genFileMap[file]; !ok {
639 continue
640 }
David Symondsb0127532010-11-09 11:10:46 +1100641 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700642 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
643 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100644 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700645 }
646}
647
648// Run all the plugins associated with the file.
649func (g *Generator) runPlugins(file *FileDescriptor) {
650 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700651 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700652 }
653}
654
655
656// FileOf return the FileDescriptor for this FileDescriptorProto.
657func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
658 for _, file := range g.allFiles {
659 if file.FileDescriptorProto == fd {
660 return file
661 }
662 }
663 g.Fail("could not find file in table:", proto.GetString(fd.Name))
664 return nil
665}
666
667// Fill the response protocol buffer with the generated output for all the files we're
668// supposed to generate.
669func (g *Generator) generate(file *FileDescriptor) {
670 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000671 g.usedPackages = make(map[string]bool)
672
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700673 for _, enum := range g.file.enum {
674 g.generateEnum(enum)
675 }
676 for _, desc := range g.file.desc {
677 g.generateMessage(desc)
678 }
679 for _, ext := range g.file.ext {
680 g.generateExtension(ext)
681 }
682 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000683
Rob Pikec9e7d972010-06-10 10:30:22 -0700684 // Run the plugins before the imports so we know which imports are necessary.
685 g.runPlugins(file)
686
David Symondsf90e3382010-05-05 10:53:44 +1000687 // Generate header and imports last, though they appear first in the output.
688 rem := g.Buffer
689 g.Buffer = new(bytes.Buffer)
690 g.generateHeader()
691 g.generateImports()
692 g.Write(rem.Bytes())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700693}
694
695// Generate the header, including package definition and imports
696func (g *Generator) generateHeader() {
697 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
698 g.P("// DO NOT EDIT!")
699 g.P()
700 g.P("package ", g.file.PackageName())
701 g.P()
702}
703
David Symonds31d58a22011-01-20 18:33:21 +1100704func (g *Generator) fileByName(filename string) *FileDescriptor {
705 for _, fd := range g.allFiles {
706 if proto.GetString(fd.Name) == filename {
707 return fd
708 }
709 }
710 return nil
711}
712
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700713// Generate the header, including package definition and imports
714func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700715 // We almost always need a proto import. Rather than computing when we
716 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100717 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700718 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100719 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100720 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700721 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100722 fd := g.fileByName(s)
723 // Do not import our own package.
724 if fd.PackageName() == g.packageName {
725 continue
726 }
727 filename := goFileName(s)
728 if substitution, ok := g.ImportMap[s]; ok {
729 filename = substitution
730 }
731 filename = g.ImportPrefix + filename
732 if strings.HasSuffix(filename, ".go") {
733 filename = filename[0 : len(filename)-3]
734 }
735 if _, ok := g.usedPackages[fd.PackageName()]; ok {
736 g.P("import ", fd.PackageName(), " ", Quote(filename))
737 } else {
738 log.Println("protoc-gen-go: discarding unused import:", filename)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700739 }
740 }
741 g.P()
742 // TODO: may need to worry about uniqueness across plugins
743 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700744 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700745 g.P()
746 }
David Symondscea785b2011-01-07 11:02:30 +1100747 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700748 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100749 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100750 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700751 g.P()
David Symonds31d58a22011-01-20 18:33:21 +1100752
753 // Symbols from public imports.
754 for _, index := range g.file.PublicDependency {
755 fd := g.fileByName(g.file.Dependency[index])
756 g.P("// Types from public import ", *fd.Name)
757 for _, sym := range fd.exported {
758 sym.GenerateAlias(g, fd.PackageName())
759 }
760 }
761 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700762}
763
764// Generate the enum definitions for this EnumDescriptor.
765func (g *Generator) generateEnum(enum *EnumDescriptor) {
766 // The full type name
767 typeName := enum.TypeName()
768 // The full type name, CamelCased.
769 ccTypeName := CamelCaseSlice(typeName)
770 ccPrefix := enum.prefix()
771 g.P("type ", ccTypeName, " int32")
David Symonds31d58a22011-01-20 18:33:21 +1100772 g.file.addExport(enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700773 g.P("const (")
774 g.In()
775 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100776 name := ccPrefix + *e.Name
777 g.P(name, " = ", e.Number)
778 g.file.addExport(constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700779 }
780 g.Out()
781 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100782 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700783 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700784 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700785 for _, e := range enum.Value {
786 duplicate := ""
787 if _, present := generated[*e.Number]; present {
788 duplicate = "// Duplicate value: "
789 }
790 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
791 generated[*e.Number] = true
792 }
793 g.Out()
794 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100795 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700796 g.In()
797 for _, e := range enum.Value {
798 g.P(Quote(*e.Name), ": ", e.Number, ",")
799 }
800 g.Out()
801 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100802
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700803 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
804 g.In()
805 g.P("e := ", ccTypeName, "(x)")
806 g.P("return &e")
807 g.Out()
808 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100809
810 g.P("func (x ", ccTypeName, ") String() string {")
811 g.In()
812 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
813 g.Out()
814 g.P("}")
815
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700816 g.P()
817}
818
819// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
820// identifies details of the field for the protocol buffer marshaling and unmarshaling
821// code. The fields are:
822// wire encoding
823// protocol tag number
824// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100825// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700826// name= the original declared name
827// enum= the name of the enum type if it is an enum-typed field.
828// def= string representation of the default value, if any.
829// The default value must be in a representation that can be used at run-time
830// to generate the default value. Thus bools become 0 and 1, for instance.
831func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
832 optrepreq := ""
833 switch {
834 case isOptional(field):
835 optrepreq = "opt"
836 case isRequired(field):
837 optrepreq = "req"
838 case isRepeated(field):
839 optrepreq = "rep"
840 }
841 defaultValue := proto.GetString(field.DefaultValue)
842 if defaultValue != "" {
843 switch *field.Type {
844 case descriptor.FieldDescriptorProto_TYPE_BOOL:
845 if defaultValue == "true" {
846 defaultValue = "1"
847 } else {
848 defaultValue = "0"
849 }
850 case descriptor.FieldDescriptorProto_TYPE_STRING,
851 descriptor.FieldDescriptorProto_TYPE_BYTES:
852 // Protect frogs.
853 defaultValue = Quote(defaultValue)
854 // Don't need the quotes
855 defaultValue = defaultValue[1 : len(defaultValue)-1]
856 case descriptor.FieldDescriptorProto_TYPE_ENUM:
857 // For enums we need to provide the integer constant.
858 obj := g.ObjectNamed(proto.GetString(field.TypeName))
859 enum, ok := obj.(*EnumDescriptor)
860 if !ok {
861 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
862 }
863 defaultValue = enum.integerValueAsString(defaultValue)
864 }
865 defaultValue = ",def=" + defaultValue
866 }
867 enum := ""
868 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
869 obj := g.ObjectNamed(proto.GetString(field.TypeName))
870 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
871 }
David Symonds5b7775e2010-12-01 10:09:04 +1100872 packed := ""
873 if field.Options != nil && proto.GetBool(field.Options.Packed) {
874 packed = ",packed"
875 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700876 name := proto.GetString(field.Name)
877 if name == CamelCase(name) {
878 name = ""
879 } else {
880 name = ",name=" + name
881 }
David Symonds5b7775e2010-12-01 10:09:04 +1100882 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s%s)",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700883 wiretype,
884 proto.GetInt32(field.Number),
885 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100886 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700887 name,
888 enum,
889 defaultValue))
890}
891
892func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
893 switch typ {
894 case descriptor.FieldDescriptorProto_TYPE_GROUP:
895 return false
896 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
897 return false
898 case descriptor.FieldDescriptorProto_TYPE_BYTES:
899 return false
900 }
901 return true
902}
903
904// TypeName is the printed name appropriate for an item. If the object is in the current file,
905// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -0700906// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700907// package name followed by the item name.
908// The result always has an initial capital.
909func (g *Generator) TypeName(obj Object) string {
910 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
911}
912
913// TypeNameWithPackage is like TypeName, but always includes the package
914// name even if the object is in our own package.
915func (g *Generator) TypeNameWithPackage(obj Object) string {
916 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
917}
918
919// GoType returns a string representing the type name, and the wire type
920func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
921 // TODO: Options.
922 switch *field.Type {
923 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
924 typ, wire = "float64", "fixed64"
925 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
926 typ, wire = "float32", "fixed32"
927 case descriptor.FieldDescriptorProto_TYPE_INT64:
928 typ, wire = "int64", "varint"
929 case descriptor.FieldDescriptorProto_TYPE_UINT64:
930 typ, wire = "uint64", "varint"
931 case descriptor.FieldDescriptorProto_TYPE_INT32:
932 typ, wire = "int32", "varint"
933 case descriptor.FieldDescriptorProto_TYPE_UINT32:
934 typ, wire = "uint32", "varint"
935 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
936 typ, wire = "uint64", "fixed64"
937 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
938 typ, wire = "uint32", "fixed32"
939 case descriptor.FieldDescriptorProto_TYPE_BOOL:
940 typ, wire = "bool", "varint"
941 case descriptor.FieldDescriptorProto_TYPE_STRING:
942 typ, wire = "string", "bytes"
943 case descriptor.FieldDescriptorProto_TYPE_GROUP:
944 desc := g.ObjectNamed(proto.GetString(field.TypeName))
945 typ, wire = "*"+g.TypeName(desc), "group"
946 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
947 desc := g.ObjectNamed(proto.GetString(field.TypeName))
948 typ, wire = "*"+g.TypeName(desc), "bytes"
949 case descriptor.FieldDescriptorProto_TYPE_BYTES:
950 typ, wire = "[]byte", "bytes"
951 case descriptor.FieldDescriptorProto_TYPE_ENUM:
952 desc := g.ObjectNamed(proto.GetString(field.TypeName))
953 typ, wire = g.TypeName(desc), "varint"
954 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
955 typ, wire = "int32", "fixed32"
956 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
957 typ, wire = "int64", "fixed64"
958 case descriptor.FieldDescriptorProto_TYPE_SINT32:
959 typ, wire = "int32", "zigzag32"
960 case descriptor.FieldDescriptorProto_TYPE_SINT64:
961 typ, wire = "int64", "zigzag64"
962 default:
963 g.Fail("unknown type for", proto.GetString(field.Name))
964 }
965 if isRepeated(field) {
966 typ = "[]" + typ
967 } else if needsStar(*field.Type) {
968 typ = "*" + typ
969 }
970 return
971}
972
David Symondsf90e3382010-05-05 10:53:44 +1000973func (g *Generator) RecordTypeUse(t string) {
974 if obj, ok := g.typeNameToObject[t]; ok {
975 g.usedPackages[obj.PackageName()] = true
976 }
977}
978
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700979// Generate the type and default constant definitions for this Descriptor.
980func (g *Generator) generateMessage(message *Descriptor) {
981 // The full type name
982 typeName := message.TypeName()
983 // The full type name, CamelCased.
984 ccTypeName := CamelCaseSlice(typeName)
985
986 g.P("type ", ccTypeName, " struct {")
987 g.In()
988 for _, field := range message.Field {
989 fieldname := CamelCase(*field.Name)
990 typename, wiretype := g.GoType(message, field)
991 tag := g.goTag(field, wiretype)
992 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000993 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700994 }
995 if len(message.ExtensionRange) > 0 {
996 g.P("XXX_extensions\t\tmap[int32][]byte")
997 }
998 g.P("XXX_unrecognized\t[]byte")
999 g.Out()
1000 g.P("}")
1001
Rob Pikec6d8e4a2010-07-28 15:34:32 -07001002 // Reset function
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001003 g.P("func (this *", ccTypeName, ") Reset() {")
1004 g.In()
1005 g.P("*this = ", ccTypeName, "{}")
1006 g.Out()
1007 g.P("}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001008
1009 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001010 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001011 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001012 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001013 // message_set_wire_format only makes sense when extensions are defined.
1014 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001015 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001016 g.P()
1017 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1018 g.In()
1019 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1020 g.Out()
1021 g.P("}")
1022 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1023 g.In()
1024 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1025 g.Out()
1026 g.P("}")
1027 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1028 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1029 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1030 }
1031
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001032 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001033 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001034 g.In()
1035 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001036 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1037 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001038 }
1039 g.Out()
1040 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001041 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001042 g.In()
1043 g.P("return extRange_", ccTypeName)
1044 g.Out()
1045 g.P("}")
1046 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1047 g.In()
1048 g.P("if this.XXX_extensions == nil {")
1049 g.In()
1050 g.P("this.XXX_extensions = make(map[int32][]byte)")
1051 g.Out()
1052 g.P("}")
1053 g.P("return this.XXX_extensions")
1054 g.Out()
1055 g.P("}")
1056 }
1057
David Symonds31d58a22011-01-20 18:33:21 +11001058 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1059
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001060 // Default constants
1061 for _, field := range message.Field {
1062 def := proto.GetString(field.DefaultValue)
1063 if def == "" {
1064 continue
1065 }
1066 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1067 typename, _ := g.GoType(message, field)
1068 if typename[0] == '*' {
1069 typename = typename[1:]
1070 }
1071 kind := "const "
1072 switch {
1073 case typename == "bool":
1074 case typename == "string":
1075 def = Quote(def)
1076 case typename == "[]byte":
1077 def = "[]byte(" + Quote(def) + ")"
1078 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001079 case def == "inf", def == "-inf", def == "nan":
1080 // These names are known to, and defined by, the protocol language.
1081 switch def {
1082 case "inf":
1083 def = "math.Inf(1)"
1084 case "-inf":
1085 def = "math.Inf(-1)"
1086 case "nan":
1087 def = "math.NaN()"
1088 }
1089 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1090 def = "float32(" + def + ")"
1091 }
1092 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001093 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1094 // Must be an enum. Need to construct the prefixed name.
1095 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1096 enum, ok := obj.(*EnumDescriptor)
1097 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001098 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001099 continue
1100 }
Rob Pike87af39e2010-07-19 10:48:02 -07001101 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001102 }
1103 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001104 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001105 }
1106 g.P()
1107
1108 for _, ext := range message.ext {
1109 g.generateExtension(ext)
1110 }
1111}
1112
1113func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
1114 // The full type name
1115 typeName := ext.TypeName()
1116 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
1117 for i, s := range typeName {
1118 typeName[i] = CamelCase(s)
1119 }
1120 ccTypeName := "E_" + strings.Join(typeName, "_")
1121
1122 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1123 field := ext.FieldDescriptorProto
1124 fieldType, wireType := g.GoType(ext.parent, field)
1125 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001126 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001127
Rob Pikec9e7d972010-06-10 10:30:22 -07001128 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001129 g.In()
1130 g.P("ExtendedType: (", extendedType, ")(nil),")
1131 g.P("ExtensionType: (", fieldType, ")(nil),")
1132 g.P("Field: ", field.Number, ",")
1133 g.P("Tag: ", tag, ",")
1134
1135 g.Out()
1136 g.P("}")
1137 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001138
1139 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001140}
1141
1142func (g *Generator) generateInitFunction() {
1143 g.P("func init() {")
1144 g.In()
1145 for _, enum := range g.file.enum {
1146 g.generateEnumRegistration(enum)
1147 }
1148 g.Out()
1149 g.P("}")
1150}
1151
1152func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1153 pkg := g.packageName + "." // We always print the full package name here.
1154 // The full type name
1155 typeName := enum.TypeName()
1156 // The full type name, CamelCased.
1157 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001158 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001159}
1160
1161// And now lots of helper functions.
1162
Rob Pike2c7bafc2010-06-10 16:07:14 -07001163// Is c an ASCII lower-case letter?
1164func isASCIILower(c byte) bool {
1165 return 'a' <= c && c <= 'z'
1166}
1167
1168// Is c an ASCII digit?
1169func isASCIIDigit(c byte) bool {
1170 return '0' <= c && c <= '9'
1171}
1172
1173// CamelCase returns the CamelCased name.
1174// If there is an interior underscore followed by a lower case letter,
1175// drop the underscore and convert the letter to upper case.
1176// There is a remote possibility of this rewrite causing a name collision,
1177// but it's so remote we're prepared to pretend it's nonexistent - since the
1178// C++ generator lowercases names, it's extremely unlikely to have two fields
1179// with different capitalizations.
1180// In short, _my_field_name_2 becomes XMyFieldName2.
1181func CamelCase(s string) string {
1182 if s == "" {
1183 return ""
1184 }
1185 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001186 i := 0
1187 if s[0] == '_' {
1188 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001189 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001190 i++
1191 }
1192 // Invariant: if the next letter is lower case, it must be converted
1193 // to upper case.
1194 // That is, we process a word at a time, where words are marked by _ or
1195 // upper case letter. Digits are treated as words.
1196 for ; i < len(s); i++ {
1197 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001198 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1199 continue // Skip the underscore in s.
1200 }
1201 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001202 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001203 continue
1204 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001205 // Assume we have a letter now - if not, it's a bogus identifier.
1206 // The next word is a sequence of characters that must start upper case.
1207 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001208 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001209 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001210 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001211 // Accept lower case sequence that follows.
1212 for i+1 < len(s) && isASCIILower(s[i+1]) {
1213 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001214 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001215 }
1216 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001217 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001218}
1219
1220// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1221// be joined with "_".
1222func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1223
1224// dottedSlice turns a sliced name into a dotted name.
1225func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1226
1227// Quote returns a Go-source quoted string representation of s.
1228func Quote(s string) string { return fmt.Sprintf("%q", s) }
1229
1230// Given a .proto file name, return the output name for the generated Go program.
1231func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001232 ext := path.Ext(name)
1233 if ext == ".proto" || ext == ".protodevel" {
1234 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001235 }
1236 return name + ".pb.go"
1237}
1238
1239// Is this field optional?
1240func isOptional(field *descriptor.FieldDescriptorProto) bool {
1241 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1242}
1243
1244// Is this field required?
1245func isRequired(field *descriptor.FieldDescriptorProto) bool {
1246 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1247}
1248
1249// Is this field repeated?
1250func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1251 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1252}
1253
1254// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001255// which can be dotted in the input .proto file. It maps dots to underscores.
1256// Because we also get here from package names generated from file names, it also maps
1257// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001258func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001259 switch rune {
1260 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001261 return '_'
1262 }
1263 return rune
1264}
Rob Pikec9e7d972010-06-10 10:30:22 -07001265
1266// BaseName returns the last path element of the name, with the last dotted suffix removed.
1267func BaseName(name string) string {
1268 // First, find the last element
1269 if i := strings.LastIndex(name, "/"); i >= 0 {
1270 name = name[i+1:]
1271 }
1272 // Now drop the suffix
1273 if i := strings.LastIndex(name, "."); i >= 0 {
1274 name = name[0:i]
1275 }
1276 return name
1277}