Add extensions to the generator.

R=r
CC=golang-dev
http://codereview.appspot.com/774041
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() {