blob: 9878ef1e9aa35be79a754d361075e0290010877f [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(")")
782 g.P("var ", ccTypeName, "_name = map[int32] string {")
783 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("}")
795 g.P("var ", ccTypeName, "_value = map[string] int32 {")
796 g.In()
797 for _, e := range enum.Value {
798 g.P(Quote(*e.Name), ": ", e.Number, ",")
799 }
800 g.Out()
801 g.P("}")
802 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
803 g.In()
804 g.P("e := ", ccTypeName, "(x)")
805 g.P("return &e")
806 g.Out()
807 g.P("}")
808 g.P()
809}
810
811// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
812// identifies details of the field for the protocol buffer marshaling and unmarshaling
813// code. The fields are:
814// wire encoding
815// protocol tag number
816// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100817// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700818// name= the original declared name
819// enum= the name of the enum type if it is an enum-typed field.
820// def= string representation of the default value, if any.
821// The default value must be in a representation that can be used at run-time
822// to generate the default value. Thus bools become 0 and 1, for instance.
823func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
824 optrepreq := ""
825 switch {
826 case isOptional(field):
827 optrepreq = "opt"
828 case isRequired(field):
829 optrepreq = "req"
830 case isRepeated(field):
831 optrepreq = "rep"
832 }
833 defaultValue := proto.GetString(field.DefaultValue)
834 if defaultValue != "" {
835 switch *field.Type {
836 case descriptor.FieldDescriptorProto_TYPE_BOOL:
837 if defaultValue == "true" {
838 defaultValue = "1"
839 } else {
840 defaultValue = "0"
841 }
842 case descriptor.FieldDescriptorProto_TYPE_STRING,
843 descriptor.FieldDescriptorProto_TYPE_BYTES:
844 // Protect frogs.
845 defaultValue = Quote(defaultValue)
846 // Don't need the quotes
847 defaultValue = defaultValue[1 : len(defaultValue)-1]
848 case descriptor.FieldDescriptorProto_TYPE_ENUM:
849 // For enums we need to provide the integer constant.
850 obj := g.ObjectNamed(proto.GetString(field.TypeName))
851 enum, ok := obj.(*EnumDescriptor)
852 if !ok {
853 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
854 }
855 defaultValue = enum.integerValueAsString(defaultValue)
856 }
857 defaultValue = ",def=" + defaultValue
858 }
859 enum := ""
860 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
861 obj := g.ObjectNamed(proto.GetString(field.TypeName))
862 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
863 }
David Symonds5b7775e2010-12-01 10:09:04 +1100864 packed := ""
865 if field.Options != nil && proto.GetBool(field.Options.Packed) {
866 packed = ",packed"
867 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700868 name := proto.GetString(field.Name)
869 if name == CamelCase(name) {
870 name = ""
871 } else {
872 name = ",name=" + name
873 }
David Symonds5b7775e2010-12-01 10:09:04 +1100874 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s%s)",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700875 wiretype,
876 proto.GetInt32(field.Number),
877 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100878 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700879 name,
880 enum,
881 defaultValue))
882}
883
884func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
885 switch typ {
886 case descriptor.FieldDescriptorProto_TYPE_GROUP:
887 return false
888 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
889 return false
890 case descriptor.FieldDescriptorProto_TYPE_BYTES:
891 return false
892 }
893 return true
894}
895
896// TypeName is the printed name appropriate for an item. If the object is in the current file,
897// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -0700898// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700899// package name followed by the item name.
900// The result always has an initial capital.
901func (g *Generator) TypeName(obj Object) string {
902 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
903}
904
905// TypeNameWithPackage is like TypeName, but always includes the package
906// name even if the object is in our own package.
907func (g *Generator) TypeNameWithPackage(obj Object) string {
908 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
909}
910
911// GoType returns a string representing the type name, and the wire type
912func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
913 // TODO: Options.
914 switch *field.Type {
915 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
916 typ, wire = "float64", "fixed64"
917 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
918 typ, wire = "float32", "fixed32"
919 case descriptor.FieldDescriptorProto_TYPE_INT64:
920 typ, wire = "int64", "varint"
921 case descriptor.FieldDescriptorProto_TYPE_UINT64:
922 typ, wire = "uint64", "varint"
923 case descriptor.FieldDescriptorProto_TYPE_INT32:
924 typ, wire = "int32", "varint"
925 case descriptor.FieldDescriptorProto_TYPE_UINT32:
926 typ, wire = "uint32", "varint"
927 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
928 typ, wire = "uint64", "fixed64"
929 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
930 typ, wire = "uint32", "fixed32"
931 case descriptor.FieldDescriptorProto_TYPE_BOOL:
932 typ, wire = "bool", "varint"
933 case descriptor.FieldDescriptorProto_TYPE_STRING:
934 typ, wire = "string", "bytes"
935 case descriptor.FieldDescriptorProto_TYPE_GROUP:
936 desc := g.ObjectNamed(proto.GetString(field.TypeName))
937 typ, wire = "*"+g.TypeName(desc), "group"
938 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
939 desc := g.ObjectNamed(proto.GetString(field.TypeName))
940 typ, wire = "*"+g.TypeName(desc), "bytes"
941 case descriptor.FieldDescriptorProto_TYPE_BYTES:
942 typ, wire = "[]byte", "bytes"
943 case descriptor.FieldDescriptorProto_TYPE_ENUM:
944 desc := g.ObjectNamed(proto.GetString(field.TypeName))
945 typ, wire = g.TypeName(desc), "varint"
946 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
947 typ, wire = "int32", "fixed32"
948 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
949 typ, wire = "int64", "fixed64"
950 case descriptor.FieldDescriptorProto_TYPE_SINT32:
951 typ, wire = "int32", "zigzag32"
952 case descriptor.FieldDescriptorProto_TYPE_SINT64:
953 typ, wire = "int64", "zigzag64"
954 default:
955 g.Fail("unknown type for", proto.GetString(field.Name))
956 }
957 if isRepeated(field) {
958 typ = "[]" + typ
959 } else if needsStar(*field.Type) {
960 typ = "*" + typ
961 }
962 return
963}
964
David Symondsf90e3382010-05-05 10:53:44 +1000965func (g *Generator) RecordTypeUse(t string) {
966 if obj, ok := g.typeNameToObject[t]; ok {
967 g.usedPackages[obj.PackageName()] = true
968 }
969}
970
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700971// Generate the type and default constant definitions for this Descriptor.
972func (g *Generator) generateMessage(message *Descriptor) {
973 // The full type name
974 typeName := message.TypeName()
975 // The full type name, CamelCased.
976 ccTypeName := CamelCaseSlice(typeName)
977
978 g.P("type ", ccTypeName, " struct {")
979 g.In()
980 for _, field := range message.Field {
981 fieldname := CamelCase(*field.Name)
982 typename, wiretype := g.GoType(message, field)
983 tag := g.goTag(field, wiretype)
984 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000985 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700986 }
987 if len(message.ExtensionRange) > 0 {
988 g.P("XXX_extensions\t\tmap[int32][]byte")
989 }
990 g.P("XXX_unrecognized\t[]byte")
991 g.Out()
992 g.P("}")
993
Rob Pikec6d8e4a2010-07-28 15:34:32 -0700994 // Reset function
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700995 g.P("func (this *", ccTypeName, ") Reset() {")
996 g.In()
997 g.P("*this = ", ccTypeName, "{}")
998 g.Out()
999 g.P("}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001000
1001 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001002 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001003 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001004 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001005 // message_set_wire_format only makes sense when extensions are defined.
1006 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001007 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001008 g.P()
1009 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1010 g.In()
1011 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1012 g.Out()
1013 g.P("}")
1014 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1015 g.In()
1016 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1017 g.Out()
1018 g.P("}")
1019 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1020 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1021 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1022 }
1023
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001024 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001025 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001026 g.In()
1027 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001028 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
1029 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001030 }
1031 g.Out()
1032 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001033 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001034 g.In()
1035 g.P("return extRange_", ccTypeName)
1036 g.Out()
1037 g.P("}")
1038 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
1039 g.In()
1040 g.P("if this.XXX_extensions == nil {")
1041 g.In()
1042 g.P("this.XXX_extensions = make(map[int32][]byte)")
1043 g.Out()
1044 g.P("}")
1045 g.P("return this.XXX_extensions")
1046 g.Out()
1047 g.P("}")
1048 }
1049
David Symonds31d58a22011-01-20 18:33:21 +11001050 g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
1051
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001052 // Default constants
1053 for _, field := range message.Field {
1054 def := proto.GetString(field.DefaultValue)
1055 if def == "" {
1056 continue
1057 }
1058 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1059 typename, _ := g.GoType(message, field)
1060 if typename[0] == '*' {
1061 typename = typename[1:]
1062 }
1063 kind := "const "
1064 switch {
1065 case typename == "bool":
1066 case typename == "string":
1067 def = Quote(def)
1068 case typename == "[]byte":
1069 def = "[]byte(" + Quote(def) + ")"
1070 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001071 case def == "inf", def == "-inf", def == "nan":
1072 // These names are known to, and defined by, the protocol language.
1073 switch def {
1074 case "inf":
1075 def = "math.Inf(1)"
1076 case "-inf":
1077 def = "math.Inf(-1)"
1078 case "nan":
1079 def = "math.NaN()"
1080 }
1081 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1082 def = "float32(" + def + ")"
1083 }
1084 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001085 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1086 // Must be an enum. Need to construct the prefixed name.
1087 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1088 enum, ok := obj.(*EnumDescriptor)
1089 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001090 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001091 continue
1092 }
Rob Pike87af39e2010-07-19 10:48:02 -07001093 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001094 }
1095 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds31d58a22011-01-20 18:33:21 +11001096 g.file.addExport(constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001097 }
1098 g.P()
1099
1100 for _, ext := range message.ext {
1101 g.generateExtension(ext)
1102 }
1103}
1104
1105func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
1106 // The full type name
1107 typeName := ext.TypeName()
1108 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
1109 for i, s := range typeName {
1110 typeName[i] = CamelCase(s)
1111 }
1112 ccTypeName := "E_" + strings.Join(typeName, "_")
1113
1114 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1115 field := ext.FieldDescriptorProto
1116 fieldType, wireType := g.GoType(ext.parent, field)
1117 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001118 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001119
Rob Pikec9e7d972010-06-10 10:30:22 -07001120 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001121 g.In()
1122 g.P("ExtendedType: (", extendedType, ")(nil),")
1123 g.P("ExtensionType: (", fieldType, ")(nil),")
1124 g.P("Field: ", field.Number, ",")
1125 g.P("Tag: ", tag, ",")
1126
1127 g.Out()
1128 g.P("}")
1129 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001130
1131 g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001132}
1133
1134func (g *Generator) generateInitFunction() {
1135 g.P("func init() {")
1136 g.In()
1137 for _, enum := range g.file.enum {
1138 g.generateEnumRegistration(enum)
1139 }
1140 g.Out()
1141 g.P("}")
1142}
1143
1144func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1145 pkg := g.packageName + "." // We always print the full package name here.
1146 // The full type name
1147 typeName := enum.TypeName()
1148 // The full type name, CamelCased.
1149 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001150 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001151}
1152
1153// And now lots of helper functions.
1154
Rob Pike2c7bafc2010-06-10 16:07:14 -07001155// Is c an ASCII lower-case letter?
1156func isASCIILower(c byte) bool {
1157 return 'a' <= c && c <= 'z'
1158}
1159
1160// Is c an ASCII digit?
1161func isASCIIDigit(c byte) bool {
1162 return '0' <= c && c <= '9'
1163}
1164
1165// CamelCase returns the CamelCased name.
1166// If there is an interior underscore followed by a lower case letter,
1167// drop the underscore and convert the letter to upper case.
1168// There is a remote possibility of this rewrite causing a name collision,
1169// but it's so remote we're prepared to pretend it's nonexistent - since the
1170// C++ generator lowercases names, it's extremely unlikely to have two fields
1171// with different capitalizations.
1172// In short, _my_field_name_2 becomes XMyFieldName2.
1173func CamelCase(s string) string {
1174 if s == "" {
1175 return ""
1176 }
1177 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001178 i := 0
1179 if s[0] == '_' {
1180 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001181 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001182 i++
1183 }
1184 // Invariant: if the next letter is lower case, it must be converted
1185 // to upper case.
1186 // That is, we process a word at a time, where words are marked by _ or
1187 // upper case letter. Digits are treated as words.
1188 for ; i < len(s); i++ {
1189 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001190 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1191 continue // Skip the underscore in s.
1192 }
1193 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001194 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001195 continue
1196 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001197 // Assume we have a letter now - if not, it's a bogus identifier.
1198 // The next word is a sequence of characters that must start upper case.
1199 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001200 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001201 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001202 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001203 // Accept lower case sequence that follows.
1204 for i+1 < len(s) && isASCIILower(s[i+1]) {
1205 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001206 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001207 }
1208 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001209 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001210}
1211
1212// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1213// be joined with "_".
1214func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1215
1216// dottedSlice turns a sliced name into a dotted name.
1217func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1218
1219// Quote returns a Go-source quoted string representation of s.
1220func Quote(s string) string { return fmt.Sprintf("%q", s) }
1221
1222// Given a .proto file name, return the output name for the generated Go program.
1223func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001224 ext := path.Ext(name)
1225 if ext == ".proto" || ext == ".protodevel" {
1226 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001227 }
1228 return name + ".pb.go"
1229}
1230
1231// Is this field optional?
1232func isOptional(field *descriptor.FieldDescriptorProto) bool {
1233 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1234}
1235
1236// Is this field required?
1237func isRequired(field *descriptor.FieldDescriptorProto) bool {
1238 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1239}
1240
1241// Is this field repeated?
1242func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1243 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1244}
1245
1246// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001247// which can be dotted in the input .proto file. It maps dots to underscores.
1248// Because we also get here from package names generated from file names, it also maps
1249// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001250func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001251 switch rune {
1252 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001253 return '_'
1254 }
1255 return rune
1256}
Rob Pikec9e7d972010-06-10 10:30:22 -07001257
1258// BaseName returns the last path element of the name, with the last dotted suffix removed.
1259func BaseName(name string) string {
1260 // First, find the last element
1261 if i := strings.LastIndex(name, "/"); i >= 0 {
1262 name = name[i+1:]
1263 }
1264 // Now drop the suffix
1265 if i := strings.LastIndex(name, "."); i >= 0 {
1266 name = name[0:i]
1267 }
1268 return name
1269}