blob: 28c978cb246e27161b5118ba6713b83236f0523f [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?
216 pkg := proto.GetString(d.Package)
217 if pkg != "" {
218 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700219 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700220 // Use the file base name.
221 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700222}
223
David Symonds31d58a22011-01-20 18:33:21 +1100224func (d *FileDescriptor) addExport(symbol Symbol) {
225 d.exported = append(d.exported, symbol)
226}
227
228// Symbol is an interface representing an exported Go symbol.
229type Symbol interface {
230 // GenerateAlias should generate an appropriate alias
231 // for the symbol from the named package.
232 GenerateAlias(g *Generator, pkg string)
233}
234
235type messageSymbol struct {
236 sym string
237 hasExtensions, isMessageSet bool
238}
239
240func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
241 remoteSym := pkg + "." + ms.sym
242
243 g.P("type ", ms.sym, " ", remoteSym)
244 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
245 if ms.hasExtensions {
246 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
247 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
248 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32][]byte ",
249 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
250 if ms.isMessageSet {
251 g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
252 "{ return (*", remoteSym, ")(this).Marshal() }")
253 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
254 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
255 }
256 }
257}
258
259type enumSymbol string
260
261func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
262 s := string(es)
263 g.P("type ", s, " ", pkg, ".", s)
264 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
265 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
266 g.P("func New", s, "(x int32) *", s, " { e := ", s, "(x); return &e }")
267}
268
269type constOrVarSymbol struct {
270 sym string
271 typ string // either "const" or "var"
272}
273
274func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
275 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
276}
277
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700278// Object is an interface abstracting the abilities shared by enums and messages.
279type Object interface {
280 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
281 TypeName() []string
282}
283
284// Each package name we generate must be unique. The package we're generating
285// gets its own name but every other package must have a unqiue name that does
286// not conflict in the code we generate. These names are chosen globally (although
287// they don't have to be, it simplifies things to do them globally).
288func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
289 s, ok := uniquePackageName[fd]
290 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100291 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700292 }
293 return s
294}
295
296// Generator is the type whose methods generate the output, stored in the associated response structure.
297type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000298 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700299
300 Request *plugin.CodeGeneratorRequest // The input.
301 Response *plugin.CodeGeneratorResponse // The output.
302
Rob Pikec9e7d972010-06-10 10:30:22 -0700303 Param map[string]string // Command-line parameters.
304 ImportPrefix string // String to prefix to imported package file names.
305 ImportMap map[string]string // Mapping from import name to generated name
306
307 ProtoPkg string // The name under which we import the library's package proto.
308
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700309 packageName string // What we're calling ourselves.
310 allFiles []*FileDescriptor // All files in the tree
311 genFiles []*FileDescriptor // Those files we will generate output for.
312 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000313 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700314 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
315 indent string
316}
317
318// New creates a new generator and allocates the request and response protobufs.
319func New() *Generator {
320 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000321 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100322 g.Request = new(plugin.CodeGeneratorRequest)
323 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700324 return g
325}
326
327// Error reports a problem, including an os.Error, and exits the program.
328func (g *Generator) Error(err os.Error, msgs ...string) {
329 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700330 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700331 g.Response.Error = proto.String(s)
332 os.Exit(1)
333}
334
335// Fail reports a problem and exits the program.
336func (g *Generator) Fail(msgs ...string) {
337 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700338 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700339 g.Response.Error = proto.String(s)
340 os.Exit(1)
341}
342
Rob Pikec9e7d972010-06-10 10:30:22 -0700343// CommandLineParameters breaks the comma-separated list of key=value pairs
344// in the parameter (a member of the request protobuf) into a key/value map.
345// It then sets file name mappings defined by those entries.
346func (g *Generator) CommandLineParameters(parameter string) {
347 g.Param = make(map[string]string)
Rob Pike53385442010-06-30 22:22:43 -0700348 for _, p := range strings.Split(parameter, ",", -1) {
Rob Pikec9e7d972010-06-10 10:30:22 -0700349 if i := strings.Index(p, "="); i < 0 {
350 g.Param[p] = ""
351 } else {
352 g.Param[p[0:i]] = p[i+1:]
353 }
354 }
355
356 g.ImportMap = make(map[string]string)
357 for k, v := range g.Param {
358 if k == "import_prefix" {
359 g.ImportPrefix = v
360 } else if len(k) > 0 && k[0] == 'M' {
361 g.ImportMap[k[1:]] = v
362 }
363 }
364}
365
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700366// DefaultPackageName returns the package name printed for the object.
367// If its file is in a different package, it returns the package name we're using for this file, plus ".".
368// Otherwise it returns the empty string.
369func (g *Generator) DefaultPackageName(obj Object) string {
370 pkg := obj.PackageName()
371 if pkg == g.packageName {
372 return ""
373 }
374 return pkg + "."
375}
376
377// For each input file, the unique package name to use, underscored.
378var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700379// Package names already registered. Key is the name from the .proto file;
380// value is the name that appears in the generated code.
381var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700382
Rob Pikec9e7d972010-06-10 10:30:22 -0700383// Create and remember a guaranteed unique package name for this file descriptor.
384// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
385// has no file descriptor.
386func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds79eae332010-10-16 11:33:20 +1100387 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700388 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100389 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700390 }
391 // Install it.
392 pkgNamesInUse[pkg] = true
393 pkg = strings.Map(DotToUnderscore, pkg)
394 if f != nil {
395 uniquePackageName[f.FileDescriptorProto] = pkg
396 }
397 return pkg
398}
399
400// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700401// The package name must agree across all files being generated.
402// It also defines unique package names for all imported files.
403func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700404 // Register the name for this package. It will be the first name
405 // registered so is guaranteed to be unmodified.
406 pkg := g.genFiles[0].originalPackageName()
407 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
408 // Register the proto package name. It might collide with the
409 // name of a package we import.
410 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
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 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700426 RegisterUniquePackageName(f.originalPackageName(), f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700427 }
428}
429
430// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
431// and FileDescriptorProtos into file-referenced objects within the Generator.
432// It also creates the list of files to generate and so should be called before GenerateAllFiles.
433func (g *Generator) WrapTypes() {
434 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
435 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700436 // We must wrap the descriptors before we wrap the enums
437 descs := wrapDescriptors(f)
438 g.buildNestedDescriptors(descs)
439 enums := wrapEnumDescriptors(f, descs)
440 exts := wrapExtensions(f)
441 g.allFiles[i] = &FileDescriptor{
442 FileDescriptorProto: f,
443 desc: descs,
444 enum: enums,
445 ext: exts,
446 }
447 }
448
449 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
450FindFiles:
451 for i, fileName := range g.Request.FileToGenerate {
452 // Search the list. This algorithm is n^2 but n is tiny.
453 for _, file := range g.allFiles {
454 if fileName == proto.GetString(file.Name) {
455 g.genFiles[i] = file
456 continue FindFiles
457 }
458 }
459 g.Fail("could not find file named", fileName)
460 }
461 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
462}
463
464// Scan the descriptors in this file. For each one, build the slice of nested descriptors
465func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
466 for _, desc := range descs {
467 if len(desc.NestedType) != 0 {
468 desc.nested = make([]*Descriptor, len(desc.NestedType))
469 n := 0
470 for _, nest := range descs {
471 if nest.parent == desc {
472 desc.nested[n] = nest
473 n++
474 }
475 }
476 if n != len(desc.NestedType) {
477 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
478 }
479 }
480 }
481}
482
483// Construct the Descriptor and add it to the slice
484func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
485 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
486
487 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
488 for i, field := range desc.Extension {
489 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
490 }
491
David Symondscc7142e2010-11-06 14:37:15 +1100492 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700493}
494
495// Return a slice of all the Descriptors defined within this file
496func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
497 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
498 for _, desc := range file.MessageType {
499 sl = wrapThisDescriptor(sl, desc, nil, file)
500 }
501 return sl
502}
503
504// Wrap this Descriptor, recursively
505func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
506 sl = addDescriptor(sl, desc, parent, file)
507 me := sl[len(sl)-1]
508 for _, nested := range desc.NestedType {
509 sl = wrapThisDescriptor(sl, nested, me, file)
510 }
511 return sl
512}
513
514// Construct the EnumDescriptor and add it to the slice
515func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symondscc7142e2010-11-06 14:37:15 +1100516 return append(sl, &EnumDescriptor{common{File: file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700517}
518
519// Return a slice of all the EnumDescriptors defined within this file
520func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
521 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000522 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700523 for _, enum := range file.EnumType {
524 sl = addEnumDescriptor(sl, enum, nil, file)
525 }
David Symonds5256cf62010-06-27 10:33:42 +1000526 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700527 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000528 for _, enum := range nested.EnumType {
529 sl = addEnumDescriptor(sl, enum, nested, file)
530 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700531 }
532 return sl
533}
534
535// Return a slice of all the top-level ExtensionDescriptors defined within this file.
536func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
537 sl := make([]*ExtensionDescriptor, len(file.Extension))
538 for i, field := range file.Extension {
539 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
540 }
541 return sl
542}
543
Rob Pikec9e7d972010-06-10 10:30:22 -0700544// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700545// The key names for the map come from the input data, which puts a period at the beginning.
546// It should be called after SetPackageNames and before GenerateAllFiles.
547func (g *Generator) BuildTypeNameMap() {
548 g.typeNameToObject = make(map[string]Object)
549 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700550 // The names in this loop are defined by the proto world, not us, so the
551 // package name may be empty. If so, the dotted package name of X will
552 // be ".X"; otherwise it will be ".pkg.X".
553 dottedPkg := "." + proto.GetString(f.Package)
554 if dottedPkg != "." {
555 dottedPkg += "."
556 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700557 for _, enum := range f.enum {
558 name := dottedPkg + dottedSlice(enum.TypeName())
559 g.typeNameToObject[name] = enum
560 }
561 for _, desc := range f.desc {
562 name := dottedPkg + dottedSlice(desc.TypeName())
563 g.typeNameToObject[name] = desc
564 }
565 }
566}
567
568// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
569// returns the descriptor for the message or enum with that name.
570func (g *Generator) ObjectNamed(typeName string) Object {
571 f, ok := g.typeNameToObject[typeName]
572 if !ok {
573 g.Fail("can't find object with type", typeName)
574 }
575 return f
576}
577
578// P prints the arguments to the generated output. It handles strings and int32s, plus
579// handling indirections because they may be *string, etc.
580func (g *Generator) P(str ...interface{}) {
581 g.WriteString(g.indent)
582 for _, v := range str {
583 switch s := v.(type) {
584 case string:
585 g.WriteString(s)
586 case *string:
587 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700588 case bool:
589 g.WriteString(fmt.Sprintf("%t", s))
590 case *bool:
591 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700592 case *int32:
593 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700594 case float64:
595 g.WriteString(fmt.Sprintf("%g", s))
596 case *float64:
597 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700598 default:
599 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
600 }
601 }
602 g.WriteByte('\n')
603}
604
605// In Indents the output one tab stop.
606func (g *Generator) In() { g.indent += "\t" }
607
608// Out unindents the output one tab stop.
609func (g *Generator) Out() {
610 if len(g.indent) > 0 {
611 g.indent = g.indent[1:]
612 }
613}
614
615// GenerateAllFiles generates the output for all the files we're outputting.
616func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700617 // Initialize the plugins
618 for _, p := range plugins {
619 p.Init(g)
620 }
David Symonds31d58a22011-01-20 18:33:21 +1100621 // Generate the output. The generator runs for every file, even the files
622 // that we don't generate output for, so that we can collate the full list
623 // of exported symbols to support public imports.
624 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
625 for _, file := range g.genFiles {
626 genFileMap[file] = true
627 }
628 i := 0
629 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700630 g.Reset()
631 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100632 if _, ok := genFileMap[file]; !ok {
633 continue
634 }
David Symondsb0127532010-11-09 11:10:46 +1100635 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700636 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
637 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100638 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700639 }
640}
641
642// Run all the plugins associated with the file.
643func (g *Generator) runPlugins(file *FileDescriptor) {
644 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700645 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700646 }
647}
648
649
650// FileOf return the FileDescriptor for this FileDescriptorProto.
651func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
652 for _, file := range g.allFiles {
653 if file.FileDescriptorProto == fd {
654 return file
655 }
656 }
657 g.Fail("could not find file in table:", proto.GetString(fd.Name))
658 return nil
659}
660
661// Fill the response protocol buffer with the generated output for all the files we're
662// supposed to generate.
663func (g *Generator) generate(file *FileDescriptor) {
664 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000665 g.usedPackages = make(map[string]bool)
666
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700667 for _, enum := range g.file.enum {
668 g.generateEnum(enum)
669 }
670 for _, desc := range g.file.desc {
671 g.generateMessage(desc)
672 }
673 for _, ext := range g.file.ext {
674 g.generateExtension(ext)
675 }
676 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000677
Rob Pikec9e7d972010-06-10 10:30:22 -0700678 // Run the plugins before the imports so we know which imports are necessary.
679 g.runPlugins(file)
680
David Symondsf90e3382010-05-05 10:53:44 +1000681 // Generate header and imports last, though they appear first in the output.
682 rem := g.Buffer
683 g.Buffer = new(bytes.Buffer)
684 g.generateHeader()
685 g.generateImports()
686 g.Write(rem.Bytes())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700687}
688
689// Generate the header, including package definition and imports
690func (g *Generator) generateHeader() {
691 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
692 g.P("// DO NOT EDIT!")
693 g.P()
694 g.P("package ", g.file.PackageName())
695 g.P()
696}
697
David Symonds31d58a22011-01-20 18:33:21 +1100698func (g *Generator) fileByName(filename string) *FileDescriptor {
699 for _, fd := range g.allFiles {
700 if proto.GetString(fd.Name) == filename {
701 return fd
702 }
703 }
704 return nil
705}
706
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700707// Generate the header, including package definition and imports
708func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700709 // We almost always need a proto import. Rather than computing when we
710 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100711 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700712 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100713 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100714 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700715 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100716 fd := g.fileByName(s)
717 // Do not import our own package.
718 if fd.PackageName() == g.packageName {
719 continue
720 }
721 filename := goFileName(s)
722 if substitution, ok := g.ImportMap[s]; ok {
723 filename = substitution
724 }
725 filename = g.ImportPrefix + filename
726 if strings.HasSuffix(filename, ".go") {
727 filename = filename[0 : len(filename)-3]
728 }
729 if _, ok := g.usedPackages[fd.PackageName()]; ok {
730 g.P("import ", fd.PackageName(), " ", Quote(filename))
731 } else {
732 log.Println("protoc-gen-go: discarding unused import:", filename)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700733 }
734 }
735 g.P()
736 // TODO: may need to worry about uniqueness across plugins
737 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700738 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700739 g.P()
740 }
David Symondscea785b2011-01-07 11:02:30 +1100741 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700742 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100743 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100744 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700745 g.P()
David Symonds31d58a22011-01-20 18:33:21 +1100746
747 // Symbols from public imports.
748 for _, index := range g.file.PublicDependency {
749 fd := g.fileByName(g.file.Dependency[index])
750 g.P("// Types from public import ", *fd.Name)
751 for _, sym := range fd.exported {
752 sym.GenerateAlias(g, fd.PackageName())
753 }
754 }
755 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700756}
757
758// Generate the enum definitions for this EnumDescriptor.
759func (g *Generator) generateEnum(enum *EnumDescriptor) {
760 // The full type name
761 typeName := enum.TypeName()
762 // The full type name, CamelCased.
763 ccTypeName := CamelCaseSlice(typeName)
764 ccPrefix := enum.prefix()
765 g.P("type ", ccTypeName, " int32")
David Symonds31d58a22011-01-20 18:33:21 +1100766 g.file.addExport(enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700767 g.P("const (")
768 g.In()
769 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100770 name := ccPrefix + *e.Name
771 g.P(name, " = ", e.Number)
772 g.file.addExport(constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700773 }
774 g.Out()
775 g.P(")")
776 g.P("var ", ccTypeName, "_name = map[int32] string {")
777 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700778 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700779 for _, e := range enum.Value {
780 duplicate := ""
781 if _, present := generated[*e.Number]; present {
782 duplicate = "// Duplicate value: "
783 }
784 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
785 generated[*e.Number] = true
786 }
787 g.Out()
788 g.P("}")
789 g.P("var ", ccTypeName, "_value = map[string] int32 {")
790 g.In()
791 for _, e := range enum.Value {
792 g.P(Quote(*e.Name), ": ", e.Number, ",")
793 }
794 g.Out()
795 g.P("}")
796 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
797 g.In()
798 g.P("e := ", ccTypeName, "(x)")
799 g.P("return &e")
800 g.Out()
801 g.P("}")
802 g.P()
803}
804
805// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
806// identifies details of the field for the protocol buffer marshaling and unmarshaling
807// code. The fields are:
808// wire encoding
809// protocol tag number
810// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100811// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700812// name= the original declared name
813// enum= the name of the enum type if it is an enum-typed field.
814// def= string representation of the default value, if any.
815// The default value must be in a representation that can be used at run-time
816// to generate the default value. Thus bools become 0 and 1, for instance.
817func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
818 optrepreq := ""
819 switch {
820 case isOptional(field):
821 optrepreq = "opt"
822 case isRequired(field):
823 optrepreq = "req"
824 case isRepeated(field):
825 optrepreq = "rep"
826 }
827 defaultValue := proto.GetString(field.DefaultValue)
828 if defaultValue != "" {
829 switch *field.Type {
830 case descriptor.FieldDescriptorProto_TYPE_BOOL:
831 if defaultValue == "true" {
832 defaultValue = "1"
833 } else {
834 defaultValue = "0"
835 }
836 case descriptor.FieldDescriptorProto_TYPE_STRING,
837 descriptor.FieldDescriptorProto_TYPE_BYTES:
838 // Protect frogs.
839 defaultValue = Quote(defaultValue)
840 // Don't need the quotes
841 defaultValue = defaultValue[1 : len(defaultValue)-1]
842 case descriptor.FieldDescriptorProto_TYPE_ENUM:
843 // For enums we need to provide the integer constant.
844 obj := g.ObjectNamed(proto.GetString(field.TypeName))
845 enum, ok := obj.(*EnumDescriptor)
846 if !ok {
847 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
848 }
849 defaultValue = enum.integerValueAsString(defaultValue)
850 }
851 defaultValue = ",def=" + defaultValue
852 }
853 enum := ""
854 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
855 obj := g.ObjectNamed(proto.GetString(field.TypeName))
856 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
857 }
David Symonds5b7775e2010-12-01 10:09:04 +1100858 packed := ""
859 if field.Options != nil && proto.GetBool(field.Options.Packed) {
860 packed = ",packed"
861 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700862 name := proto.GetString(field.Name)
863 if name == CamelCase(name) {
864 name = ""
865 } else {
866 name = ",name=" + name
867 }
David Symonds5b7775e2010-12-01 10:09:04 +1100868 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s%s)",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700869 wiretype,
870 proto.GetInt32(field.Number),
871 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100872 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700873 name,
874 enum,
875 defaultValue))
876}
877
878func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
879 switch typ {
880 case descriptor.FieldDescriptorProto_TYPE_GROUP:
881 return false
882 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
883 return false
884 case descriptor.FieldDescriptorProto_TYPE_BYTES:
885 return false
886 }
887 return true
888}
889
890// TypeName is the printed name appropriate for an item. If the object is in the current file,
891// TypeName drops the package name and underscores the rest.
892// Otherwise the object is from another package; and the result is the underscored
893// package name followed by the item name.
894// The result always has an initial capital.
895func (g *Generator) TypeName(obj Object) string {
896 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
897}
898
899// TypeNameWithPackage is like TypeName, but always includes the package
900// name even if the object is in our own package.
901func (g *Generator) TypeNameWithPackage(obj Object) string {
902 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
903}
904
905// GoType returns a string representing the type name, and the wire type
906func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
907 // TODO: Options.
908 switch *field.Type {
909 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
910 typ, wire = "float64", "fixed64"
911 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
912 typ, wire = "float32", "fixed32"
913 case descriptor.FieldDescriptorProto_TYPE_INT64:
914 typ, wire = "int64", "varint"
915 case descriptor.FieldDescriptorProto_TYPE_UINT64:
916 typ, wire = "uint64", "varint"
917 case descriptor.FieldDescriptorProto_TYPE_INT32:
918 typ, wire = "int32", "varint"
919 case descriptor.FieldDescriptorProto_TYPE_UINT32:
920 typ, wire = "uint32", "varint"
921 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
922 typ, wire = "uint64", "fixed64"
923 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
924 typ, wire = "uint32", "fixed32"
925 case descriptor.FieldDescriptorProto_TYPE_BOOL:
926 typ, wire = "bool", "varint"
927 case descriptor.FieldDescriptorProto_TYPE_STRING:
928 typ, wire = "string", "bytes"
929 case descriptor.FieldDescriptorProto_TYPE_GROUP:
930 desc := g.ObjectNamed(proto.GetString(field.TypeName))
931 typ, wire = "*"+g.TypeName(desc), "group"
932 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
933 desc := g.ObjectNamed(proto.GetString(field.TypeName))
934 typ, wire = "*"+g.TypeName(desc), "bytes"
935 case descriptor.FieldDescriptorProto_TYPE_BYTES:
936 typ, wire = "[]byte", "bytes"
937 case descriptor.FieldDescriptorProto_TYPE_ENUM:
938 desc := g.ObjectNamed(proto.GetString(field.TypeName))
939 typ, wire = g.TypeName(desc), "varint"
940 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
941 typ, wire = "int32", "fixed32"
942 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
943 typ, wire = "int64", "fixed64"
944 case descriptor.FieldDescriptorProto_TYPE_SINT32:
945 typ, wire = "int32", "zigzag32"
946 case descriptor.FieldDescriptorProto_TYPE_SINT64:
947 typ, wire = "int64", "zigzag64"
948 default:
949 g.Fail("unknown type for", proto.GetString(field.Name))
950 }
951 if isRepeated(field) {
952 typ = "[]" + typ
953 } else if needsStar(*field.Type) {
954 typ = "*" + typ
955 }
956 return
957}
958
David Symondsf90e3382010-05-05 10:53:44 +1000959func (g *Generator) RecordTypeUse(t string) {
960 if obj, ok := g.typeNameToObject[t]; ok {
961 g.usedPackages[obj.PackageName()] = true
962 }
963}
964
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700965// Generate the type and default constant definitions for this Descriptor.
966func (g *Generator) generateMessage(message *Descriptor) {
967 // The full type name
968 typeName := message.TypeName()
969 // The full type name, CamelCased.
970 ccTypeName := CamelCaseSlice(typeName)
971
972 g.P("type ", ccTypeName, " struct {")
973 g.In()
974 for _, field := range message.Field {
975 fieldname := CamelCase(*field.Name)
976 typename, wiretype := g.GoType(message, field)
977 tag := g.goTag(field, wiretype)
978 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000979 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700980 }
981 if len(message.ExtensionRange) > 0 {
982 g.P("XXX_extensions\t\tmap[int32][]byte")
983 }
984 g.P("XXX_unrecognized\t[]byte")
985 g.Out()
986 g.P("}")
987
Rob Pikec6d8e4a2010-07-28 15:34:32 -0700988 // Reset function
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700989 g.P("func (this *", ccTypeName, ") Reset() {")
990 g.In()
991 g.P("*this = ", ccTypeName, "{}")
992 g.Out()
993 g.P("}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700994
995 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +1100996 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700997 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +1100998 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +1100999 // message_set_wire_format only makes sense when extensions are defined.
1000 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001001 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001002 g.P()
1003 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1004 g.In()
1005 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1006 g.Out()
1007 g.P("}")
1008 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1009 g.In()
1010 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1011 g.Out()
1012 g.P("}")
1013 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1014 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1015 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1016 }
1017
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001018 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001019 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001020 g.In()
1021 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001022 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1023 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001024 }
1025 g.Out()
1026 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001027 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001028 g.In()
1029 g.P("return extRange_", ccTypeName)
1030 g.Out()
1031 g.P("}")
1032 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1033 g.In()
1034 g.P("if this.XXX_extensions == nil {")
1035 g.In()
1036 g.P("this.XXX_extensions = make(map[int32][]byte)")
1037 g.Out()
1038 g.P("}")
1039 g.P("return this.XXX_extensions")
1040 g.Out()
1041 g.P("}")
1042 }
1043
David Symonds31d58a22011-01-20 18:33:21 +11001044 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1045
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001046 // Default constants
1047 for _, field := range message.Field {
1048 def := proto.GetString(field.DefaultValue)
1049 if def == "" {
1050 continue
1051 }
1052 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1053 typename, _ := g.GoType(message, field)
1054 if typename[0] == '*' {
1055 typename = typename[1:]
1056 }
1057 kind := "const "
1058 switch {
1059 case typename == "bool":
1060 case typename == "string":
1061 def = Quote(def)
1062 case typename == "[]byte":
1063 def = "[]byte(" + Quote(def) + ")"
1064 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001065 case def == "inf", def == "-inf", def == "nan":
1066 // These names are known to, and defined by, the protocol language.
1067 switch def {
1068 case "inf":
1069 def = "math.Inf(1)"
1070 case "-inf":
1071 def = "math.Inf(-1)"
1072 case "nan":
1073 def = "math.NaN()"
1074 }
1075 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1076 def = "float32(" + def + ")"
1077 }
1078 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001079 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1080 // Must be an enum. Need to construct the prefixed name.
1081 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1082 enum, ok := obj.(*EnumDescriptor)
1083 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001084 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001085 continue
1086 }
Rob Pike87af39e2010-07-19 10:48:02 -07001087 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001088 }
1089 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001090 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001091 }
1092 g.P()
1093
1094 for _, ext := range message.ext {
1095 g.generateExtension(ext)
1096 }
1097}
1098
1099func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
1100 // The full type name
1101 typeName := ext.TypeName()
1102 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
1103 for i, s := range typeName {
1104 typeName[i] = CamelCase(s)
1105 }
1106 ccTypeName := "E_" + strings.Join(typeName, "_")
1107
1108 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1109 field := ext.FieldDescriptorProto
1110 fieldType, wireType := g.GoType(ext.parent, field)
1111 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001112 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001113
Rob Pikec9e7d972010-06-10 10:30:22 -07001114 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001115 g.In()
1116 g.P("ExtendedType: (", extendedType, ")(nil),")
1117 g.P("ExtensionType: (", fieldType, ")(nil),")
1118 g.P("Field: ", field.Number, ",")
1119 g.P("Tag: ", tag, ",")
1120
1121 g.Out()
1122 g.P("}")
1123 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001124
1125 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001126}
1127
1128func (g *Generator) generateInitFunction() {
1129 g.P("func init() {")
1130 g.In()
1131 for _, enum := range g.file.enum {
1132 g.generateEnumRegistration(enum)
1133 }
1134 g.Out()
1135 g.P("}")
1136}
1137
1138func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1139 pkg := g.packageName + "." // We always print the full package name here.
1140 // The full type name
1141 typeName := enum.TypeName()
1142 // The full type name, CamelCased.
1143 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001144 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001145}
1146
1147// And now lots of helper functions.
1148
Rob Pike2c7bafc2010-06-10 16:07:14 -07001149// Is c an ASCII lower-case letter?
1150func isASCIILower(c byte) bool {
1151 return 'a' <= c && c <= 'z'
1152}
1153
1154// Is c an ASCII digit?
1155func isASCIIDigit(c byte) bool {
1156 return '0' <= c && c <= '9'
1157}
1158
1159// CamelCase returns the CamelCased name.
1160// If there is an interior underscore followed by a lower case letter,
1161// drop the underscore and convert the letter to upper case.
1162// There is a remote possibility of this rewrite causing a name collision,
1163// but it's so remote we're prepared to pretend it's nonexistent - since the
1164// C++ generator lowercases names, it's extremely unlikely to have two fields
1165// with different capitalizations.
1166// In short, _my_field_name_2 becomes XMyFieldName2.
1167func CamelCase(s string) string {
1168 if s == "" {
1169 return ""
1170 }
1171 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001172 i := 0
1173 if s[0] == '_' {
1174 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001175 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001176 i++
1177 }
1178 // Invariant: if the next letter is lower case, it must be converted
1179 // to upper case.
1180 // That is, we process a word at a time, where words are marked by _ or
1181 // upper case letter. Digits are treated as words.
1182 for ; i < len(s); i++ {
1183 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001184 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1185 continue // Skip the underscore in s.
1186 }
1187 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001188 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001189 continue
1190 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001191 // Assume we have a letter now - if not, it's a bogus identifier.
1192 // The next word is a sequence of characters that must start upper case.
1193 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001194 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001195 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001196 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001197 // Accept lower case sequence that follows.
1198 for i+1 < len(s) && isASCIILower(s[i+1]) {
1199 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001200 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001201 }
1202 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001203 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001204}
1205
1206// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1207// be joined with "_".
1208func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1209
1210// dottedSlice turns a sliced name into a dotted name.
1211func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1212
1213// Quote returns a Go-source quoted string representation of s.
1214func Quote(s string) string { return fmt.Sprintf("%q", s) }
1215
1216// Given a .proto file name, return the output name for the generated Go program.
1217func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001218 ext := path.Ext(name)
1219 if ext == ".proto" || ext == ".protodevel" {
1220 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001221 }
1222 return name + ".pb.go"
1223}
1224
1225// Is this field optional?
1226func isOptional(field *descriptor.FieldDescriptorProto) bool {
1227 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1228}
1229
1230// Is this field required?
1231func isRequired(field *descriptor.FieldDescriptorProto) bool {
1232 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1233}
1234
1235// Is this field repeated?
1236func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1237 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1238}
1239
1240// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001241// which can be dotted in the input .proto file. It maps dots to underscores.
1242// Because we also get here from package names generated from file names, it also maps
1243// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001244func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001245 switch rune {
1246 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001247 return '_'
1248 }
1249 return rune
1250}
Rob Pikec9e7d972010-06-10 10:30:22 -07001251
1252// BaseName returns the last path element of the name, with the last dotted suffix removed.
1253func BaseName(name string) string {
1254 // First, find the last element
1255 if i := strings.LastIndex(name, "/"); i >= 0 {
1256 name = name[i+1:]
1257 }
1258 // Now drop the suffix
1259 if i := strings.LastIndex(name, "."); i >= 0 {
1260 name = name[0:i]
1261 }
1262 return name
1263}