Add extensions to the generator.
R=r
CC=golang-dev
http://codereview.appspot.com/774041
diff --git a/README b/README
index c90ae44..6c12f05 100644
--- a/README
+++ b/README
@@ -24,9 +24,6 @@
There is no support for RPC in Go using protocol buffers. It may come
once a standard RPC protocol develops for protobufs.
-Extensions are not supported by the plugin in this release, although
-they are in the library. The work will be completed soon.
-
There are no insertion points in the plugin.
To install this code:
@@ -49,7 +46,7 @@
them, with the support library, into your program.
To compile the protocol buffer definition, write a Makefile in the
-style shown in the commentin the file Make.protobuf. If your Makefile
+style shown in the comment in the file Make.protobuf. If your Makefile
includes Make.protobuf, the rest should follow automatically. The
generated code can be compiled separately or as part of a normal Go
package.
diff --git a/compiler/main.go b/compiler/main.go
index f2cb3b2..5864cde 100644
--- a/compiler/main.go
+++ b/compiler/main.go
@@ -37,7 +37,6 @@
That may change.
Not supported yet:
- Extensions
Services
*/
@@ -114,9 +113,10 @@
type Descriptor struct {
common
*descriptor.DescriptorProto
- parent *Descriptor // The containing message, if any.
- nested []*Descriptor // Inner messages, if any.
- typename []string // Cached typename vector.
+ parent *Descriptor // The containing message, if any.
+ nested []*Descriptor // Inner messages, if any.
+ ext []*ExtensionDescriptor // Extensions, if any.
+ typename []string // Cached typename vector.
}
// Return the elements of the dotted type name. The package name is not part
@@ -188,12 +188,36 @@
return ""
}
+// An extension. If it's at top level, its parent will be nil. Otherwise it will be
+// the descriptor of the message in which it is defined.
+type ExtensionDescriptor struct {
+ common
+ *descriptor.FieldDescriptorProto
+ parent *Descriptor // The containing message, if any.
+}
+
+// Return the elements of the dotted type name.
+func (e *ExtensionDescriptor) typeName() (s []string) {
+ name := proto.GetString(e.Name)
+ if e.parent == nil {
+ s = make([]string, 2)
+ s[0] = "E" // top-level extension namespace
+ } else {
+ pname := e.parent.typeName()
+ s = make([]string, len(pname)+1)
+ copy(s, pname)
+ }
+ s[len(s)-1] = name
+ return s
+}
+
// A file. Includes slices of all the messages and enums defined within it.
// Those slices are constructed by WrapTypes.
type FileDescriptor struct {
*descriptor.FileDescriptorProto
- desc []*Descriptor // All the messages defined in this file.
- enum []*EnumDescriptor // All the enums defined in this file.
+ desc []*Descriptor // All the messages defined in this file.
+ enum []*EnumDescriptor // All the enums defined in this file.
+ ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
}
// The package name we'll use in the generated code to refer to this file.
@@ -204,6 +228,20 @@
return proto.GetString(d.Package)
}
+// Whether the proto library needs importing.
+// This will be true if there are any enums or any extensions, or any messages with extension ranges.
+func (d *FileDescriptor) needProtoImport() bool {
+ if len(d.enum) > 0 || len(d.ext) > 0 {
+ return true
+ }
+ for _, desc := range d.desc {
+ if len(desc.ext) > 0 || len(desc.ExtensionRange) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
// Simplify some things by abstracting the abilities shared by enums and messages.
type Object interface {
packageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
@@ -308,15 +346,12 @@
break
}
// Install it.
- if pkg != truePkg {
- log.Stderr("renaming duplicate imported package named", truePkg, "to", pkg)
- }
inUse[pkg] = true
uniquePackageName[f.FileDescriptorProto] = strings.Map(DotToUnderscore, pkg)
}
}
-// Walk the incoming data, wrapping DescriptorProtos and EnumDescriptorProtos
+// Walk the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos and FileDescriptorProtos
// into file-referenced objects within the Generator. Also create the list of files
// to generate
func (g *Generator) WrapTypes() {
@@ -330,10 +365,12 @@
descs := WrapDescriptors(f)
g.BuildNestedDescriptors(descs)
enums := WrapEnumDescriptors(f, descs)
+ exts := WrapExtensions(f)
g.allFiles[i] = &FileDescriptor{
FileDescriptorProto: f,
desc: descs,
enum: enums,
+ ext: exts,
}
}
@@ -373,13 +410,20 @@
// Construct the Descriptor and add it to the slice
func AddDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
+ d := &Descriptor{common{file: file}, desc, parent, nil, nil, nil}
+
+ d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
+ for i, field := range desc.Extension {
+ d.ext[i] = &ExtensionDescriptor{common{file: file}, field, d}
+ }
+
if len(sl) == cap(sl) {
nsl := make([]*Descriptor, len(sl), 2*len(sl))
copy(nsl, sl)
sl = nsl
}
sl = sl[0 : len(sl)+1]
- sl[len(sl)-1] = &Descriptor{common{file: file}, desc, parent, nil, nil}
+ sl[len(sl)-1] = d
return sl
}
@@ -437,6 +481,15 @@
return sl
}
+// Return a slice of all the top-level ExtensionDescriptors defined within this file.
+func WrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
+ sl := make([]*ExtensionDescriptor, len(file.Extension))
+ for i, field := range file.Extension {
+ sl[i] = &ExtensionDescriptor{common{file: file}, field, nil}
+ }
+ return sl
+}
+
// Build the map from fully qualified type names to objects. The key for the map
// comes from the input data, which puts a period at the beginning.
func (g *Generator) BuildTypeNameMap() {
@@ -525,6 +578,9 @@
for _, desc := range g.file.desc {
g.GenerateMessage(desc)
}
+ for _, ext := range g.file.ext {
+ g.GenerateExtension(ext)
+ }
g.GenerateInitFunction()
}
@@ -539,7 +595,7 @@
// Generate the header, including package definition and imports
func (g *Generator) GenerateImports() {
- if len(g.file.enum) > 0 {
+ if g.file.needProtoImport() {
g.p(`import "goprotobuf.googlecode.com/hg/proto"`)
}
for _, s := range g.file.Dependency {
@@ -763,6 +819,9 @@
tag := g.GoTag(field, wiretype)
g.p(fieldname, "\t", typename, "\t", tag)
}
+ if len(message.ExtensionRange) > 0 {
+ g.p("XXX_extensions\t\tmap[int32][]byte")
+ }
g.p("XXX_unrecognized\t[]byte")
g.out()
g.p("}")
@@ -779,6 +838,34 @@
g.out()
g.p("}")
+ // Extension support methods
+ if len(message.ExtensionRange) > 0 {
+ g.p()
+ g.p("var extRange_", ccTypeName, " = []proto.ExtensionRange{")
+ g.in()
+ for _, r := range message.ExtensionRange {
+ end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
+ g.p("proto.ExtensionRange{", r.Start, ", ", end, "},")
+ }
+ g.out()
+ g.p("}")
+ g.p("func (*", ccTypeName, ") ExtensionRangeArray() []proto.ExtensionRange {")
+ g.in()
+ g.p("return extRange_", ccTypeName)
+ g.out()
+ g.p("}")
+ g.p("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
+ g.in()
+ g.p("if this.XXX_extensions == nil {")
+ g.in()
+ g.p("this.XXX_extensions = make(map[int32][]byte)")
+ g.out()
+ g.p("}")
+ g.p("return this.XXX_extensions")
+ g.out()
+ g.p("}")
+ }
+
// Default constants
for _, field := range message.Field {
def := proto.GetString(field.DefaultValue)
@@ -798,7 +885,7 @@
case typename == "[]byte":
def = "[]byte(" + Quote(def) + ")"
kind = "var "
- case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
+ case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
// Must be an enum. Need to construct the prefixed name.
obj := g.objectNamed(proto.GetString(field.TypeName))
enum, ok := obj.(*EnumDescriptor)
@@ -811,6 +898,37 @@
g.p(kind, fieldname, " ", typename, " = ", def)
}
g.p()
+
+ for _, ext := range message.ext {
+ g.GenerateExtension(ext)
+ }
+}
+
+// Generate the extension descriptor for this ExtensionDescriptor.
+func (g *Generator) GenerateExtension(ext *ExtensionDescriptor) {
+ // The full type name
+ typeName := ext.typeName()
+ // Each scope of the extension is individually CamelCased, and all are joined with "_".
+ for i, s := range typeName {
+ typeName[i] = CamelCase(s)
+ }
+ ccTypeName := strings.Join(typeName, "_")
+
+ extendedType := "*" + g.TypeName(g.objectNamed(*ext.Extendee))
+ field := ext.FieldDescriptorProto
+ fieldType, wireType := g.GoType(ext.parent, field)
+ tag := g.GoTag(field, wireType)
+
+ g.p("var ", ccTypeName, " = &proto.ExtensionDesc{")
+ g.in()
+ g.p("ExtendedType: (", extendedType, ")(nil),")
+ g.p("ExtensionType: (", fieldType, ")(nil),")
+ g.p("Field: ", field.Number, ",")
+ g.p("Tag: ", tag, ",")
+
+ g.out()
+ g.p("}")
+ g.p()
}
func (g *Generator) GenerateInitFunction() {
diff --git a/compiler/testdata/Makefile b/compiler/testdata/Makefile
index 0cc9ad1..3eb2efb 100644
--- a/compiler/testdata/Makefile
+++ b/compiler/testdata/Makefile
@@ -37,9 +37,10 @@
include $(GOROOT)/src/Make.common
include $(GOROOT)/src/pkg/goprotobuf.googlecode.com/hg/Make.protobuf
-CLEANFILES+=*.pb.go
+CLEANFILES+=*.pb.go extension_test
-test: golden testbuild
+test: golden testbuild extension_test
+ ./extension_test
@echo PASS
golden:
@@ -51,6 +52,9 @@
testbuild: main.$O
$(LD) -L. main.$O
+extension_test: extension_test.$O
+ $(LD) -L. -o $@ $<
+
multi.a: multi3.pb.$O multi2.pb.$O multi1.pb.$O
rm -f multi.a
$(QUOTED_GOBIN)/gopack grc $@ $<
@@ -61,3 +65,4 @@
test.pb.go: imp.pb.go
multi1.pb.go: multi2.pb.go multi3.pb.go
main.$O: imp.pb.$O test.pb.$O multi.a
+extension_test.$O: extension_base.pb.$O extension_user.pb.$O
diff --git a/compiler/testdata/extension_base.proto b/compiler/testdata/extension_base.proto
new file mode 100644
index 0000000..5f91628
--- /dev/null
+++ b/compiler/testdata/extension_base.proto
@@ -0,0 +1,38 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 Google Inc. All rights reserved.
+// http://code.google.com/p/goprotobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package extension_base;
+
+message BaseMessage {
+ optional int32 height = 1;
+ extensions 4 to 9;
+ extensions 16 to max;
+}
diff --git a/compiler/testdata/extension_test.go b/compiler/testdata/extension_test.go
new file mode 100644
index 0000000..1ddb229
--- /dev/null
+++ b/compiler/testdata/extension_test.go
@@ -0,0 +1,159 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 Google Inc. All rights reserved.
+// http://code.google.com/p/goprotobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test that we can use protocol buffers that use extensions.
+
+package main
+
+import (
+ "testing"
+
+ "goprotobuf.googlecode.com/hg/proto"
+ base "extension_base.pb"
+ user "extension_user.pb"
+)
+
+func TestSingleFieldExtension(t *testing.T) {
+ bm := base.NewBaseMessage()
+ bm.Height = proto.Int32(178)
+
+ // Use extension within scope of another type.
+ vol := proto.Uint32(11)
+ t.Logf("bm: %T, user.LoudMessage_Volume: %T", bm, user.LoudMessage_Volume)
+ err := proto.SetExtension(bm, user.LoudMessage_Volume, vol)
+ if err != nil {
+ t.Fatal("Failed setting extension:", err)
+ }
+ buf, err := proto.Marshal(bm)
+ if err != nil {
+ t.Fatal("Failed encoding message with extension:", err)
+ }
+ bm_new := base.NewBaseMessage()
+ if err := proto.Unmarshal(buf, bm_new); err != nil {
+ t.Fatal("Failed decoding message with extension:", err)
+ }
+ if !proto.HasExtension(bm_new, user.LoudMessage_Volume) {
+ t.Fatal("Decoded message didn't contain extension.")
+ }
+ vol_out, err := proto.GetExtension(bm_new, user.LoudMessage_Volume)
+ if err != nil {
+ t.Fatal("Failed getting extension:", err)
+ }
+ if v := vol_out.(*uint32); *v != *vol {
+ t.Errorf("vol_out = %v, expected %v", *v, *vol)
+ }
+ proto.ClearExtension(bm_new, user.LoudMessage_Volume)
+ if proto.HasExtension(bm_new, user.LoudMessage_Volume) {
+ t.Fatal("Failed clearing extension.")
+ }
+}
+
+func TestMessageExtension(t *testing.T) {
+ bm := base.NewBaseMessage()
+ bm.Height = proto.Int32(179)
+
+ // Use extension that is itself a message.
+ um := &user.UserMessage{
+ Name: proto.String("Dave"),
+ Rank: proto.String("Major"),
+ }
+ err := proto.SetExtension(bm, user.LoginMessage_UserMessage, um)
+ if err != nil {
+ t.Fatal("Failed setting extension:", err)
+ }
+ buf, err := proto.Marshal(bm)
+ if err != nil {
+ t.Fatal("Failed encoding message with extension:", err)
+ }
+ bm_new := base.NewBaseMessage()
+ if err := proto.Unmarshal(buf, bm_new); err != nil {
+ t.Fatal("Failed decoding message with extension:", err)
+ }
+ if !proto.HasExtension(bm_new, user.LoginMessage_UserMessage) {
+ t.Fatal("Decoded message didn't contain extension.")
+ }
+ um_out, err := proto.GetExtension(bm_new, user.LoginMessage_UserMessage)
+ if err != nil {
+ t.Fatal("Failed getting extension:", err)
+ }
+ if n := um_out.(*user.UserMessage).Name; *n != *um.Name {
+ t.Errorf("um_out.Name = %q, expected %q", *n, *um.Name)
+ }
+ if r := um_out.(*user.UserMessage).Rank; *r != *um.Rank {
+ t.Errorf("um_out.Rank = %q, expected %q", *r, *um.Rank)
+ }
+ proto.ClearExtension(bm_new, user.LoginMessage_UserMessage)
+ if proto.HasExtension(bm_new, user.LoginMessage_UserMessage) {
+ t.Fatal("Failed clearing extension.")
+ }
+}
+
+func TestTopLevelExtension(t *testing.T) {
+ bm := base.NewBaseMessage()
+ bm.Height = proto.Int32(179)
+
+ width := proto.Int32(17)
+ err := proto.SetExtension(bm, user.E_Width, width)
+ if err != nil {
+ t.Fatal("Failed setting extension:", err)
+ }
+ buf, err := proto.Marshal(bm)
+ if err != nil {
+ t.Fatal("Failed encoding message with extension:", err)
+ }
+ bm_new := base.NewBaseMessage()
+ if err := proto.Unmarshal(buf, bm_new); err != nil {
+ t.Fatal("Failed decoding message with extension:", err)
+ }
+ if !proto.HasExtension(bm_new, user.E_Width) {
+ t.Fatal("Decoded message didn't contain extension.")
+ }
+ width_out, err := proto.GetExtension(bm_new, user.E_Width)
+ if err != nil {
+ t.Fatal("Failed getting extension:", err)
+ }
+ if w := width_out.(*int32); *w != *width {
+ t.Errorf("width_out = %v, expected %v", *w, *width)
+ }
+ proto.ClearExtension(bm_new, user.E_Width)
+ if proto.HasExtension(bm_new, user.E_Width) {
+ t.Fatal("Failed clearing extension.")
+ }
+}
+
+func main() {
+ // simpler than rigging up gotest
+ testing.Main([]testing.Test{
+ testing.Test{"TestSingleFieldExtension", TestSingleFieldExtension},
+ testing.Test{"TestMessageExtension", TestMessageExtension},
+ testing.Test{"TestTopLevelExtension", TestTopLevelExtension},
+ })
+}
diff --git a/compiler/testdata/extension_user.proto b/compiler/testdata/extension_user.proto
new file mode 100644
index 0000000..6fb3d72
--- /dev/null
+++ b/compiler/testdata/extension_user.proto
@@ -0,0 +1,73 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 Google Inc. All rights reserved.
+// http://code.google.com/p/goprotobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import "extension_base.proto";
+
+package extension_user;
+
+message UserMessage {
+ optional string name = 1;
+ optional string rank = 2;
+}
+
+// Extend with a message
+extend extension_base.BaseMessage {
+ optional UserMessage user_message = 5;
+}
+
+// Extend with some primitive types
+extend extension_base.BaseMessage {
+ optional int32 width = 6;
+ optional int64 area = 7;
+}
+
+// Extend inside the scope of another type
+message LoudMessage {
+ extend extension_base.BaseMessage {
+ optional uint32 volume = 8;
+ }
+ extensions 100 to max;
+}
+
+// Extend inside the scope of another type, using a message.
+message LoginMessage {
+ extend extension_base.BaseMessage {
+ required UserMessage user_message = 16;
+ }
+}
+
+// An extension of an extension
+message Announcement {
+ optional string words = 1;
+ extend LoudMessage {
+ optional Announcement loud_ext = 100;
+ }
+}
diff --git a/compiler/testdata/test.pb.go.golden b/compiler/testdata/test.pb.go.golden
index 0d7a7cb..6807d20 100644
--- a/compiler/testdata/test.pb.go.golden
+++ b/compiler/testdata/test.pb.go.golden
@@ -84,6 +84,7 @@
type Reply struct {
Found []*Reply_Entry "PB(bytes,1,rep,name=found)"
+ XXX_extensions map[int32][]byte
XXX_unrecognized []byte
}
func (this *Reply) Reset() {
@@ -93,6 +94,19 @@
return new(Reply)
}
+var extRange_Reply = []proto.ExtensionRange{
+ proto.ExtensionRange{100, 536870911},
+}
+func (*Reply) ExtensionRangeArray() []proto.ExtensionRange {
+ return extRange_Reply
+}
+func (this *Reply) ExtensionMap() map[int32][]byte {
+ if this.XXX_extensions == nil {
+ this.XXX_extensions = make(map[int32][]byte)
+ }
+ return this.XXX_extensions
+}
+
type Reply_Entry struct {
KeyThatNeeds_1234camel_CasIng *int64 "PB(varint,1,req,name=key_that_needs_1234camel_CasIng)"
Value *int64 "PB(varint,2,opt,name=value,def=7)"
@@ -117,6 +131,20 @@
return new(ReplyExtensions)
}
+var ReplyExtensions_Time = &proto.ExtensionDesc{
+ ExtendedType: (*Reply)(nil),
+ ExtensionType: (*float64)(nil),
+ Field: 101,
+ Tag: "PB(fixed64,101,opt,name=time)",
+}
+
+var E_Tag = &proto.ExtensionDesc{
+ ExtendedType: (*Reply)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 103,
+ Tag: "PB(bytes,103,opt,name=tag)",
+}
+
func init() {
proto.RegisterEnum("my_test.HatType", HatType_name, HatType_value)
proto.RegisterEnum("my_test.Days", Days_name, Days_value)
diff --git a/compiler/testdata/test.proto b/compiler/testdata/test.proto
index f7e7a3d..ef91b03 100644
--- a/compiler/testdata/test.proto
+++ b/compiler/testdata/test.proto
@@ -73,3 +73,8 @@
optional double time = 101;
}
}
+
+// top-level extension
+extend Reply {
+ optional string tag = 103;
+}
diff --git a/proto/extensions.go b/proto/extensions.go
index 480e8d3..3726d3c 100644
--- a/proto/extensions.go
+++ b/proto/extensions.go
@@ -128,7 +128,7 @@
return unsafe.Unreflect(t, base), nil
}
-// TODO(: (needed for repeated extensions)
+// TODO: (needed for repeated extensions)
// - ExtensionSize
// - AddExtension