blob: aee2449b23eefe11bee75cc34df8451ccd43f8a5 [file] [log] [blame]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32/*
33 The code generator for the plugin for the Google protocol buffer compiler.
34 It generates Go code from the protocol buffer description files read by the
35 main routine.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070036*/
37package generator
38
39import (
40 "bytes"
41 "fmt"
David Symondsb1d55a02011-04-08 09:55:06 +100042 "go/parser"
43 "go/printer"
44 "go/token"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070045 "log"
46 "os"
Rob Pike87af39e2010-07-19 10:48:02 -070047 "path"
David Symonds79eae332010-10-16 11:33:20 +110048 "strconv"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070049 "strings"
David Symonds162d0032012-06-28 09:44:46 -070050 "unicode"
51 "unicode/utf8"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070052
Rob Pike3f6f2d82011-12-18 13:55:35 -080053 "code.google.com/p/goprotobuf/proto"
Rob Pikeb7907bf2012-02-13 14:25:20 +110054 descriptor "code.google.com/p/goprotobuf/protoc-gen-go/descriptor"
55 plugin "code.google.com/p/goprotobuf/protoc-gen-go/plugin"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070056)
57
58// A Plugin provides functionality to add to the output during Go code generation,
59// such as to produce RPC stubs.
60type Plugin interface {
61 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070062 Name() string
63 // Init is called once after data structures are built but before
64 // code generation begins.
65 Init(g *Generator)
66 // Generate produces the code generated by the plugin for this file,
67 // except for the imports, by calling the generator's methods P, In, and Out.
68 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070069 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070070 // It is called after Generate.
71 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070072}
73
74var plugins []Plugin
75
76// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
77// It is typically called during initialization.
78func RegisterPlugin(p Plugin) {
David Symondscc7142e2010-11-06 14:37:15 +110079 plugins = append(plugins, p)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070080}
81
82// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
83// a pointer to the FileDescriptorProto that represents it. These types achieve that
84// wrapping by placing each Proto inside a struct with the pointer to its File. The
85// structs have the same names as their contents, with "Proto" removed.
86// FileDescriptor is used to store the things that it points to.
87
88// The file and package name method are common to messages and enums.
89type common struct {
David Symonds4decd802011-08-04 11:27:07 +100090 file *descriptor.FileDescriptorProto // File this object comes from.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070091}
92
93// PackageName is name in the package clause in the generated file.
David Symonds4decd802011-08-04 11:27:07 +100094func (c *common) PackageName() string { return uniquePackageOf(c.file) }
95
96func (c *common) File() *descriptor.FileDescriptorProto { return c.file }
Rob Pikeaf82b4e2010-04-30 15:19:25 -070097
98// Descriptor represents a protocol buffer message.
99type Descriptor struct {
100 common
101 *descriptor.DescriptorProto
102 parent *Descriptor // The containing message, if any.
103 nested []*Descriptor // Inner messages, if any.
104 ext []*ExtensionDescriptor // Extensions, if any.
105 typename []string // Cached typename vector.
106}
107
108// TypeName returns the elements of the dotted type name.
109// The package name is not part of this name.
110func (d *Descriptor) TypeName() []string {
111 if d.typename != nil {
112 return d.typename
113 }
114 n := 0
115 for parent := d; parent != nil; parent = parent.parent {
116 n++
117 }
118 s := make([]string, n, n)
119 for parent := d; parent != nil; parent = parent.parent {
120 n--
David Symonds8bb32ca2012-06-28 10:22:09 -0700121 s[n] = parent.GetName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700122 }
123 d.typename = s
124 return s
125}
126
127// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
128// Otherwise it will be the descriptor of the message in which it is defined.
129type EnumDescriptor struct {
130 common
131 *descriptor.EnumDescriptorProto
132 parent *Descriptor // The containing message, if any.
133 typename []string // Cached typename vector.
134}
135
136// TypeName returns the elements of the dotted type name.
137// The package name is not part of this name.
138func (e *EnumDescriptor) TypeName() (s []string) {
139 if e.typename != nil {
140 return e.typename
141 }
David Symonds8bb32ca2012-06-28 10:22:09 -0700142 name := e.GetName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700143 if e.parent == nil {
144 s = make([]string, 1)
145 } else {
146 pname := e.parent.TypeName()
147 s = make([]string, len(pname)+1)
148 copy(s, pname)
149 }
150 s[len(s)-1] = name
151 e.typename = s
152 return s
153}
154
155// Everything but the last element of the full type name, CamelCased.
156// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
157func (e *EnumDescriptor) prefix() string {
158 typeName := e.TypeName()
159 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
160 if e.parent == nil {
161 // If the enum is not part of a message, the prefix is just the type name.
162 ccPrefix = CamelCase(*e.Name) + "_"
163 }
164 return ccPrefix
165}
166
167// The integer value of the named constant in this enumerated type.
168func (e *EnumDescriptor) integerValueAsString(name string) string {
169 for _, c := range e.Value {
David Symonds8bb32ca2012-06-28 10:22:09 -0700170 if c.GetName() == name {
171 return fmt.Sprint(c.GetNumber())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700172 }
173 }
David Symonds9d0000e2011-02-03 10:48:14 +1100174 log.Fatal("cannot find value for enum constant")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700175 return ""
176}
177
David Symonds4decd802011-08-04 11:27:07 +1000178// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700179// Otherwise it will be the descriptor of the message in which it is defined.
180type ExtensionDescriptor struct {
181 common
182 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700183 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700184}
185
186// TypeName returns the elements of the dotted type name.
187// The package name is not part of this name.
188func (e *ExtensionDescriptor) TypeName() (s []string) {
David Symonds8bb32ca2012-06-28 10:22:09 -0700189 name := e.GetName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700190 if e.parent == nil {
191 // top-level extension
192 s = make([]string, 1)
193 } else {
194 pname := e.parent.TypeName()
195 s = make([]string, len(pname)+1)
196 copy(s, pname)
197 }
198 s[len(s)-1] = name
199 return s
200}
201
David Symondse37856c2011-06-22 12:52:53 +1000202// DescName returns the variable name used for the generated descriptor.
203func (e *ExtensionDescriptor) DescName() string {
204 // The full type name.
205 typeName := e.TypeName()
206 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
207 for i, s := range typeName {
208 typeName[i] = CamelCase(s)
209 }
210 return "E_" + strings.Join(typeName, "_")
211}
212
David Symonds4decd802011-08-04 11:27:07 +1000213// ImportedDescriptor describes a type that has been publicly imported from another file.
214type ImportedDescriptor struct {
215 common
216 o Object
217}
218
219func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() }
220
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700221// FileDescriptor describes an protocol buffer descriptor file (.proto).
222// It includes slices of all the messages and enums defined within it.
223// Those slices are constructed by WrapTypes.
224type FileDescriptor struct {
225 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700226 desc []*Descriptor // All the messages defined in this file.
227 enum []*EnumDescriptor // All the enums defined in this file.
228 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds4decd802011-08-04 11:27:07 +1000229 imp []*ImportedDescriptor // All types defined in files publicly imported by this file.
David Symonds31d58a22011-01-20 18:33:21 +1100230
David Symondsb2a00c82011-08-04 16:52:58 +1000231 // The full list of symbols that are exported,
232 // as a map from the exported object to its symbols.
David Symonds31d58a22011-01-20 18:33:21 +1100233 // This is used for supporting public imports.
David Symondsb2a00c82011-08-04 16:52:58 +1000234 exported map[Object][]Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700235}
236
237// PackageName is the package name we'll use in the generated code to refer to this file.
238func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
239
David Symonds162d0032012-06-28 09:44:46 -0700240// goPackageName returns the Go package name to use in the
241// generated Go file. The result explicit reports whether the name
242// came from an option go_package statement. If explicit is false,
243// the name was derived from the protocol buffer's package statement
244// or the input file name.
245func (d *FileDescriptor) goPackageName() (name string, explicit bool) {
246
Rob Pikec9e7d972010-06-10 10:30:22 -0700247 // Does the file have a package clause?
David Symonds8bb32ca2012-06-28 10:22:09 -0700248 if pkg := d.GetPackage(); pkg != "" {
David Symonds162d0032012-06-28 09:44:46 -0700249 return pkg, false
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700250 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700251 // Use the file base name.
David Symonds8bb32ca2012-06-28 10:22:09 -0700252 return BaseName(d.GetName()), false
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700253}
254
David Symondsb2a00c82011-08-04 16:52:58 +1000255func (d *FileDescriptor) addExport(obj Object, symbol Symbol) {
256 d.exported[obj] = append(d.exported[obj], symbol)
David Symonds31d58a22011-01-20 18:33:21 +1100257}
258
259// Symbol is an interface representing an exported Go symbol.
260type Symbol interface {
261 // GenerateAlias should generate an appropriate alias
262 // for the symbol from the named package.
263 GenerateAlias(g *Generator, pkg string)
264}
265
266type messageSymbol struct {
267 sym string
268 hasExtensions, isMessageSet bool
269}
270
271func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
272 remoteSym := pkg + "." + ms.sym
273
274 g.P("type ", ms.sym, " ", remoteSym)
275 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
David Symondse37856c2011-06-22 12:52:53 +1000276 g.P("func (this *", ms.sym, ") String() string { return (*", remoteSym, ")(this).String() }")
David Symonds9f60f432012-06-14 09:45:25 +1000277 g.P("func (*", ms.sym, ") ProtoMessage() {}")
David Symonds31d58a22011-01-20 18:33:21 +1100278 if ms.hasExtensions {
David Symonds11db5982012-08-15 10:54:05 +1000279 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.Pkg["proto"], ".ExtensionRange ",
David Symonds31d58a22011-01-20 18:33:21 +1100280 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
David Symonds11db5982012-08-15 10:54:05 +1000281 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32]", g.Pkg["proto"], ".Extension ",
David Symonds31d58a22011-01-20 18:33:21 +1100282 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
283 if ms.isMessageSet {
Rob Pikea17fdd92011-11-02 12:43:05 -0700284 g.P("func (this *", ms.sym, ") Marshal() ([]byte, error) ",
David Symonds31d58a22011-01-20 18:33:21 +1100285 "{ return (*", remoteSym, ")(this).Marshal() }")
Rob Pikea17fdd92011-11-02 12:43:05 -0700286 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) error ",
David Symonds31d58a22011-01-20 18:33:21 +1100287 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
288 }
289 }
290}
291
292type enumSymbol string
293
294func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
295 s := string(es)
296 g.P("type ", s, " ", pkg, ".", s)
297 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
298 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
David Symondsdd9ca042011-08-29 16:07:16 +1000299 g.P("func New", s, "(x ", s, ") *", s, " { e := ", s, "(x); return &e }")
David Symonds31d58a22011-01-20 18:33:21 +1100300}
301
302type constOrVarSymbol struct {
303 sym string
304 typ string // either "const" or "var"
305}
306
307func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
308 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
309}
310
David Symonds4decd802011-08-04 11:27:07 +1000311// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700312type Object interface {
313 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
314 TypeName() []string
David Symonds4decd802011-08-04 11:27:07 +1000315 File() *descriptor.FileDescriptorProto
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700316}
317
318// Each package name we generate must be unique. The package we're generating
David Symonds4decd802011-08-04 11:27:07 +1000319// gets its own name but every other package must have a unique name that does
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700320// not conflict in the code we generate. These names are chosen globally (although
321// they don't have to be, it simplifies things to do them globally).
322func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
323 s, ok := uniquePackageName[fd]
324 if !ok {
David Symonds8bb32ca2012-06-28 10:22:09 -0700325 log.Fatal("internal error: no package name defined for", fd.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700326 }
327 return s
328}
329
330// Generator is the type whose methods generate the output, stored in the associated response structure.
331type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000332 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700333
334 Request *plugin.CodeGeneratorRequest // The input.
335 Response *plugin.CodeGeneratorResponse // The output.
336
David Symonds162d0032012-06-28 09:44:46 -0700337 Param map[string]string // Command-line parameters.
338 PackageImportPath string // Go import path of the package we're generating code for
339 ImportPrefix string // String to prefix to imported package file names.
340 ImportMap map[string]string // Mapping from import name to generated name
Rob Pikec9e7d972010-06-10 10:30:22 -0700341
David Symonds11db5982012-08-15 10:54:05 +1000342 Pkg map[string]string // The names under which we import support packages
Rob Pikec9e7d972010-06-10 10:30:22 -0700343
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700344 packageName string // What we're calling ourselves.
345 allFiles []*FileDescriptor // All files in the tree
346 genFiles []*FileDescriptor // Those files we will generate output for.
347 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000348 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700349 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
350 indent string
351}
352
353// New creates a new generator and allocates the request and response protobufs.
354func New() *Generator {
355 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000356 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100357 g.Request = new(plugin.CodeGeneratorRequest)
358 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700359 return g
360}
361
Rob Pikea17fdd92011-11-02 12:43:05 -0700362// Error reports a problem, including an error, and exits the program.
363func (g *Generator) Error(err error, msgs ...string) {
364 s := strings.Join(msgs, " ") + ":" + err.Error()
Rob Pike5194c512010-10-14 13:02:16 -0700365 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700366 g.Response.Error = proto.String(s)
367 os.Exit(1)
368}
369
370// Fail reports a problem and exits the program.
371func (g *Generator) Fail(msgs ...string) {
372 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700373 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700374 g.Response.Error = proto.String(s)
375 os.Exit(1)
376}
377
Rob Pikec9e7d972010-06-10 10:30:22 -0700378// CommandLineParameters breaks the comma-separated list of key=value pairs
379// in the parameter (a member of the request protobuf) into a key/value map.
380// It then sets file name mappings defined by those entries.
381func (g *Generator) CommandLineParameters(parameter string) {
382 g.Param = make(map[string]string)
David Symonds8935abf2011-07-04 15:53:16 +1000383 for _, p := range strings.Split(parameter, ",") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700384 if i := strings.Index(p, "="); i < 0 {
385 g.Param[p] = ""
386 } else {
387 g.Param[p[0:i]] = p[i+1:]
388 }
389 }
390
391 g.ImportMap = make(map[string]string)
392 for k, v := range g.Param {
David Symonds162d0032012-06-28 09:44:46 -0700393 switch k {
394 case "import_prefix":
Rob Pikec9e7d972010-06-10 10:30:22 -0700395 g.ImportPrefix = v
David Symonds162d0032012-06-28 09:44:46 -0700396 case "import_path", "go_import_path": // TODO: remove go_import_path
397 g.PackageImportPath = v
398 default:
399 if len(k) > 0 && k[0] == 'M' {
400 g.ImportMap[k[1:]] = v
401 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700402 }
403 }
404}
405
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700406// DefaultPackageName returns the package name printed for the object.
407// If its file is in a different package, it returns the package name we're using for this file, plus ".".
408// Otherwise it returns the empty string.
409func (g *Generator) DefaultPackageName(obj Object) string {
410 pkg := obj.PackageName()
411 if pkg == g.packageName {
412 return ""
413 }
414 return pkg + "."
415}
416
417// For each input file, the unique package name to use, underscored.
418var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pike3f6f2d82011-12-18 13:55:35 -0800419
Rob Pikec9e7d972010-06-10 10:30:22 -0700420// Package names already registered. Key is the name from the .proto file;
421// value is the name that appears in the generated code.
422var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700423
Rob Pikec9e7d972010-06-10 10:30:22 -0700424// Create and remember a guaranteed unique package name for this file descriptor.
425// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
426// has no file descriptor.
427func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds41389812011-07-19 14:57:40 +1000428 // Convert dots to underscores before finding a unique alias.
David Symonds162d0032012-06-28 09:44:46 -0700429 pkg = strings.Map(BadToUnderscore, pkg)
David Symonds41389812011-07-19 14:57:40 +1000430
David Symonds79eae332010-10-16 11:33:20 +1100431 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700432 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100433 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700434 }
435 // Install it.
436 pkgNamesInUse[pkg] = true
Rob Pikec9e7d972010-06-10 10:30:22 -0700437 if f != nil {
438 uniquePackageName[f.FileDescriptorProto] = pkg
439 }
440 return pkg
441}
442
David Symonds162d0032012-06-28 09:44:46 -0700443var isGoKeyword = map[string]bool{
444 "break": true,
445 "case": true,
446 "chan": true,
447 "const": true,
448 "continue": true,
449 "default": true,
450 "else": true,
451 "defer": true,
452 "fallthrough": true,
453 "for": true,
454 "func": true,
455 "go": true,
456 "goto": true,
457 "if": true,
458 "import": true,
459 "interface": true,
460 "map": true,
461 "package": true,
462 "range": true,
463 "return": true,
464 "select": true,
465 "struct": true,
466 "switch": true,
467 "type": true,
468 "var": true,
469}
470
471// defaultGoPackage returns the package name to use,
472// derived from the import path of the package we're building code for.
473func (g *Generator) defaultGoPackage() string {
474 p := g.PackageImportPath
475 if i := strings.LastIndex(p, "/"); i >= 0 {
476 p = p[i+1:]
477 }
478 if p == "" {
479 return ""
480 }
481
482 p = strings.Map(BadToUnderscore, p)
483 // Identifier must not be keyword: insert _.
484 if isGoKeyword[p] {
485 p = "_" + p
486 }
487 // Identifier must not begin with digit: insert _.
488 if r, _ := utf8.DecodeRuneInString(p); unicode.IsDigit(r) {
489 p = "_" + p
490 }
491 return p
492}
493
Rob Pikec9e7d972010-06-10 10:30:22 -0700494// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700495// The package name must agree across all files being generated.
496// It also defines unique package names for all imported files.
497func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700498 // Register the name for this package. It will be the first name
499 // registered so is guaranteed to be unmodified.
David Symonds162d0032012-06-28 09:44:46 -0700500 pkg, explicit := g.genFiles[0].goPackageName()
501
502 // Check all files for an explicit go_package option.
503 for _, f := range g.genFiles {
504 thisPkg, thisExplicit := f.goPackageName()
505 if thisExplicit {
506 if !explicit {
507 // Let this file's go_package option serve for all input files.
508 pkg, explicit = thisPkg, true
509 } else if thisPkg != pkg {
510 g.Fail("inconsistent package names:", thisPkg, pkg)
511 }
512 }
513 }
514
515 // If we don't have an explicit go_package option but we have an
516 // import path, use that.
517 if !explicit {
518 p := g.defaultGoPackage()
519 if p != "" {
520 pkg, explicit = p, true
521 }
522 }
523
524 // If there was no go_package and no import path to use,
525 // double-check that all the inputs have the same implicit
526 // Go package name.
527 if !explicit {
528 for _, f := range g.genFiles {
529 thisPkg, _ := f.goPackageName()
530 if thisPkg != pkg {
531 g.Fail("inconsistent package names:", thisPkg, pkg)
532 }
533 }
534 }
535
Rob Pikec9e7d972010-06-10 10:30:22 -0700536 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
David Symonds162d0032012-06-28 09:44:46 -0700537
David Symonds11db5982012-08-15 10:54:05 +1000538 // Register the support package names. They might collide with the
Rob Pikec9e7d972010-06-10 10:30:22 -0700539 // name of a package we import.
David Symonds11db5982012-08-15 10:54:05 +1000540 g.Pkg = map[string]string{
541 "json": RegisterUniquePackageName("json", nil),
542 "math": RegisterUniquePackageName("math", nil),
543 "proto": RegisterUniquePackageName("proto", nil),
544 }
David Symonds162d0032012-06-28 09:44:46 -0700545
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700546AllFiles:
547 for _, f := range g.allFiles {
548 for _, genf := range g.genFiles {
549 if f == genf {
550 // In this package already.
551 uniquePackageName[f.FileDescriptorProto] = g.packageName
552 continue AllFiles
553 }
554 }
David Symonds7d5c8242011-03-14 12:03:50 -0700555 // The file is a dependency, so we want to ignore its go_package option
556 // because that is only relevant for its specific generated output.
David Symonds8bb32ca2012-06-28 10:22:09 -0700557 pkg := f.GetPackage()
David Symonds7d5c8242011-03-14 12:03:50 -0700558 if pkg == "" {
559 pkg = BaseName(*f.Name)
560 }
561 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700562 }
563}
564
565// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
566// and FileDescriptorProtos into file-referenced objects within the Generator.
567// It also creates the list of files to generate and so should be called before GenerateAllFiles.
568func (g *Generator) WrapTypes() {
569 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
570 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700571 // We must wrap the descriptors before we wrap the enums
572 descs := wrapDescriptors(f)
573 g.buildNestedDescriptors(descs)
574 enums := wrapEnumDescriptors(f, descs)
575 exts := wrapExtensions(f)
David Symonds4decd802011-08-04 11:27:07 +1000576 imps := wrapImported(f, g)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700577 g.allFiles[i] = &FileDescriptor{
578 FileDescriptorProto: f,
579 desc: descs,
580 enum: enums,
581 ext: exts,
David Symonds4decd802011-08-04 11:27:07 +1000582 imp: imps,
David Symondsb2a00c82011-08-04 16:52:58 +1000583 exported: make(map[Object][]Symbol),
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700584 }
585 }
586
587 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
588FindFiles:
589 for i, fileName := range g.Request.FileToGenerate {
590 // Search the list. This algorithm is n^2 but n is tiny.
591 for _, file := range g.allFiles {
David Symonds8bb32ca2012-06-28 10:22:09 -0700592 if fileName == file.GetName() {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700593 g.genFiles[i] = file
594 continue FindFiles
595 }
596 }
597 g.Fail("could not find file named", fileName)
598 }
599 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
600}
601
602// Scan the descriptors in this file. For each one, build the slice of nested descriptors
603func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
604 for _, desc := range descs {
605 if len(desc.NestedType) != 0 {
606 desc.nested = make([]*Descriptor, len(desc.NestedType))
607 n := 0
608 for _, nest := range descs {
609 if nest.parent == desc {
610 desc.nested[n] = nest
611 n++
612 }
613 }
614 if n != len(desc.NestedType) {
David Symonds8bb32ca2012-06-28 10:22:09 -0700615 g.Fail("internal error: nesting failure for", desc.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700616 }
617 }
618 }
619}
620
621// Construct the Descriptor and add it to the slice
622func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
David Symonds4decd802011-08-04 11:27:07 +1000623 d := &Descriptor{common{file}, desc, parent, nil, nil, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700624
625 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
626 for i, field := range desc.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000627 d.ext[i] = &ExtensionDescriptor{common{file}, field, d}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700628 }
629
David Symondscc7142e2010-11-06 14:37:15 +1100630 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700631}
632
633// Return a slice of all the Descriptors defined within this file
634func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
635 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
636 for _, desc := range file.MessageType {
637 sl = wrapThisDescriptor(sl, desc, nil, file)
638 }
639 return sl
640}
641
642// Wrap this Descriptor, recursively
643func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
644 sl = addDescriptor(sl, desc, parent, file)
645 me := sl[len(sl)-1]
646 for _, nested := range desc.NestedType {
647 sl = wrapThisDescriptor(sl, nested, me, file)
648 }
649 return sl
650}
651
652// Construct the EnumDescriptor and add it to the slice
653func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symonds4decd802011-08-04 11:27:07 +1000654 return append(sl, &EnumDescriptor{common{file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700655}
656
657// Return a slice of all the EnumDescriptors defined within this file
658func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
659 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000660 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700661 for _, enum := range file.EnumType {
662 sl = addEnumDescriptor(sl, enum, nil, file)
663 }
David Symonds5256cf62010-06-27 10:33:42 +1000664 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700665 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000666 for _, enum := range nested.EnumType {
667 sl = addEnumDescriptor(sl, enum, nested, file)
668 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700669 }
670 return sl
671}
672
673// Return a slice of all the top-level ExtensionDescriptors defined within this file.
674func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
675 sl := make([]*ExtensionDescriptor, len(file.Extension))
676 for i, field := range file.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000677 sl[i] = &ExtensionDescriptor{common{file}, field, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700678 }
679 return sl
680}
681
David Symonds4decd802011-08-04 11:27:07 +1000682// Return a slice of all the types that are publicly imported into this file.
683func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) {
684 for _, index := range file.PublicDependency {
685 df := g.fileByName(file.Dependency[index])
686 for _, d := range df.desc {
687 sl = append(sl, &ImportedDescriptor{common{file}, d})
688 }
689 for _, e := range df.enum {
690 sl = append(sl, &ImportedDescriptor{common{file}, e})
691 }
692 for _, ext := range df.ext {
693 sl = append(sl, &ImportedDescriptor{common{file}, ext})
694 }
695 }
696 return
697}
698
Rob Pikec9e7d972010-06-10 10:30:22 -0700699// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700700// The key names for the map come from the input data, which puts a period at the beginning.
701// It should be called after SetPackageNames and before GenerateAllFiles.
702func (g *Generator) BuildTypeNameMap() {
703 g.typeNameToObject = make(map[string]Object)
704 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700705 // The names in this loop are defined by the proto world, not us, so the
706 // package name may be empty. If so, the dotted package name of X will
707 // be ".X"; otherwise it will be ".pkg.X".
David Symonds8bb32ca2012-06-28 10:22:09 -0700708 dottedPkg := "." + f.GetPackage()
Rob Pikec9e7d972010-06-10 10:30:22 -0700709 if dottedPkg != "." {
710 dottedPkg += "."
711 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700712 for _, enum := range f.enum {
713 name := dottedPkg + dottedSlice(enum.TypeName())
714 g.typeNameToObject[name] = enum
715 }
716 for _, desc := range f.desc {
717 name := dottedPkg + dottedSlice(desc.TypeName())
718 g.typeNameToObject[name] = desc
719 }
720 }
721}
722
723// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
724// returns the descriptor for the message or enum with that name.
725func (g *Generator) ObjectNamed(typeName string) Object {
David Symonds4decd802011-08-04 11:27:07 +1000726 o, ok := g.typeNameToObject[typeName]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700727 if !ok {
728 g.Fail("can't find object with type", typeName)
729 }
David Symonds4decd802011-08-04 11:27:07 +1000730
731 // If the file of this object isn't a direct dependency of the current file,
732 // or in the current file, then this object has been publicly imported into
733 // a dependency of the current file.
734 // We should return the ImportedDescriptor object for it instead.
735 direct := *o.File().Name == *g.file.Name
736 if !direct {
737 for _, dep := range g.file.Dependency {
738 if *g.fileByName(dep).Name == *o.File().Name {
739 direct = true
740 break
741 }
742 }
743 }
744 if !direct {
745 found := false
746 Loop:
747 for _, dep := range g.file.Dependency {
748 df := g.fileByName(*g.fileByName(dep).Name)
749 for _, td := range df.imp {
750 if td.o == o {
751 // Found it!
752 o = td
753 found = true
754 break Loop
755 }
756 }
757 }
758 if !found {
759 log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name)
760 }
761 }
762
763 return o
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700764}
765
766// P prints the arguments to the generated output. It handles strings and int32s, plus
767// handling indirections because they may be *string, etc.
768func (g *Generator) P(str ...interface{}) {
769 g.WriteString(g.indent)
770 for _, v := range str {
771 switch s := v.(type) {
772 case string:
773 g.WriteString(s)
774 case *string:
775 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700776 case bool:
777 g.WriteString(fmt.Sprintf("%t", s))
778 case *bool:
779 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700780 case *int32:
781 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700782 case float64:
783 g.WriteString(fmt.Sprintf("%g", s))
784 case *float64:
785 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700786 default:
787 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
788 }
789 }
790 g.WriteByte('\n')
791}
792
793// In Indents the output one tab stop.
794func (g *Generator) In() { g.indent += "\t" }
795
796// Out unindents the output one tab stop.
797func (g *Generator) Out() {
798 if len(g.indent) > 0 {
799 g.indent = g.indent[1:]
800 }
801}
802
803// GenerateAllFiles generates the output for all the files we're outputting.
804func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700805 // Initialize the plugins
806 for _, p := range plugins {
807 p.Init(g)
808 }
David Symonds31d58a22011-01-20 18:33:21 +1100809 // Generate the output. The generator runs for every file, even the files
810 // that we don't generate output for, so that we can collate the full list
811 // of exported symbols to support public imports.
812 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
813 for _, file := range g.genFiles {
814 genFileMap[file] = true
815 }
816 i := 0
817 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700818 g.Reset()
819 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100820 if _, ok := genFileMap[file]; !ok {
821 continue
822 }
David Symondsb0127532010-11-09 11:10:46 +1100823 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700824 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
825 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100826 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700827 }
828}
829
830// Run all the plugins associated with the file.
831func (g *Generator) runPlugins(file *FileDescriptor) {
832 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700833 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700834 }
835}
836
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700837// FileOf return the FileDescriptor for this FileDescriptorProto.
838func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
839 for _, file := range g.allFiles {
840 if file.FileDescriptorProto == fd {
841 return file
842 }
843 }
David Symonds8bb32ca2012-06-28 10:22:09 -0700844 g.Fail("could not find file in table:", fd.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700845 return nil
846}
847
848// Fill the response protocol buffer with the generated output for all the files we're
849// supposed to generate.
850func (g *Generator) generate(file *FileDescriptor) {
851 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000852 g.usedPackages = make(map[string]bool)
853
David Symonds4decd802011-08-04 11:27:07 +1000854 for _, td := range g.file.imp {
855 g.generateImported(td)
856 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700857 for _, enum := range g.file.enum {
858 g.generateEnum(enum)
859 }
860 for _, desc := range g.file.desc {
861 g.generateMessage(desc)
862 }
863 for _, ext := range g.file.ext {
864 g.generateExtension(ext)
865 }
866 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000867
Rob Pikec9e7d972010-06-10 10:30:22 -0700868 // Run the plugins before the imports so we know which imports are necessary.
869 g.runPlugins(file)
870
David Symondsf90e3382010-05-05 10:53:44 +1000871 // Generate header and imports last, though they appear first in the output.
872 rem := g.Buffer
873 g.Buffer = new(bytes.Buffer)
874 g.generateHeader()
875 g.generateImports()
876 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +1000877
878 // Reformat generated code.
879 fset := token.NewFileSet()
880 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
881 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -0700882 g.Fail("bad Go source code was generated:", err.Error())
David Symondsb1d55a02011-04-08 09:55:06 +1000883 return
884 }
885 g.Reset()
David Symonds5ed980c2011-11-29 09:52:21 +1100886 err = (&printer.Config{printer.TabIndent | printer.UseSpaces, 8}).Fprint(g, fset, ast)
David Symondsb1d55a02011-04-08 09:55:06 +1000887 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -0700888 g.Fail("generated Go source code could not be reformatted:", err.Error())
David Symondsb1d55a02011-04-08 09:55:06 +1000889 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700890}
891
David Symonds62539862012-08-04 10:06:55 +1000892// Generate the header, including package definition
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700893func (g *Generator) generateHeader() {
894 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
895 g.P("// DO NOT EDIT!")
896 g.P()
897 g.P("package ", g.file.PackageName())
898 g.P()
899}
900
David Symonds31d58a22011-01-20 18:33:21 +1100901func (g *Generator) fileByName(filename string) *FileDescriptor {
902 for _, fd := range g.allFiles {
David Symonds8bb32ca2012-06-28 10:22:09 -0700903 if fd.GetName() == filename {
David Symonds31d58a22011-01-20 18:33:21 +1100904 return fd
905 }
906 }
907 return nil
908}
909
David Symonds9685cb02012-02-13 08:10:34 +1100910// weak returns whether the ith import of the current file is a weak import.
911func (g *Generator) weak(i int32) bool {
912 for _, j := range g.file.WeakDependency {
913 if j == i {
914 return true
915 }
916 }
917 return false
918}
919
David Symonds62539862012-08-04 10:06:55 +1000920// Generate the imports
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700921func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700922 // We almost always need a proto import. Rather than computing when we
923 // do, which is tricky when there's a plugin, just import it and
Rob Pikea17fdd92011-11-02 12:43:05 -0700924 // reference it later. The same argument applies to the math package,
David Symonds11db5982012-08-15 10:54:05 +1000925 // for handling bit patterns for floating-point numbers, and to the
926 // json package, for symbolic names of enum values for JSON marshaling.
927 g.P("import " + g.Pkg["proto"] + " " + Quote(g.ImportPrefix+"code.google.com/p/goprotobuf/proto"))
928 g.P("import " + g.Pkg["json"] + ` "encoding/json"`)
929 g.P("import " + g.Pkg["math"] + ` "math"`)
David Symonds9685cb02012-02-13 08:10:34 +1100930 for i, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100931 fd := g.fileByName(s)
932 // Do not import our own package.
933 if fd.PackageName() == g.packageName {
934 continue
935 }
936 filename := goFileName(s)
937 if substitution, ok := g.ImportMap[s]; ok {
938 filename = substitution
939 }
940 filename = g.ImportPrefix + filename
941 if strings.HasSuffix(filename, ".go") {
942 filename = filename[0 : len(filename)-3]
943 }
David Symonds9685cb02012-02-13 08:10:34 +1100944 // Skip weak imports.
945 if g.weak(int32(i)) {
946 g.P("// skipping weak import ", fd.PackageName(), " ", Quote(filename))
947 continue
948 }
David Symonds31d58a22011-01-20 18:33:21 +1100949 if _, ok := g.usedPackages[fd.PackageName()]; ok {
950 g.P("import ", fd.PackageName(), " ", Quote(filename))
951 } else {
David Symonds3fa055f2011-05-05 15:19:04 -0700952 // TODO: Re-enable this when we are more feature-complete.
953 // For instance, some protos use foreign field extensions, which we don't support.
954 // Until then, this is just annoying spam.
955 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
David Symonds4decd802011-08-04 11:27:07 +1000956 g.P("// discarding unused import ", fd.PackageName(), " ", Quote(filename))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700957 }
958 }
959 g.P()
960 // TODO: may need to worry about uniqueness across plugins
961 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700962 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700963 g.P()
964 }
David Symonds62539862012-08-04 10:06:55 +1000965 g.P("// Reference proto, json, and math imports to suppress error if they are not otherwise used.")
David Symonds11db5982012-08-15 10:54:05 +1000966 g.P("var _ = ", g.Pkg["proto"], ".Marshal")
David Symonds62539862012-08-04 10:06:55 +1000967 g.P("var _ = &json.SyntaxError{}")
David Symondscea785b2011-01-07 11:02:30 +1100968 g.P("var _ = math.Inf")
Rob Pikec9e7d972010-06-10 10:30:22 -0700969 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700970}
971
David Symonds4decd802011-08-04 11:27:07 +1000972func (g *Generator) generateImported(id *ImportedDescriptor) {
David Symondsb2a00c82011-08-04 16:52:58 +1000973 // Don't generate public import symbols for files that we are generating
974 // code for, since those symbols will already be in this package.
975 // We can't simply avoid creating the ImportedDescriptor objects,
976 // because g.genFiles isn't populated at that stage.
David Symonds4decd802011-08-04 11:27:07 +1000977 tn := id.TypeName()
978 sn := tn[len(tn)-1]
David Symondsb2a00c82011-08-04 16:52:58 +1000979 df := g.FileOf(id.o.File())
980 filename := *df.Name
981 for _, fd := range g.genFiles {
982 if *fd.Name == filename {
983 g.P("// Ignoring public import of ", sn, " from ", filename)
984 g.P()
985 return
986 }
987 }
988 g.P("// ", sn, " from public import ", filename)
989 g.usedPackages[df.PackageName()] = true
David Symonds4decd802011-08-04 11:27:07 +1000990
David Symondsb2a00c82011-08-04 16:52:58 +1000991 for _, sym := range df.exported[id.o] {
992 sym.GenerateAlias(g, df.PackageName())
993 }
994
995 g.P()
David Symonds4decd802011-08-04 11:27:07 +1000996}
997
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700998// Generate the enum definitions for this EnumDescriptor.
999func (g *Generator) generateEnum(enum *EnumDescriptor) {
1000 // The full type name
1001 typeName := enum.TypeName()
1002 // The full type name, CamelCased.
1003 ccTypeName := CamelCaseSlice(typeName)
1004 ccPrefix := enum.prefix()
1005 g.P("type ", ccTypeName, " int32")
David Symondsb2a00c82011-08-04 16:52:58 +10001006 g.file.addExport(enum, enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001007 g.P("const (")
1008 g.In()
1009 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +11001010 name := ccPrefix + *e.Name
David Symondsdd9ca042011-08-29 16:07:16 +10001011 g.P(name, " ", ccTypeName, " = ", e.Number)
David Symondsb2a00c82011-08-04 16:52:58 +10001012 g.file.addExport(enum, constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001013 }
1014 g.Out()
1015 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +11001016 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001017 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -07001018 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001019 for _, e := range enum.Value {
1020 duplicate := ""
1021 if _, present := generated[*e.Number]; present {
1022 duplicate = "// Duplicate value: "
1023 }
1024 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
1025 generated[*e.Number] = true
1026 }
1027 g.Out()
1028 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +11001029 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001030 g.In()
1031 for _, e := range enum.Value {
1032 g.P(Quote(*e.Name), ": ", e.Number, ",")
1033 }
1034 g.Out()
1035 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +11001036
David Symondsefeca9a2012-05-08 10:36:04 +10001037 g.P("func (x ", ccTypeName, ") Enum() *", ccTypeName, " {")
1038 g.In()
1039 g.P("p := new(", ccTypeName, ")")
1040 g.P("*p = x")
1041 g.P("return p")
1042 g.Out()
1043 g.P("}")
1044
David Symonds940b9612011-04-01 10:45:23 +11001045 g.P("func (x ", ccTypeName, ") String() string {")
1046 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001047 g.P("return ", g.Pkg["proto"], ".EnumName(", ccTypeName, "_name, int32(x))")
David Symonds940b9612011-04-01 10:45:23 +11001048 g.Out()
1049 g.P("}")
1050
David Symonds62539862012-08-04 10:06:55 +10001051 g.P("func (x ", ccTypeName, ") MarshalJSON() ([]byte, error) {")
1052 g.In()
1053 g.P("return json.Marshal(x.String())")
1054 g.Out()
1055 g.P("}")
1056
1057 g.P("func (x *", ccTypeName, ") UnmarshalJSON(data []byte) error {")
1058 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001059 g.P("value, err := ", g.Pkg["proto"], ".UnmarshalJSONEnum(", ccTypeName, `_value, data, "`, ccTypeName, `")`)
David Symonds62539862012-08-04 10:06:55 +10001060 g.P("if err != nil {")
1061 g.In()
1062 g.P("return err")
1063 g.Out()
1064 g.P("}")
1065 g.P("*x = ", ccTypeName, "(value)")
1066 g.P("return nil")
1067 g.Out()
1068 g.P("}")
1069
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001070 g.P()
1071}
1072
David Symonds8935abf2011-07-04 15:53:16 +10001073// The tag is a string like "varint,2,opt,name=fieldname,def=7" that
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001074// identifies details of the field for the protocol buffer marshaling and unmarshaling
1075// code. The fields are:
1076// wire encoding
1077// protocol tag number
1078// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +11001079// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001080// name= the original declared name
1081// enum= the name of the enum type if it is an enum-typed field.
1082// def= string representation of the default value, if any.
1083// The default value must be in a representation that can be used at run-time
1084// to generate the default value. Thus bools become 0 and 1, for instance.
1085func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
1086 optrepreq := ""
1087 switch {
1088 case isOptional(field):
1089 optrepreq = "opt"
1090 case isRequired(field):
1091 optrepreq = "req"
1092 case isRepeated(field):
1093 optrepreq = "rep"
1094 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001095 defaultValue := field.GetDefaultValue()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001096 if defaultValue != "" {
1097 switch *field.Type {
1098 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1099 if defaultValue == "true" {
1100 defaultValue = "1"
1101 } else {
1102 defaultValue = "0"
1103 }
1104 case descriptor.FieldDescriptorProto_TYPE_STRING,
1105 descriptor.FieldDescriptorProto_TYPE_BYTES:
David Symondsb79d99b2011-08-29 16:38:49 +10001106 // Nothing to do. Quoting is done for the whole tag.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001107 case descriptor.FieldDescriptorProto_TYPE_ENUM:
1108 // For enums we need to provide the integer constant.
David Symonds8bb32ca2012-06-28 10:22:09 -07001109 obj := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001110 enum, ok := obj.(*EnumDescriptor)
1111 if !ok {
1112 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
1113 }
1114 defaultValue = enum.integerValueAsString(defaultValue)
1115 }
1116 defaultValue = ",def=" + defaultValue
1117 }
1118 enum := ""
1119 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
David Symonds4decd802011-08-04 11:27:07 +10001120 // We avoid using obj.PackageName(), because we want to use the
1121 // original (proto-world) package name.
David Symonds8bb32ca2012-06-28 10:22:09 -07001122 obj := g.ObjectNamed(field.GetTypeName())
David Symonds4decd802011-08-04 11:27:07 +10001123 enum = ",enum="
David Symonds8bb32ca2012-06-28 10:22:09 -07001124 if pkg := obj.File().GetPackage(); pkg != "" {
David Symonds4decd802011-08-04 11:27:07 +10001125 enum += pkg + "."
1126 }
1127 enum += CamelCaseSlice(obj.TypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001128 }
David Symonds5b7775e2010-12-01 10:09:04 +11001129 packed := ""
David Symonds8bb32ca2012-06-28 10:22:09 -07001130 if field.Options != nil && field.Options.GetPacked() {
David Symonds5b7775e2010-12-01 10:09:04 +11001131 packed = ",packed"
1132 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001133 fieldName := field.GetName()
David Symondse37856c2011-06-22 12:52:53 +10001134 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +10001135 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
1136 // We must use the type name for groups instead of
1137 // the field name to preserve capitalization.
1138 // type_name in FieldDescriptorProto is fully-qualified,
1139 // but we only want the local part.
1140 name = *field.TypeName
1141 if i := strings.LastIndex(name, "."); i >= 0 {
1142 name = name[i+1:]
1143 }
David Symondse37856c2011-06-22 12:52:53 +10001144 }
1145 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001146 name = ""
1147 } else {
1148 name = ",name=" + name
1149 }
David Symonds8935abf2011-07-04 15:53:16 +10001150 return Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001151 wiretype,
David Symonds8bb32ca2012-06-28 10:22:09 -07001152 field.GetNumber(),
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001153 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +11001154 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001155 name,
1156 enum,
1157 defaultValue))
1158}
1159
1160func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
1161 switch typ {
1162 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1163 return false
1164 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1165 return false
1166 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1167 return false
1168 }
1169 return true
1170}
1171
1172// TypeName is the printed name appropriate for an item. If the object is in the current file,
1173// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -07001174// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001175// package name followed by the item name.
1176// The result always has an initial capital.
1177func (g *Generator) TypeName(obj Object) string {
1178 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
1179}
1180
1181// TypeNameWithPackage is like TypeName, but always includes the package
1182// name even if the object is in our own package.
1183func (g *Generator) TypeNameWithPackage(obj Object) string {
1184 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
1185}
1186
1187// GoType returns a string representing the type name, and the wire type
1188func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
1189 // TODO: Options.
1190 switch *field.Type {
1191 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
1192 typ, wire = "float64", "fixed64"
1193 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
1194 typ, wire = "float32", "fixed32"
1195 case descriptor.FieldDescriptorProto_TYPE_INT64:
1196 typ, wire = "int64", "varint"
1197 case descriptor.FieldDescriptorProto_TYPE_UINT64:
1198 typ, wire = "uint64", "varint"
1199 case descriptor.FieldDescriptorProto_TYPE_INT32:
1200 typ, wire = "int32", "varint"
1201 case descriptor.FieldDescriptorProto_TYPE_UINT32:
1202 typ, wire = "uint32", "varint"
1203 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
1204 typ, wire = "uint64", "fixed64"
1205 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
1206 typ, wire = "uint32", "fixed32"
1207 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1208 typ, wire = "bool", "varint"
1209 case descriptor.FieldDescriptorProto_TYPE_STRING:
1210 typ, wire = "string", "bytes"
1211 case descriptor.FieldDescriptorProto_TYPE_GROUP:
David Symonds8bb32ca2012-06-28 10:22:09 -07001212 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001213 typ, wire = "*"+g.TypeName(desc), "group"
1214 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
David Symonds8bb32ca2012-06-28 10:22:09 -07001215 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001216 typ, wire = "*"+g.TypeName(desc), "bytes"
1217 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1218 typ, wire = "[]byte", "bytes"
1219 case descriptor.FieldDescriptorProto_TYPE_ENUM:
David Symonds8bb32ca2012-06-28 10:22:09 -07001220 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001221 typ, wire = g.TypeName(desc), "varint"
1222 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
1223 typ, wire = "int32", "fixed32"
1224 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
1225 typ, wire = "int64", "fixed64"
1226 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1227 typ, wire = "int32", "zigzag32"
1228 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1229 typ, wire = "int64", "zigzag64"
1230 default:
David Symonds8bb32ca2012-06-28 10:22:09 -07001231 g.Fail("unknown type for", field.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001232 }
1233 if isRepeated(field) {
1234 typ = "[]" + typ
1235 } else if needsStar(*field.Type) {
1236 typ = "*" + typ
1237 }
1238 return
1239}
1240
David Symondsf90e3382010-05-05 10:53:44 +10001241func (g *Generator) RecordTypeUse(t string) {
1242 if obj, ok := g.typeNameToObject[t]; ok {
David Symonds4decd802011-08-04 11:27:07 +10001243 // Call ObjectNamed to get the true object to record the use.
1244 obj = g.ObjectNamed(t)
David Symondsf90e3382010-05-05 10:53:44 +10001245 g.usedPackages[obj.PackageName()] = true
1246 }
1247}
1248
David Symonds3ce53032012-02-13 10:55:03 +11001249// Method names that may be generated. Fields with these names get an
1250// underscore appended.
1251var methodNames = [...]string{
1252 "Reset",
1253 "String",
David Symonds9f60f432012-06-14 09:45:25 +10001254 "ProtoMessage",
David Symonds3ce53032012-02-13 10:55:03 +11001255 "Marshal",
1256 "Unmarshal",
1257 "ExtensionRangeArray",
1258 "ExtensionMap",
1259}
1260
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001261// Generate the type and default constant definitions for this Descriptor.
1262func (g *Generator) generateMessage(message *Descriptor) {
1263 // The full type name
1264 typeName := message.TypeName()
1265 // The full type name, CamelCased.
1266 ccTypeName := CamelCaseSlice(typeName)
1267
David Symonds3ce53032012-02-13 10:55:03 +11001268 usedNames := make(map[string]bool)
1269 for _, n := range methodNames {
1270 usedNames[n] = true
1271 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001272 fieldNames := make(map[*descriptor.FieldDescriptorProto]string)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001273 g.P("type ", ccTypeName, " struct {")
1274 g.In()
1275 for _, field := range message.Field {
1276 fieldname := CamelCase(*field.Name)
David Symonds3ce53032012-02-13 10:55:03 +11001277 for usedNames[fieldname] {
1278 fieldname += "_"
1279 }
1280 usedNames[fieldname] = true
David Symonds8bb32ca2012-06-28 10:22:09 -07001281 fieldNames[field] = fieldname
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001282 typename, wiretype := g.GoType(message, field)
David Symondsb2a00c82011-08-04 16:52:58 +10001283 jsonName := *field.Name
David Symonds002ec402011-09-29 17:24:20 -07001284 tag := fmt.Sprintf("`protobuf:%s json:%q`", g.goTag(field, wiretype), jsonName+",omitempty")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001285 g.P(fieldname, "\t", typename, "\t", tag)
David Symonds8bb32ca2012-06-28 10:22:09 -07001286 g.RecordTypeUse(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001287 }
1288 if len(message.ExtensionRange) > 0 {
David Symonds11db5982012-08-15 10:54:05 +10001289 g.P("XXX_extensions\t\tmap[int32]", g.Pkg["proto"], ".Extension `json:\"-\"`")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001290 }
David Symondsfd08ece2012-03-23 13:48:31 +11001291 g.P("XXX_unrecognized\t[]byte `json:\"-\"`")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001292 g.Out()
1293 g.P("}")
1294
David Symonds9f60f432012-06-14 09:45:25 +10001295 // Reset, String and ProtoMessage methods.
David Symondse37856c2011-06-22 12:52:53 +10001296 g.P("func (this *", ccTypeName, ") Reset() { *this = ", ccTypeName, "{} }")
David Symonds11db5982012-08-15 10:54:05 +10001297 g.P("func (this *", ccTypeName, ") String() string { return ", g.Pkg["proto"], ".CompactTextString(this) }")
David Symonds9f60f432012-06-14 09:45:25 +10001298 g.P("func (*", ccTypeName, ") ProtoMessage() {}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001299
1300 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001301 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001302 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001303 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001304 // message_set_wire_format only makes sense when extensions are defined.
David Symonds8bb32ca2012-06-28 10:22:09 -07001305 if opts := message.Options; opts != nil && opts.GetMessageSetWireFormat() {
David Symonds31d58a22011-01-20 18:33:21 +11001306 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001307 g.P()
Rob Pikea17fdd92011-11-02 12:43:05 -07001308 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, error) {")
David Symonds4fee3b12010-11-11 10:00:13 +11001309 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001310 g.P("return ", g.Pkg["proto"], ".MarshalMessageSet(this.ExtensionMap())")
David Symonds4fee3b12010-11-11 10:00:13 +11001311 g.Out()
1312 g.P("}")
Rob Pikea17fdd92011-11-02 12:43:05 -07001313 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) error {")
David Symonds4fee3b12010-11-11 10:00:13 +11001314 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001315 g.P("return ", g.Pkg["proto"], ".UnmarshalMessageSet(buf, this.ExtensionMap())")
David Symonds4fee3b12010-11-11 10:00:13 +11001316 g.Out()
1317 g.P("}")
1318 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
David Symonds11db5982012-08-15 10:54:05 +10001319 g.P("var _ ", g.Pkg["proto"], ".Marshaler = (*", ccTypeName, ")(nil)")
1320 g.P("var _ ", g.Pkg["proto"], ".Unmarshaler = (*", ccTypeName, ")(nil)")
David Symonds4fee3b12010-11-11 10:00:13 +11001321 }
1322
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001323 g.P()
David Symonds11db5982012-08-15 10:54:05 +10001324 g.P("var extRange_", ccTypeName, " = []", g.Pkg["proto"], ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001325 g.In()
1326 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001327 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
David Symonds1d72f7a2011-08-19 18:28:52 +10001328 g.P("{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001329 }
1330 g.Out()
1331 g.P("}")
David Symonds11db5982012-08-15 10:54:05 +10001332 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.Pkg["proto"], ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001333 g.In()
1334 g.P("return extRange_", ccTypeName)
1335 g.Out()
1336 g.P("}")
David Symonds11db5982012-08-15 10:54:05 +10001337 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32]", g.Pkg["proto"], ".Extension {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001338 g.In()
1339 g.P("if this.XXX_extensions == nil {")
1340 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001341 g.P("this.XXX_extensions = make(map[int32]", g.Pkg["proto"], ".Extension)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001342 g.Out()
1343 g.P("}")
1344 g.P("return this.XXX_extensions")
1345 g.Out()
1346 g.P("}")
1347 }
1348
David Symondsb2a00c82011-08-04 16:52:58 +10001349 g.file.addExport(message, messageSymbol{ccTypeName, hasExtensions, isMessageSet})
David Symonds31d58a22011-01-20 18:33:21 +11001350
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001351 // Default constants
David Symonds8bb32ca2012-06-28 10:22:09 -07001352 defNames := make(map[*descriptor.FieldDescriptorProto]string)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001353 for _, field := range message.Field {
David Symonds8bb32ca2012-06-28 10:22:09 -07001354 def := field.GetDefaultValue()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001355 if def == "" {
1356 continue
1357 }
1358 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
David Symonds8bb32ca2012-06-28 10:22:09 -07001359 defNames[field] = fieldname
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001360 typename, _ := g.GoType(message, field)
1361 if typename[0] == '*' {
1362 typename = typename[1:]
1363 }
1364 kind := "const "
1365 switch {
1366 case typename == "bool":
1367 case typename == "string":
1368 def = Quote(def)
1369 case typename == "[]byte":
1370 def = "[]byte(" + Quote(def) + ")"
1371 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001372 case def == "inf", def == "-inf", def == "nan":
1373 // These names are known to, and defined by, the protocol language.
1374 switch def {
1375 case "inf":
1376 def = "math.Inf(1)"
1377 case "-inf":
1378 def = "math.Inf(-1)"
1379 case "nan":
1380 def = "math.NaN()"
1381 }
1382 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1383 def = "float32(" + def + ")"
1384 }
1385 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001386 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1387 // Must be an enum. Need to construct the prefixed name.
David Symonds8bb32ca2012-06-28 10:22:09 -07001388 obj := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001389 enum, ok := obj.(*EnumDescriptor)
1390 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001391 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001392 continue
1393 }
Rob Pike87af39e2010-07-19 10:48:02 -07001394 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001395 }
1396 g.P(kind, fieldname, " ", typename, " = ", def)
David Symondsb2a00c82011-08-04 16:52:58 +10001397 g.file.addExport(message, constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001398 }
1399 g.P()
1400
David Symonds8bb32ca2012-06-28 10:22:09 -07001401 // Field getters
1402 // TODO: Generate getters for publicly imported aliases, if required.
1403 for _, field := range message.Field {
1404 if isRepeated(field) {
1405 continue
1406 }
1407 fname := fieldNames[field]
1408 typename, _ := g.GoType(message, field)
David Symonds8bb32ca2012-06-28 10:22:09 -07001409 mname := "Get" + fname
1410 star := ""
1411 if needsStar(*field.Type) && typename[0] == '*' {
1412 typename = typename[1:]
1413 star = "*"
1414 }
1415 g.P("func (this *", ccTypeName, ") "+mname+"() "+typename+" {")
1416 g.In()
1417 def, hasDef := defNames[field]
1418 typeDefaultIsNil := false // whether this field type's default value is a literal nil unless specified
1419 switch *field.Type {
1420 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1421 typeDefaultIsNil = !hasDef
1422 case descriptor.FieldDescriptorProto_TYPE_GROUP, descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1423 typeDefaultIsNil = true
1424 }
1425 if typeDefaultIsNil {
1426 // A bytes field with no explicit default needs less generated code,
1427 // as does a message or group field.
1428 g.P("if this != nil {")
1429 g.In()
1430 g.P("return this." + fname)
1431 g.Out()
1432 g.P("}")
1433 g.P("return nil")
1434 g.Out()
1435 g.P("}")
1436 g.P()
1437 continue
1438 }
1439 g.P("if this != nil && this." + fname + " != nil {")
1440 g.In()
1441 g.P("return " + star + "this." + fname)
1442 g.Out()
1443 g.P("}")
1444 if hasDef {
1445 if *field.Type != descriptor.FieldDescriptorProto_TYPE_BYTES {
1446 g.P("return " + def)
1447 } else {
1448 // The default is a []byte var.
1449 // Make a copy when returning it to be safe.
1450 g.P("return append([]byte(nil), ", def, "...)")
1451 }
1452 } else {
1453 switch *field.Type {
1454 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1455 g.P("return false")
1456 case descriptor.FieldDescriptorProto_TYPE_STRING:
1457 g.P(`return ""`)
1458 default:
1459 g.P("return 0")
1460 }
1461 }
1462 g.Out()
1463 g.P("}")
1464 g.P()
1465 }
1466
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001467 for _, ext := range message.ext {
1468 g.generateExtension(ext)
1469 }
David Symonds525838c2012-07-20 15:42:49 +10001470
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001471}
1472
1473func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001474 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001475
1476 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1477 field := ext.FieldDescriptorProto
1478 fieldType, wireType := g.GoType(ext.parent, field)
1479 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001480 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001481 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1482 // foreign extension type
1483 g.RecordTypeUse(*n)
1484 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001485
David Symonds20c73662012-01-20 07:32:21 +11001486 typeName := ext.TypeName()
1487
1488 // Special case for proto2 message sets: If this extension is extending
1489 // proto2_bridge.MessageSet, and its final name component is "message_set_extension",
1490 // then drop that last component.
1491 if extendedType == "*proto2_bridge.MessageSet" && typeName[len(typeName)-1] == "message_set_extension" {
1492 typeName = typeName[:len(typeName)-1]
1493 }
1494
David Symondsc057ad52012-02-11 15:43:42 +11001495 // For text formatting, the package must be exactly what the .proto file declares,
1496 // ignoring overrides such as the go_package option, and with no dot/underscore mapping.
1497 extName := strings.Join(typeName, ".")
1498 if g.file.Package != nil {
1499 extName = *g.file.Package + "." + extName
1500 }
1501
David Symonds11db5982012-08-15 10:54:05 +10001502 g.P("var ", ccTypeName, " = &", g.Pkg["proto"], ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001503 g.In()
1504 g.P("ExtendedType: (", extendedType, ")(nil),")
1505 g.P("ExtensionType: (", fieldType, ")(nil),")
1506 g.P("Field: ", field.Number, ",")
David Symondsc057ad52012-02-11 15:43:42 +11001507 g.P(`Name: "`, extName, `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001508 g.P("Tag: ", tag, ",")
1509
1510 g.Out()
1511 g.P("}")
1512 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001513
David Symondsb2a00c82011-08-04 16:52:58 +10001514 g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001515}
1516
1517func (g *Generator) generateInitFunction() {
1518 g.P("func init() {")
1519 g.In()
1520 for _, enum := range g.file.enum {
1521 g.generateEnumRegistration(enum)
1522 }
David Symondse37856c2011-06-22 12:52:53 +10001523 for _, d := range g.file.desc {
1524 for _, ext := range d.ext {
1525 g.generateExtensionRegistration(ext)
1526 }
1527 }
1528 for _, ext := range g.file.ext {
1529 g.generateExtensionRegistration(ext)
1530 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001531 g.Out()
1532 g.P("}")
1533}
1534
1535func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
David Symonds4decd802011-08-04 11:27:07 +10001536 // // We always print the full (proto-world) package name here.
David Symonds8bb32ca2012-06-28 10:22:09 -07001537 pkg := enum.File().GetPackage()
David Symonds4decd802011-08-04 11:27:07 +10001538 if pkg != "" {
1539 pkg += "."
1540 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001541 // The full type name
1542 typeName := enum.TypeName()
1543 // The full type name, CamelCased.
1544 ccTypeName := CamelCaseSlice(typeName)
David Symonds11db5982012-08-15 10:54:05 +10001545 g.P(g.Pkg["proto"]+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001546}
1547
David Symondse37856c2011-06-22 12:52:53 +10001548func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
David Symonds11db5982012-08-15 10:54:05 +10001549 g.P(g.Pkg["proto"]+".RegisterExtension(", ext.DescName(), ")")
David Symondse37856c2011-06-22 12:52:53 +10001550}
1551
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001552// And now lots of helper functions.
1553
Rob Pike2c7bafc2010-06-10 16:07:14 -07001554// Is c an ASCII lower-case letter?
1555func isASCIILower(c byte) bool {
1556 return 'a' <= c && c <= 'z'
1557}
1558
1559// Is c an ASCII digit?
1560func isASCIIDigit(c byte) bool {
1561 return '0' <= c && c <= '9'
1562}
1563
1564// CamelCase returns the CamelCased name.
1565// If there is an interior underscore followed by a lower case letter,
1566// drop the underscore and convert the letter to upper case.
1567// There is a remote possibility of this rewrite causing a name collision,
1568// but it's so remote we're prepared to pretend it's nonexistent - since the
1569// C++ generator lowercases names, it's extremely unlikely to have two fields
1570// with different capitalizations.
1571// In short, _my_field_name_2 becomes XMyFieldName2.
1572func CamelCase(s string) string {
1573 if s == "" {
1574 return ""
1575 }
1576 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001577 i := 0
1578 if s[0] == '_' {
1579 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001580 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001581 i++
1582 }
1583 // Invariant: if the next letter is lower case, it must be converted
1584 // to upper case.
1585 // That is, we process a word at a time, where words are marked by _ or
1586 // upper case letter. Digits are treated as words.
1587 for ; i < len(s); i++ {
1588 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001589 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1590 continue // Skip the underscore in s.
1591 }
1592 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001593 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001594 continue
1595 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001596 // Assume we have a letter now - if not, it's a bogus identifier.
1597 // The next word is a sequence of characters that must start upper case.
1598 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001599 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001600 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001601 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001602 // Accept lower case sequence that follows.
1603 for i+1 < len(s) && isASCIILower(s[i+1]) {
1604 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001605 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001606 }
1607 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001608 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001609}
1610
1611// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1612// be joined with "_".
1613func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1614
1615// dottedSlice turns a sliced name into a dotted name.
1616func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1617
1618// Quote returns a Go-source quoted string representation of s.
1619func Quote(s string) string { return fmt.Sprintf("%q", s) }
1620
1621// Given a .proto file name, return the output name for the generated Go program.
1622func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001623 ext := path.Ext(name)
1624 if ext == ".proto" || ext == ".protodevel" {
1625 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001626 }
1627 return name + ".pb.go"
1628}
1629
1630// Is this field optional?
1631func isOptional(field *descriptor.FieldDescriptorProto) bool {
1632 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1633}
1634
1635// Is this field required?
1636func isRequired(field *descriptor.FieldDescriptorProto) bool {
1637 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1638}
1639
1640// Is this field repeated?
1641func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1642 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1643}
1644
David Symonds162d0032012-06-28 09:44:46 -07001645// BadToUnderscore is the mapping function used to generate Go names from package names,
1646// which can be dotted in the input .proto file. It replaces non-identifier characters such as
1647// dot or dash with underscore.
1648func BadToUnderscore(r rune) rune {
1649 if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
1650 return r
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001651 }
David Symonds162d0032012-06-28 09:44:46 -07001652 return '_'
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001653}
Rob Pikec9e7d972010-06-10 10:30:22 -07001654
1655// BaseName returns the last path element of the name, with the last dotted suffix removed.
1656func BaseName(name string) string {
1657 // First, find the last element
1658 if i := strings.LastIndex(name, "/"); i >= 0 {
1659 name = name[i+1:]
1660 }
1661 // Now drop the suffix
1662 if i := strings.LastIndex(name, "."); i >= 0 {
1663 name = name[0:i]
1664 }
1665 return name
1666}