Просмотр исходного кода

Add support for default values in proto extensions.

David Symonds 10 лет назад
Родитель
Сommit
c8ba1155c8
5 измененных файлов с 672 добавлено и 95 удалено
  1. 40 2
      proto/extensions.go
  2. 139 0
      proto/extensions_test.go
  3. 99 93
      proto/lib.go
  4. 349 0
      proto/testdata/test.pb.go
  5. 45 0
      proto/testdata/test.proto

+ 40 - 2
proto/extensions.go

@@ -222,7 +222,7 @@ func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
 }
 
 // GetExtension parses and returns the given extension of pb.
-// If the extension is not present it returns ErrMissingExtension.
+// If the extension is not present and has no default value it returns ErrMissingExtension.
 func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) {
 	if err := checkExtensionTypes(pb, extension); err != nil {
 		return nil, err
@@ -231,8 +231,11 @@ func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, er
 	emap := pb.ExtensionMap()
 	e, ok := emap[extension.Field]
 	if !ok {
-		return nil, ErrMissingExtension
+		// defaultExtensionValue returns the default value or
+		// ErrMissingExtension if there is no default.
+		return defaultExtensionValue(extension)
 	}
+
 	if e.value != nil {
 		// Already decoded. Check the descriptor, though.
 		if e.desc != extension {
@@ -258,6 +261,41 @@ func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, er
 	return e.value, nil
 }
 
+// defaultExtensionValue returns the default value for extension.
+// If no default for an extension is defined ErrMissingExtension is returned.
+func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
+	t := reflect.TypeOf(extension.ExtensionType)
+	props := extensionProperties(extension)
+
+	sf, _, err := fieldDefault(t, props)
+	if err != nil {
+		return nil, err
+	}
+
+	if sf == nil || sf.value == nil {
+		// There is no default value.
+		return nil, ErrMissingExtension
+	}
+
+	if t.Kind() != reflect.Ptr {
+		// We do not need to return a Ptr, we can directly return sf.value.
+		return sf.value, nil
+	}
+
+	// We need to return an interface{} that is a pointer to sf.value.
+	value := reflect.New(t).Elem()
+	value.Set(reflect.New(value.Type().Elem()))
+	if sf.kind == reflect.Int32 {
+		// We may have an int32 or an enum, but the underlying data is int32.
+		// Since we can't set an int32 into a non int32 reflect.value directly
+		// set it as a int32.
+		value.Elem().SetInt(int64(sf.value.(int32)))
+	} else {
+		value.Elem().Set(reflect.ValueOf(sf.value))
+	}
+	return value.Interface(), nil
+}
+
 // decodeExtension decodes an extension encoded in b.
 func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
 	o := NewBuffer(b)

+ 139 - 0
proto/extensions_test.go

@@ -32,6 +32,8 @@
 package proto_test
 
 import (
+	"fmt"
+	"reflect"
 	"testing"
 
 	"github.com/golang/protobuf/proto"
@@ -93,6 +95,143 @@ func TestGetExtensionStability(t *testing.T) {
 	}
 }
 
+func TestGetExtensionDefaults(t *testing.T) {
+	var setFloat64 float64 = 1
+	var setFloat32 float32 = 2
+	var setInt32 int32 = 3
+	var setInt64 int64 = 4
+	var setUint32 uint32 = 5
+	var setUint64 uint64 = 6
+	var setBool = true
+	var setBool2 = false
+	var setString = "Goodnight string"
+	var setBytes = []byte("Goodnight bytes")
+	var setEnum = pb.DefaultsMessage_TWO
+
+	type testcase struct {
+		ext  *proto.ExtensionDesc // Extension we are testing.
+		want interface{}          // Expected value of extension, or nil (meaning that GetExtension will fail).
+		def  interface{}          // Expected value of extension after ClearExtension().
+	}
+	tests := []testcase{
+		{pb.E_NoDefaultDouble, setFloat64, nil},
+		{pb.E_NoDefaultFloat, setFloat32, nil},
+		{pb.E_NoDefaultInt32, setInt32, nil},
+		{pb.E_NoDefaultInt64, setInt64, nil},
+		{pb.E_NoDefaultUint32, setUint32, nil},
+		{pb.E_NoDefaultUint64, setUint64, nil},
+		{pb.E_NoDefaultSint32, setInt32, nil},
+		{pb.E_NoDefaultSint64, setInt64, nil},
+		{pb.E_NoDefaultFixed32, setUint32, nil},
+		{pb.E_NoDefaultFixed64, setUint64, nil},
+		{pb.E_NoDefaultSfixed32, setInt32, nil},
+		{pb.E_NoDefaultSfixed64, setInt64, nil},
+		{pb.E_NoDefaultBool, setBool, nil},
+		{pb.E_NoDefaultBool, setBool2, nil},
+		{pb.E_NoDefaultString, setString, nil},
+		{pb.E_NoDefaultBytes, setBytes, nil},
+		{pb.E_NoDefaultEnum, setEnum, nil},
+		{pb.E_DefaultDouble, setFloat64, float64(3.1415)},
+		{pb.E_DefaultFloat, setFloat32, float32(3.14)},
+		{pb.E_DefaultInt32, setInt32, int32(42)},
+		{pb.E_DefaultInt64, setInt64, int64(43)},
+		{pb.E_DefaultUint32, setUint32, uint32(44)},
+		{pb.E_DefaultUint64, setUint64, uint64(45)},
+		{pb.E_DefaultSint32, setInt32, int32(46)},
+		{pb.E_DefaultSint64, setInt64, int64(47)},
+		{pb.E_DefaultFixed32, setUint32, uint32(48)},
+		{pb.E_DefaultFixed64, setUint64, uint64(49)},
+		{pb.E_DefaultSfixed32, setInt32, int32(50)},
+		{pb.E_DefaultSfixed64, setInt64, int64(51)},
+		{pb.E_DefaultBool, setBool, true},
+		{pb.E_DefaultBool, setBool2, true},
+		{pb.E_DefaultString, setString, "Hello, string"},
+		{pb.E_DefaultBytes, setBytes, []byte("Hello, bytes")},
+		{pb.E_DefaultEnum, setEnum, pb.DefaultsMessage_ONE},
+	}
+
+	checkVal := func(test testcase, msg *pb.DefaultsMessage, valWant interface{}) error {
+		val, err := proto.GetExtension(msg, test.ext)
+		if err != nil {
+			if valWant != nil {
+				return fmt.Errorf("GetExtension(): %s", err)
+			}
+			if want := proto.ErrMissingExtension; err != want {
+				return fmt.Errorf("Unexpected error: got %v, want %v", err, want)
+			}
+			return nil
+		}
+
+		// All proto2 extension values are either a pointer to a value or a slice of values.
+		ty := reflect.TypeOf(val)
+		tyWant := reflect.TypeOf(test.ext.ExtensionType)
+		if got, want := ty, tyWant; got != want {
+			return fmt.Errorf("unexpected reflect.TypeOf(): got %v want %v", got, want)
+		}
+		tye := ty.Elem()
+		tyeWant := tyWant.Elem()
+		if got, want := tye, tyeWant; got != want {
+			return fmt.Errorf("unexpected reflect.TypeOf().Elem(): got %v want %v", got, want)
+		}
+
+		// Check the name of the type of the value.
+		// If it is an enum it will be type int32 with the name of the enum.
+		if got, want := tye.Name(), tye.Name(); got != want {
+			return fmt.Errorf("unexpected reflect.TypeOf().Elem().Name(): got %v want %v", got, want)
+		}
+
+		// Check that value is what we expect.
+		// If we have a pointer in val, get the value it points to.
+		valExp := val
+		if ty.Kind() == reflect.Ptr {
+			valExp = reflect.ValueOf(val).Elem().Interface()
+		}
+		if got, want := valExp, valWant; !reflect.DeepEqual(got, want) {
+			return fmt.Errorf("unexpected reflect.DeepEqual(): got %v want %v", got, want)
+		}
+
+		return nil
+	}
+
+	setTo := func(test testcase) interface{} {
+		setTo := reflect.ValueOf(test.want)
+		if typ := reflect.TypeOf(test.ext.ExtensionType); typ.Kind() == reflect.Ptr {
+			setTo = reflect.New(typ).Elem()
+			setTo.Set(reflect.New(setTo.Type().Elem()))
+			setTo.Elem().Set(reflect.ValueOf(test.want))
+		}
+		return setTo.Interface()
+	}
+
+	for _, test := range tests {
+		msg := &pb.DefaultsMessage{}
+		name := test.ext.Name
+
+		// Check the initial value.
+		if err := checkVal(test, msg, test.def); err != nil {
+			t.Errorf("%s: %v", name, err)
+		}
+
+		// Set the per-type value and check value.
+		name = fmt.Sprintf("%s (set to %T %v)", name, test.want, test.want)
+		if err := proto.SetExtension(msg, test.ext, setTo(test)); err != nil {
+			t.Errorf("%s: SetExtension(): %v", name, err)
+			continue
+		}
+		if err := checkVal(test, msg, test.want); err != nil {
+			t.Errorf("%s: %v", name, err)
+			continue
+		}
+
+		// Set and check the value.
+		name += " (cleared)"
+		proto.ClearExtension(msg, test.ext)
+		if err := checkVal(test, msg, test.def); err != nil {
+			t.Errorf("%s: %v", name, err)
+		}
+	}
+}
+
 func TestExtensionsRoundTrip(t *testing.T) {
 	msg := &pb.MyMessage{}
 	ext1 := &pb.Ext{

+ 99 - 93
proto/lib.go

@@ -668,112 +668,118 @@ func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
 		}
 		ft := t.Field(fi).Type
 
-		var canHaveDefault, nestedMessage bool
-		switch ft.Kind() {
-		case reflect.Ptr:
-			if ft.Elem().Kind() == reflect.Struct {
-				nestedMessage = true
-			} else {
-				canHaveDefault = true // proto2 scalar field
-			}
+		sf, nested, err := fieldDefault(ft, prop)
+		switch {
+		case err != nil:
+			log.Print(err)
+		case nested:
+			dm.nested = append(dm.nested, fi)
+		case sf != nil:
+			sf.index = fi
+			dm.scalars = append(dm.scalars, *sf)
+		}
+	}
 
-		case reflect.Slice:
-			switch ft.Elem().Kind() {
-			case reflect.Ptr:
-				nestedMessage = true // repeated message
-			case reflect.Uint8:
-				canHaveDefault = true // bytes field
-			}
+	return dm
+}
 
-		case reflect.Map:
-			if ft.Elem().Kind() == reflect.Ptr {
-				nestedMessage = true // map with message values
-			}
+// fieldDefault returns the scalarField for field type ft.
+// sf will be nil if the field can not have a default.
+// nestedMessage will be true if this is a nested message.
+// Note that sf.index is not set on return.
+func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
+	var canHaveDefault bool
+	switch ft.Kind() {
+	case reflect.Ptr:
+		if ft.Elem().Kind() == reflect.Struct {
+			nestedMessage = true
+		} else {
+			canHaveDefault = true // proto2 scalar field
 		}
 
-		if !canHaveDefault {
-			if nestedMessage {
-				dm.nested = append(dm.nested, fi)
-			}
-			continue
+	case reflect.Slice:
+		switch ft.Elem().Kind() {
+		case reflect.Ptr:
+			nestedMessage = true // repeated message
+		case reflect.Uint8:
+			canHaveDefault = true // bytes field
 		}
 
-		sf := scalarField{
-			index: fi,
-			kind:  ft.Elem().Kind(),
+	case reflect.Map:
+		if ft.Elem().Kind() == reflect.Ptr {
+			nestedMessage = true // map with message values
 		}
+	}
 
-		// scalar fields without defaults
-		if !prop.HasDefault {
-			dm.scalars = append(dm.scalars, sf)
-			continue
+	if !canHaveDefault {
+		if nestedMessage {
+			return nil, true, nil
 		}
+		return nil, false, nil
+	}
 
-		// a scalar field: either *T or []byte
-		switch ft.Elem().Kind() {
-		case reflect.Bool:
-			x, err := strconv.ParseBool(prop.Default)
-			if err != nil {
-				log.Printf("proto: bad default bool %q: %v", prop.Default, err)
-				continue
-			}
-			sf.value = x
-		case reflect.Float32:
-			x, err := strconv.ParseFloat(prop.Default, 32)
-			if err != nil {
-				log.Printf("proto: bad default float32 %q: %v", prop.Default, err)
-				continue
-			}
-			sf.value = float32(x)
-		case reflect.Float64:
-			x, err := strconv.ParseFloat(prop.Default, 64)
-			if err != nil {
-				log.Printf("proto: bad default float64 %q: %v", prop.Default, err)
-				continue
-			}
-			sf.value = x
-		case reflect.Int32:
-			x, err := strconv.ParseInt(prop.Default, 10, 32)
-			if err != nil {
-				log.Printf("proto: bad default int32 %q: %v", prop.Default, err)
-				continue
-			}
-			sf.value = int32(x)
-		case reflect.Int64:
-			x, err := strconv.ParseInt(prop.Default, 10, 64)
-			if err != nil {
-				log.Printf("proto: bad default int64 %q: %v", prop.Default, err)
-				continue
-			}
-			sf.value = x
-		case reflect.String:
-			sf.value = prop.Default
-		case reflect.Uint8:
-			// []byte (not *uint8)
-			sf.value = []byte(prop.Default)
-		case reflect.Uint32:
-			x, err := strconv.ParseUint(prop.Default, 10, 32)
-			if err != nil {
-				log.Printf("proto: bad default uint32 %q: %v", prop.Default, err)
-				continue
-			}
-			sf.value = uint32(x)
-		case reflect.Uint64:
-			x, err := strconv.ParseUint(prop.Default, 10, 64)
-			if err != nil {
-				log.Printf("proto: bad default uint64 %q: %v", prop.Default, err)
-				continue
-			}
-			sf.value = x
-		default:
-			log.Printf("proto: unhandled def kind %v", ft.Elem().Kind())
-			continue
-		}
+	// We now know that ft is a pointer or slice.
+	sf = &scalarField{kind: ft.Elem().Kind()}
 
-		dm.scalars = append(dm.scalars, sf)
+	// scalar fields without defaults
+	if !prop.HasDefault {
+		return sf, false, nil
 	}
 
-	return dm
+	// a scalar field: either *T or []byte
+	switch ft.Elem().Kind() {
+	case reflect.Bool:
+		x, err := strconv.ParseBool(prop.Default)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
+		}
+		sf.value = x
+	case reflect.Float32:
+		x, err := strconv.ParseFloat(prop.Default, 32)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
+		}
+		sf.value = float32(x)
+	case reflect.Float64:
+		x, err := strconv.ParseFloat(prop.Default, 64)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
+		}
+		sf.value = x
+	case reflect.Int32:
+		x, err := strconv.ParseInt(prop.Default, 10, 32)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
+		}
+		sf.value = int32(x)
+	case reflect.Int64:
+		x, err := strconv.ParseInt(prop.Default, 10, 64)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
+		}
+		sf.value = x
+	case reflect.String:
+		sf.value = prop.Default
+	case reflect.Uint8:
+		// []byte (not *uint8)
+		sf.value = []byte(prop.Default)
+	case reflect.Uint32:
+		x, err := strconv.ParseUint(prop.Default, 10, 32)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
+		}
+		sf.value = uint32(x)
+	case reflect.Uint64:
+		x, err := strconv.ParseUint(prop.Default, 10, 64)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
+		}
+		sf.value = x
+	default:
+		return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
+	}
+
+	return sf, false, nil
 }
 
 // Map fields may have key types of non-float scalars, strings and enums.

+ 349 - 0
proto/testdata/test.pb.go

@@ -22,6 +22,7 @@ It has these top-level messages:
 	OtherMessage
 	MyMessage
 	Ext
+	DefaultsMessage
 	MyMessageSet
 	Empty
 	MessageList
@@ -181,6 +182,42 @@ func (x *MyMessage_Color) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
+type DefaultsMessage_DefaultsEnum int32
+
+const (
+	DefaultsMessage_ZERO DefaultsMessage_DefaultsEnum = 0
+	DefaultsMessage_ONE  DefaultsMessage_DefaultsEnum = 1
+	DefaultsMessage_TWO  DefaultsMessage_DefaultsEnum = 2
+)
+
+var DefaultsMessage_DefaultsEnum_name = map[int32]string{
+	0: "ZERO",
+	1: "ONE",
+	2: "TWO",
+}
+var DefaultsMessage_DefaultsEnum_value = map[string]int32{
+	"ZERO": 0,
+	"ONE":  1,
+	"TWO":  2,
+}
+
+func (x DefaultsMessage_DefaultsEnum) Enum() *DefaultsMessage_DefaultsEnum {
+	p := new(DefaultsMessage_DefaultsEnum)
+	*p = x
+	return p
+}
+func (x DefaultsMessage_DefaultsEnum) String() string {
+	return proto.EnumName(DefaultsMessage_DefaultsEnum_name, int32(x))
+}
+func (x *DefaultsMessage_DefaultsEnum) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(DefaultsMessage_DefaultsEnum_value, data, "DefaultsMessage_DefaultsEnum")
+	if err != nil {
+		return err
+	}
+	*x = DefaultsMessage_DefaultsEnum(value)
+	return nil
+}
+
 type Defaults_Color int32
 
 const (
@@ -1402,6 +1439,29 @@ var E_Ext_Number = &proto.ExtensionDesc{
 	Tag:           "varint,105,opt,name=number",
 }
 
+type DefaultsMessage struct {
+	XXX_extensions   map[int32]proto.Extension `json:"-"`
+	XXX_unrecognized []byte                    `json:"-"`
+}
+
+func (m *DefaultsMessage) Reset()         { *m = DefaultsMessage{} }
+func (m *DefaultsMessage) String() string { return proto.CompactTextString(m) }
+func (*DefaultsMessage) ProtoMessage()    {}
+
+var extRange_DefaultsMessage = []proto.ExtensionRange{
+	{100, 536870911},
+}
+
+func (*DefaultsMessage) ExtensionRangeArray() []proto.ExtensionRange {
+	return extRange_DefaultsMessage
+}
+func (m *DefaultsMessage) ExtensionMap() map[int32]proto.Extension {
+	if m.XXX_extensions == nil {
+		m.XXX_extensions = make(map[int32]proto.Extension)
+	}
+	return m.XXX_extensions
+}
+
 type MyMessageSet struct {
 	XXX_extensions   map[int32]proto.Extension `json:"-"`
 	XXX_unrecognized []byte                    `json:"-"`
@@ -1934,6 +1994,262 @@ var E_Greeting = &proto.ExtensionDesc{
 	Tag:           "bytes,106,rep,name=greeting",
 }
 
+var E_NoDefaultDouble = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*float64)(nil),
+	Field:         101,
+	Name:          "testdata.no_default_double",
+	Tag:           "fixed64,101,opt,name=no_default_double",
+}
+
+var E_NoDefaultFloat = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*float32)(nil),
+	Field:         102,
+	Name:          "testdata.no_default_float",
+	Tag:           "fixed32,102,opt,name=no_default_float",
+}
+
+var E_NoDefaultInt32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int32)(nil),
+	Field:         103,
+	Name:          "testdata.no_default_int32",
+	Tag:           "varint,103,opt,name=no_default_int32",
+}
+
+var E_NoDefaultInt64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int64)(nil),
+	Field:         104,
+	Name:          "testdata.no_default_int64",
+	Tag:           "varint,104,opt,name=no_default_int64",
+}
+
+var E_NoDefaultUint32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*uint32)(nil),
+	Field:         105,
+	Name:          "testdata.no_default_uint32",
+	Tag:           "varint,105,opt,name=no_default_uint32",
+}
+
+var E_NoDefaultUint64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*uint64)(nil),
+	Field:         106,
+	Name:          "testdata.no_default_uint64",
+	Tag:           "varint,106,opt,name=no_default_uint64",
+}
+
+var E_NoDefaultSint32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int32)(nil),
+	Field:         107,
+	Name:          "testdata.no_default_sint32",
+	Tag:           "zigzag32,107,opt,name=no_default_sint32",
+}
+
+var E_NoDefaultSint64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int64)(nil),
+	Field:         108,
+	Name:          "testdata.no_default_sint64",
+	Tag:           "zigzag64,108,opt,name=no_default_sint64",
+}
+
+var E_NoDefaultFixed32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*uint32)(nil),
+	Field:         109,
+	Name:          "testdata.no_default_fixed32",
+	Tag:           "fixed32,109,opt,name=no_default_fixed32",
+}
+
+var E_NoDefaultFixed64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*uint64)(nil),
+	Field:         110,
+	Name:          "testdata.no_default_fixed64",
+	Tag:           "fixed64,110,opt,name=no_default_fixed64",
+}
+
+var E_NoDefaultSfixed32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int32)(nil),
+	Field:         111,
+	Name:          "testdata.no_default_sfixed32",
+	Tag:           "fixed32,111,opt,name=no_default_sfixed32",
+}
+
+var E_NoDefaultSfixed64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int64)(nil),
+	Field:         112,
+	Name:          "testdata.no_default_sfixed64",
+	Tag:           "fixed64,112,opt,name=no_default_sfixed64",
+}
+
+var E_NoDefaultBool = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         113,
+	Name:          "testdata.no_default_bool",
+	Tag:           "varint,113,opt,name=no_default_bool",
+}
+
+var E_NoDefaultString = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         114,
+	Name:          "testdata.no_default_string",
+	Tag:           "bytes,114,opt,name=no_default_string",
+}
+
+var E_NoDefaultBytes = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: ([]byte)(nil),
+	Field:         115,
+	Name:          "testdata.no_default_bytes",
+	Tag:           "bytes,115,opt,name=no_default_bytes",
+}
+
+var E_NoDefaultEnum = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*DefaultsMessage_DefaultsEnum)(nil),
+	Field:         116,
+	Name:          "testdata.no_default_enum",
+	Tag:           "varint,116,opt,name=no_default_enum,enum=testdata.DefaultsMessage_DefaultsEnum",
+}
+
+var E_DefaultDouble = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*float64)(nil),
+	Field:         201,
+	Name:          "testdata.default_double",
+	Tag:           "fixed64,201,opt,name=default_double,def=3.1415",
+}
+
+var E_DefaultFloat = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*float32)(nil),
+	Field:         202,
+	Name:          "testdata.default_float",
+	Tag:           "fixed32,202,opt,name=default_float,def=3.14",
+}
+
+var E_DefaultInt32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int32)(nil),
+	Field:         203,
+	Name:          "testdata.default_int32",
+	Tag:           "varint,203,opt,name=default_int32,def=42",
+}
+
+var E_DefaultInt64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int64)(nil),
+	Field:         204,
+	Name:          "testdata.default_int64",
+	Tag:           "varint,204,opt,name=default_int64,def=43",
+}
+
+var E_DefaultUint32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*uint32)(nil),
+	Field:         205,
+	Name:          "testdata.default_uint32",
+	Tag:           "varint,205,opt,name=default_uint32,def=44",
+}
+
+var E_DefaultUint64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*uint64)(nil),
+	Field:         206,
+	Name:          "testdata.default_uint64",
+	Tag:           "varint,206,opt,name=default_uint64,def=45",
+}
+
+var E_DefaultSint32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int32)(nil),
+	Field:         207,
+	Name:          "testdata.default_sint32",
+	Tag:           "zigzag32,207,opt,name=default_sint32,def=46",
+}
+
+var E_DefaultSint64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int64)(nil),
+	Field:         208,
+	Name:          "testdata.default_sint64",
+	Tag:           "zigzag64,208,opt,name=default_sint64,def=47",
+}
+
+var E_DefaultFixed32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*uint32)(nil),
+	Field:         209,
+	Name:          "testdata.default_fixed32",
+	Tag:           "fixed32,209,opt,name=default_fixed32,def=48",
+}
+
+var E_DefaultFixed64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*uint64)(nil),
+	Field:         210,
+	Name:          "testdata.default_fixed64",
+	Tag:           "fixed64,210,opt,name=default_fixed64,def=49",
+}
+
+var E_DefaultSfixed32 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int32)(nil),
+	Field:         211,
+	Name:          "testdata.default_sfixed32",
+	Tag:           "fixed32,211,opt,name=default_sfixed32,def=50",
+}
+
+var E_DefaultSfixed64 = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*int64)(nil),
+	Field:         212,
+	Name:          "testdata.default_sfixed64",
+	Tag:           "fixed64,212,opt,name=default_sfixed64,def=51",
+}
+
+var E_DefaultBool = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         213,
+	Name:          "testdata.default_bool",
+	Tag:           "varint,213,opt,name=default_bool,def=1",
+}
+
+var E_DefaultString = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         214,
+	Name:          "testdata.default_string",
+	Tag:           "bytes,214,opt,name=default_string,def=Hello, string",
+}
+
+var E_DefaultBytes = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: ([]byte)(nil),
+	Field:         215,
+	Name:          "testdata.default_bytes",
+	Tag:           "bytes,215,opt,name=default_bytes,def=Hello, bytes",
+}
+
+var E_DefaultEnum = &proto.ExtensionDesc{
+	ExtendedType:  (*DefaultsMessage)(nil),
+	ExtensionType: (*DefaultsMessage_DefaultsEnum)(nil),
+	Field:         216,
+	Name:          "testdata.default_enum",
+	Tag:           "varint,216,opt,name=default_enum,enum=testdata.DefaultsMessage_DefaultsEnum,def=1",
+}
+
 var E_X201 = &proto.ExtensionDesc{
 	ExtendedType:  (*MyMessageSet)(nil),
 	ExtensionType: (*Empty)(nil),
@@ -2338,12 +2654,45 @@ func init() {
 	proto.RegisterEnum("testdata.FOO", FOO_name, FOO_value)
 	proto.RegisterEnum("testdata.GoTest_KIND", GoTest_KIND_name, GoTest_KIND_value)
 	proto.RegisterEnum("testdata.MyMessage_Color", MyMessage_Color_name, MyMessage_Color_value)
+	proto.RegisterEnum("testdata.DefaultsMessage_DefaultsEnum", DefaultsMessage_DefaultsEnum_name, DefaultsMessage_DefaultsEnum_value)
 	proto.RegisterEnum("testdata.Defaults_Color", Defaults_Color_name, Defaults_Color_value)
 	proto.RegisterEnum("testdata.RepeatedEnum_Color", RepeatedEnum_Color_name, RepeatedEnum_Color_value)
 	proto.RegisterExtension(E_Ext_More)
 	proto.RegisterExtension(E_Ext_Text)
 	proto.RegisterExtension(E_Ext_Number)
 	proto.RegisterExtension(E_Greeting)
+	proto.RegisterExtension(E_NoDefaultDouble)
+	proto.RegisterExtension(E_NoDefaultFloat)
+	proto.RegisterExtension(E_NoDefaultInt32)
+	proto.RegisterExtension(E_NoDefaultInt64)
+	proto.RegisterExtension(E_NoDefaultUint32)
+	proto.RegisterExtension(E_NoDefaultUint64)
+	proto.RegisterExtension(E_NoDefaultSint32)
+	proto.RegisterExtension(E_NoDefaultSint64)
+	proto.RegisterExtension(E_NoDefaultFixed32)
+	proto.RegisterExtension(E_NoDefaultFixed64)
+	proto.RegisterExtension(E_NoDefaultSfixed32)
+	proto.RegisterExtension(E_NoDefaultSfixed64)
+	proto.RegisterExtension(E_NoDefaultBool)
+	proto.RegisterExtension(E_NoDefaultString)
+	proto.RegisterExtension(E_NoDefaultBytes)
+	proto.RegisterExtension(E_NoDefaultEnum)
+	proto.RegisterExtension(E_DefaultDouble)
+	proto.RegisterExtension(E_DefaultFloat)
+	proto.RegisterExtension(E_DefaultInt32)
+	proto.RegisterExtension(E_DefaultInt64)
+	proto.RegisterExtension(E_DefaultUint32)
+	proto.RegisterExtension(E_DefaultUint64)
+	proto.RegisterExtension(E_DefaultSint32)
+	proto.RegisterExtension(E_DefaultSint64)
+	proto.RegisterExtension(E_DefaultFixed32)
+	proto.RegisterExtension(E_DefaultFixed64)
+	proto.RegisterExtension(E_DefaultSfixed32)
+	proto.RegisterExtension(E_DefaultSfixed64)
+	proto.RegisterExtension(E_DefaultBool)
+	proto.RegisterExtension(E_DefaultString)
+	proto.RegisterExtension(E_DefaultBytes)
+	proto.RegisterExtension(E_DefaultEnum)
 	proto.RegisterExtension(E_X201)
 	proto.RegisterExtension(E_X202)
 	proto.RegisterExtension(E_X203)

+ 45 - 0
proto/testdata/test.proto

@@ -277,6 +277,51 @@ extend MyMessage {
   repeated string greeting = 106;
 }
 
+message DefaultsMessage {
+  enum DefaultsEnum {
+    ZERO = 0;
+    ONE = 1;
+    TWO = 2;
+  };
+  extensions 100 to max;
+}
+
+extend DefaultsMessage {
+  optional double no_default_double = 101;
+  optional float no_default_float = 102;
+  optional int32 no_default_int32 = 103;
+  optional int64 no_default_int64 = 104;
+  optional uint32 no_default_uint32 = 105;
+  optional uint64 no_default_uint64 = 106;
+  optional sint32 no_default_sint32 = 107;
+  optional sint64 no_default_sint64 = 108;
+  optional fixed32 no_default_fixed32 = 109;
+  optional fixed64 no_default_fixed64 = 110;
+  optional sfixed32 no_default_sfixed32 = 111;
+  optional sfixed64 no_default_sfixed64 = 112;
+  optional bool no_default_bool = 113;
+  optional string no_default_string = 114;
+  optional bytes no_default_bytes = 115;
+  optional DefaultsMessage.DefaultsEnum no_default_enum = 116;
+
+  optional double default_double = 201 [default = 3.1415];
+  optional float default_float = 202 [default = 3.14];
+  optional int32 default_int32 = 203 [default = 42];
+  optional int64 default_int64 = 204 [default = 43];
+  optional uint32 default_uint32 = 205 [default = 44];
+  optional uint64 default_uint64 = 206 [default = 45];
+  optional sint32 default_sint32 = 207 [default = 46];
+  optional sint64 default_sint64 = 208 [default = 47];
+  optional fixed32 default_fixed32 = 209 [default = 48];
+  optional fixed64 default_fixed64 = 210 [default = 49];
+  optional sfixed32 default_sfixed32 = 211 [default = 50];
+  optional sfixed64 default_sfixed64 = 212 [default = 51];
+  optional bool default_bool = 213 [default = true];
+  optional string default_string = 214 [default = "Hello, string"];
+  optional bytes default_bytes = 215 [default = "Hello, bytes"];
+  optional DefaultsMessage.DefaultsEnum default_enum = 216 [default = ONE];
+}
+
 message MyMessageSet {
   option message_set_wire_format = true;
   extensions 100 to max;