internal/filedesc, internal/filetype: initial commit

The internal/fileinit package is split apart into two packages:
* internal/filedesc constructs descriptors from the raw proto.
It is very similar to the previous internal/fileinit package.
* internal/filetype wraps descriptors with Go type information

Overview:
* The internal/fileinit package will be deleted in a future CL.
It is kept around since the v1 repo currently depends on it.
* The internal/prototype package is deleted. All former usages of it
are now using internal/filedesc instead. Most significantly,
the reflect/protodesc package was almost entirely re-written.
* The internal/impl package drops support for messages that do not
have a Descriptor method (pre-2016). This removes a significant amount
of technical debt.
filedesc.Builder to parse raw descriptors.
* The internal/encoding/defval package now handles enum values by name.

Change-Id: I3957bcc8588a70470fd6c7de1122216b80615ab7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/182360
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/encoding/defval/default.go b/internal/encoding/defval/default.go
index a15df02..f03acdf 100644
--- a/internal/encoding/defval/default.go
+++ b/internal/encoding/defval/default.go
@@ -34,54 +34,55 @@
 )
 
 // Unmarshal deserializes the default string s according to the given kind k.
-// When using the Descriptor format on an enum kind, a Value of type string
-// representing the enum identifier is returned. It is the caller's
-// responsibility to verify that the identifier is valid.
-func Unmarshal(s string, k pref.Kind, f Format) (pref.Value, error) {
+// When k is an enum, a list of enum value descriptors must be provided.
+func Unmarshal(s string, k pref.Kind, evs pref.EnumValueDescriptors, f Format) (pref.Value, pref.EnumValueDescriptor, error) {
 	switch k {
 	case pref.BoolKind:
 		if f == GoTag {
 			switch s {
 			case "1":
-				return pref.ValueOf(true), nil
+				return pref.ValueOf(true), nil, nil
 			case "0":
-				return pref.ValueOf(false), nil
+				return pref.ValueOf(false), nil, nil
 			}
 		} else {
 			switch s {
 			case "true":
-				return pref.ValueOf(true), nil
+				return pref.ValueOf(true), nil, nil
 			case "false":
-				return pref.ValueOf(false), nil
+				return pref.ValueOf(false), nil, nil
 			}
 		}
 	case pref.EnumKind:
 		if f == GoTag {
-			// Go tags used the numeric form of the enum value.
+			// Go tags use the numeric form of the enum value.
 			if n, err := strconv.ParseInt(s, 10, 32); err == nil {
-				return pref.ValueOf(pref.EnumNumber(n)), nil
+				if ev := evs.ByNumber(pref.EnumNumber(n)); ev != nil {
+					return pref.ValueOf(ev.Number()), ev, nil
+				}
 			}
 		} else {
-			// Descriptor default_value used the enum identifier.
-			if pref.Name(s).IsValid() {
-				return pref.ValueOf(s), nil
+			// Descriptor default_value use the enum identifier.
+			ev := evs.ByName(pref.Name(s))
+			if ev != nil {
+				return pref.ValueOf(ev.Number()), ev, nil
 			}
 		}
 	case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
 		if v, err := strconv.ParseInt(s, 10, 32); err == nil {
-			return pref.ValueOf(int32(v)), nil
+			return pref.ValueOf(int32(v)), nil, nil
 		}
 	case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
 		if v, err := strconv.ParseInt(s, 10, 64); err == nil {
-			return pref.ValueOf(int64(v)), nil
+			return pref.ValueOf(int64(v)), nil, nil
 		}
 	case pref.Uint32Kind, pref.Fixed32Kind:
 		if v, err := strconv.ParseUint(s, 10, 32); err == nil {
-			return pref.ValueOf(uint32(v)), nil
+			return pref.ValueOf(uint32(v)), nil, nil
 		}
 	case pref.Uint64Kind, pref.Fixed64Kind:
 		if v, err := strconv.ParseUint(s, 10, 64); err == nil {
-			return pref.ValueOf(uint64(v)), nil
+			return pref.ValueOf(uint64(v)), nil, nil
 		}
 	case pref.FloatKind, pref.DoubleKind:
 		var v float64
@@ -98,25 +99,26 @@
 		}
 		if err == nil {
 			if k == pref.FloatKind {
-				return pref.ValueOf(float32(v)), nil
+				return pref.ValueOf(float32(v)), nil, nil
 			} else {
-				return pref.ValueOf(float64(v)), nil
+				return pref.ValueOf(float64(v)), nil, nil
 			}
 		}
 	case pref.StringKind:
 		// String values are already unescaped and can be used as is.
-		return pref.ValueOf(s), nil
+		return pref.ValueOf(s), nil, nil
 	case pref.BytesKind:
 		if b, ok := unmarshalBytes(s); ok {
-			return pref.ValueOf(b), nil
+			return pref.ValueOf(b), nil, nil
 		}
 	}
-	return pref.Value{}, errors.New("invalid default value for %v: %q", k, s)
+	return pref.Value{}, nil, errors.New("invalid default value for %v: %q", k, s)
 }
 
 // Marshal serializes v as the default string according to the given kind k.
-// Enums are serialized in numeric form regardless of format chosen.
-func Marshal(v pref.Value, k pref.Kind, f Format) (string, error) {
+// When specifying the Descriptor format for an enum kind, the associated
+// enum value descriptor must be provided.
+func Marshal(v pref.Value, ev pref.EnumValueDescriptor, k pref.Kind, f Format) (string, error) {
 	switch k {
 	case pref.BoolKind:
 		if f == GoTag {
@@ -133,7 +135,11 @@
 			}
 		}
 	case pref.EnumKind:
-		return strconv.FormatInt(int64(v.Enum()), 10), nil
+		if f == GoTag {
+			return strconv.FormatInt(int64(v.Enum()), 10), nil
+		} else {
+			return string(ev.Name()), nil
+		}
 	case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind, pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
 		return strconv.FormatInt(v.Int(), 10), nil
 	case pref.Uint32Kind, pref.Fixed32Kind, pref.Uint64Kind, pref.Fixed64Kind:
diff --git a/internal/encoding/defval/default_test.go b/internal/encoding/defval/default_test.go
index a25a93f..d81150d 100644
--- a/internal/encoding/defval/default_test.go
+++ b/internal/encoding/defval/default_test.go
@@ -2,52 +2,103 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package defval
+package defval_test
 
 import (
 	"math"
 	"reflect"
 	"testing"
 
+	"google.golang.org/protobuf/internal/encoding/defval"
+	fdesc "google.golang.org/protobuf/internal/filedesc"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
 )
 
 func Test(t *testing.T) {
+	evs := fdesc.EnumValues{List: []fdesc.EnumValue{{}}}
+	evs.List[0].L0.ParentFile = fdesc.SurrogateProto2
+	evs.List[0].L0.FullName = "ALPHA"
+	evs.List[0].L1.Number = 1
+
 	V := pref.ValueOf
 	tests := []struct {
 		val   pref.Value
+		enum  pref.EnumValueDescriptor
+		enums pref.EnumValueDescriptors
 		kind  pref.Kind
 		strPB string
 		strGo string
-	}{
-		{V(bool(true)), pref.BoolKind, "true", "1"},
-		{V(int32(-0x1234)), pref.Int32Kind, "-4660", "-4660"},
-		{V(float32(math.Pi)), pref.FloatKind, "3.1415927", "3.1415927"},
-		{V(float64(math.Pi)), pref.DoubleKind, "3.141592653589793", "3.141592653589793"},
-		{V(string("hello, \xde\xad\xbe\xef\n")), pref.StringKind, "hello, \xde\xad\xbe\xef\n", "hello, \xde\xad\xbe\xef\n"},
-		{V([]byte("hello, \xde\xad\xbe\xef\n")), pref.BytesKind, "hello, \\336\\255\\276\\357\\n", "hello, \\336\\255\\276\\357\\n"},
-	}
+	}{{
+		val:   V(bool(true)),
+		enum:  nil,
+		enums: nil,
+		kind:  pref.BoolKind,
+		strPB: "true",
+		strGo: "1",
+	}, {
+		val:   V(int32(-0x1234)),
+		enum:  nil,
+		enums: nil,
+		kind:  pref.Int32Kind,
+		strPB: "-4660",
+		strGo: "-4660",
+	}, {
+		val:   V(float32(math.Pi)),
+		enum:  nil,
+		enums: nil,
+		kind:  pref.FloatKind,
+		strPB: "3.1415927",
+		strGo: "3.1415927",
+	}, {
+		val:   V(float64(math.Pi)),
+		enum:  nil,
+		enums: nil,
+		kind:  pref.DoubleKind,
+		strPB: "3.141592653589793",
+		strGo: "3.141592653589793",
+	}, {
+		val:   V(string("hello, \xde\xad\xbe\xef\n")),
+		enum:  nil,
+		enums: nil,
+		kind:  pref.StringKind,
+		strPB: "hello, \xde\xad\xbe\xef\n",
+		strGo: "hello, \xde\xad\xbe\xef\n",
+	}, {
+		val:   V([]byte("hello, \xde\xad\xbe\xef\n")),
+		enum:  nil,
+		enums: nil,
+		kind:  pref.BytesKind,
+		strPB: "hello, \\336\\255\\276\\357\\n",
+		strGo: "hello, \\336\\255\\276\\357\\n",
+	}, {
+		val:   V(pref.EnumNumber(1)),
+		enum:  &evs.List[0],
+		enums: &evs,
+		kind:  pref.EnumKind,
+		strPB: "ALPHA",
+		strGo: "1",
+	}}
 
 	for _, tt := range tests {
 		t.Run("", func(t *testing.T) {
-			gotStrPB, _ := Marshal(tt.val, tt.kind, Descriptor)
-			if gotStrPB != tt.strPB {
-				t.Errorf("Marshal(%v, %v, Descriptor) = %q, want %q", tt.val, tt.kind, gotStrPB, tt.strPB)
+			gotStr, _ := defval.Marshal(tt.val, tt.enum, tt.kind, defval.Descriptor)
+			if gotStr != tt.strPB {
+				t.Errorf("Marshal(%v, %v, Descriptor) = %q, want %q", tt.val, tt.kind, gotStr, tt.strPB)
 			}
 
-			gotStrGo, _ := Marshal(tt.val, tt.kind, GoTag)
-			if gotStrGo != tt.strGo {
-				t.Errorf("Marshal(%v, %v, GoTag) = %q, want %q", tt.val, tt.kind, gotStrGo, tt.strGo)
+			gotStr, _ = defval.Marshal(tt.val, tt.enum, tt.kind, defval.GoTag)
+			if gotStr != tt.strGo {
+				t.Errorf("Marshal(%v, %v, GoTag) = %q, want %q", tt.val, tt.kind, gotStr, tt.strGo)
 			}
 
-			gotValPB, _ := Unmarshal(tt.strPB, tt.kind, Descriptor)
-			if !reflect.DeepEqual(gotValPB.Interface(), tt.val.Interface()) {
-				t.Errorf("Unmarshal(%v, %v, Descriptor) = %q, want %q", tt.strPB, tt.kind, gotValPB, tt.val)
+			gotVal, gotEnum, _ := defval.Unmarshal(tt.strPB, tt.kind, tt.enums, defval.Descriptor)
+			if !reflect.DeepEqual(gotVal.Interface(), tt.val.Interface()) || gotEnum != tt.enum {
+				t.Errorf("Unmarshal(%v, %v, Descriptor) = (%q, %v), want (%q, %v)", tt.strPB, tt.kind, gotVal, gotEnum, tt.val, tt.enum)
 			}
 
-			gotValGo, _ := Unmarshal(tt.strGo, tt.kind, GoTag)
-			if !reflect.DeepEqual(gotValGo.Interface(), tt.val.Interface()) {
-				t.Errorf("Unmarshal(%v, %v, GoTag) = %q, want %q", tt.strGo, tt.kind, gotValGo, tt.val)
+			gotVal, gotEnum, _ = defval.Unmarshal(tt.strGo, tt.kind, tt.enums, defval.GoTag)
+			if !reflect.DeepEqual(gotVal.Interface(), tt.val.Interface()) || gotEnum != tt.enum {
+				t.Errorf("Unmarshal(%v, %v, GoTag) = (%q, %v), want (%q, %v)", tt.strGo, tt.kind, gotVal, gotEnum, tt.val, tt.enum)
 			}
 		})
 	}
diff --git a/internal/encoding/pack/pack_test.go b/internal/encoding/pack/pack_test.go
index d2f8baa..d6704e9 100644
--- a/internal/encoding/pack/pack_test.go
+++ b/internal/encoding/pack/pack_test.go
@@ -11,35 +11,47 @@
 	"math"
 	"testing"
 
-	cmp "github.com/google/go-cmp/cmp"
-	ptype "google.golang.org/protobuf/internal/prototype"
+	"github.com/google/go-cmp/cmp"
+	"google.golang.org/protobuf/encoding/prototext"
+	pdesc "google.golang.org/protobuf/reflect/protodesc"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+
+	"google.golang.org/protobuf/types/descriptorpb"
 )
 
 var msgDesc = func() pref.MessageDescriptor {
-	mtyp, err := ptype.NewMessage(&ptype.StandaloneMessage{
-		Syntax:   pref.Proto2,
-		FullName: "Message",
-		Fields: []ptype.Field{
-			{Name: "F1", Number: 1, Cardinality: pref.Repeated, Kind: pref.BoolKind, IsPacked: ptype.True},
-			{Name: "F2", Number: 2, Cardinality: pref.Repeated, Kind: pref.Int64Kind, IsPacked: ptype.True},
-			{Name: "F3", Number: 3, Cardinality: pref.Repeated, Kind: pref.Sint64Kind, IsPacked: ptype.True},
-			{Name: "F4", Number: 4, Cardinality: pref.Repeated, Kind: pref.Uint64Kind, IsPacked: ptype.True},
-			{Name: "F5", Number: 5, Cardinality: pref.Repeated, Kind: pref.Fixed32Kind, IsPacked: ptype.True},
-			{Name: "F6", Number: 6, Cardinality: pref.Repeated, Kind: pref.Sfixed32Kind, IsPacked: ptype.True},
-			{Name: "F7", Number: 7, Cardinality: pref.Repeated, Kind: pref.FloatKind, IsPacked: ptype.True},
-			{Name: "F8", Number: 8, Cardinality: pref.Repeated, Kind: pref.Fixed64Kind, IsPacked: ptype.True},
-			{Name: "F9", Number: 9, Cardinality: pref.Repeated, Kind: pref.Sfixed64Kind, IsPacked: ptype.True},
-			{Name: "F10", Number: 10, Cardinality: pref.Repeated, Kind: pref.DoubleKind, IsPacked: ptype.True},
-			{Name: "F11", Number: 11, Cardinality: pref.Optional, Kind: pref.StringKind},
-			{Name: "F12", Number: 12, Cardinality: pref.Optional, Kind: pref.BytesKind},
-			{Name: "F13", Number: 13, Cardinality: pref.Optional, Kind: pref.MessageKind, MessageType: ptype.PlaceholderMessage("Message")},
-			{Name: "F14", Number: 14, Cardinality: pref.Optional, Kind: pref.GroupKind, MessageType: ptype.PlaceholderMessage("Message")}},
-	})
+	const s = `
+		name:   "test.proto"
+		syntax: "proto2"
+		message_type: [{
+			name: "Message"
+			field: [
+				{name:"F1"  number:1  label:LABEL_REPEATED type:TYPE_BOOL     options:{packed:true}},
+				{name:"F2"  number:2  label:LABEL_REPEATED type:TYPE_INT64    options:{packed:true}},
+				{name:"F3"  number:3  label:LABEL_REPEATED type:TYPE_SINT64   options:{packed:true}},
+				{name:"F4"  number:4  label:LABEL_REPEATED type:TYPE_UINT64   options:{packed:true}},
+				{name:"F5"  number:5  label:LABEL_REPEATED type:TYPE_FIXED32  options:{packed:true}},
+				{name:"F6"  number:6  label:LABEL_REPEATED type:TYPE_SFIXED32 options:{packed:true}},
+				{name:"F7"  number:7  label:LABEL_REPEATED type:TYPE_FLOAT    options:{packed:true}},
+				{name:"F8"  number:8  label:LABEL_REPEATED type:TYPE_FIXED64  options:{packed:true}},
+				{name:"F9"  number:9  label:LABEL_REPEATED type:TYPE_SFIXED64 options:{packed:true}},
+				{name:"F10" number:10 label:LABEL_REPEATED type:TYPE_DOUBLE   options:{packed:true}},
+				{name:"F11" number:11 label:LABEL_OPTIONAL type:TYPE_STRING},
+				{name:"F12" number:12 label:LABEL_OPTIONAL type:TYPE_BYTES},
+				{name:"F13" number:13 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".Message"},
+				{name:"F14" number:14 label:LABEL_OPTIONAL type:TYPE_GROUP   type_name:".Message"}
+			]
+		}]
+	`
+	pb := new(descriptorpb.FileDescriptorProto)
+	if err := prototext.Unmarshal([]byte(s), pb); err != nil {
+		panic(err)
+	}
+	fd, err := pdesc.NewFile(pb, nil)
 	if err != nil {
 		panic(err)
 	}
-	return mtyp
+	return fd.Messages().Get(0)
 }()
 
 // dhex decodes a hex-string and returns the bytes and panics if s is invalid.
diff --git a/internal/encoding/tag/tag.go b/internal/encoding/tag/tag.go
index f1f24e8..1c568e3 100644
--- a/internal/encoding/tag/tag.go
+++ b/internal/encoding/tag/tag.go
@@ -12,7 +12,7 @@
 	"strings"
 
 	defval "google.golang.org/protobuf/internal/encoding/defval"
-	ptype "google.golang.org/protobuf/internal/prototype"
+	fdesc "google.golang.org/protobuf/internal/filedesc"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
 )
 
@@ -24,11 +24,13 @@
 // tag does not record sufficient information to determine that.
 // The type is the underlying field type (e.g., a repeated field may be
 // represented by []T, but the Go type passed in is just T).
-// This does not populate the EnumType or MessageType (except for weak message).
+// A list of enum value descriptors must be provided for enum fields.
+// This does not populate the Enum or Message (except for weak message).
 //
 // This function is a best effort attempt; parsing errors are ignored.
-func Unmarshal(tag string, goType reflect.Type) ptype.Field {
-	f := ptype.Field{}
+func Unmarshal(tag string, goType reflect.Type, evs pref.EnumValueDescriptors) pref.FieldDescriptor {
+	f := new(fdesc.Field)
+	f.L0.ParentFile = fdesc.SurrogateProto2
 	for len(tag) > 0 {
 		i := strings.IndexByte(tag, ',')
 		if i < 0 {
@@ -36,88 +38,92 @@
 		}
 		switch s := tag[:i]; {
 		case strings.HasPrefix(s, "name="):
-			f.Name = pref.Name(s[len("name="):])
+			f.L0.FullName = pref.FullName(s[len("name="):])
 		case strings.Trim(s, "0123456789") == "":
 			n, _ := strconv.ParseUint(s, 10, 32)
-			f.Number = pref.FieldNumber(n)
+			f.L1.Number = pref.FieldNumber(n)
 		case s == "opt":
-			f.Cardinality = pref.Optional
+			f.L1.Cardinality = pref.Optional
 		case s == "req":
-			f.Cardinality = pref.Required
+			f.L1.Cardinality = pref.Required
 		case s == "rep":
-			f.Cardinality = pref.Repeated
+			f.L1.Cardinality = pref.Repeated
 		case s == "varint":
 			switch goType.Kind() {
 			case reflect.Bool:
-				f.Kind = pref.BoolKind
+				f.L1.Kind = pref.BoolKind
 			case reflect.Int32:
-				f.Kind = pref.Int32Kind
+				f.L1.Kind = pref.Int32Kind
 			case reflect.Int64:
-				f.Kind = pref.Int64Kind
+				f.L1.Kind = pref.Int64Kind
 			case reflect.Uint32:
-				f.Kind = pref.Uint32Kind
+				f.L1.Kind = pref.Uint32Kind
 			case reflect.Uint64:
-				f.Kind = pref.Uint64Kind
+				f.L1.Kind = pref.Uint64Kind
 			}
 		case s == "zigzag32":
 			if goType.Kind() == reflect.Int32 {
-				f.Kind = pref.Sint32Kind
+				f.L1.Kind = pref.Sint32Kind
 			}
 		case s == "zigzag64":
 			if goType.Kind() == reflect.Int64 {
-				f.Kind = pref.Sint64Kind
+				f.L1.Kind = pref.Sint64Kind
 			}
 		case s == "fixed32":
 			switch goType.Kind() {
 			case reflect.Int32:
-				f.Kind = pref.Sfixed32Kind
+				f.L1.Kind = pref.Sfixed32Kind
 			case reflect.Uint32:
-				f.Kind = pref.Fixed32Kind
+				f.L1.Kind = pref.Fixed32Kind
 			case reflect.Float32:
-				f.Kind = pref.FloatKind
+				f.L1.Kind = pref.FloatKind
 			}
 		case s == "fixed64":
 			switch goType.Kind() {
 			case reflect.Int64:
-				f.Kind = pref.Sfixed64Kind
+				f.L1.Kind = pref.Sfixed64Kind
 			case reflect.Uint64:
-				f.Kind = pref.Fixed64Kind
+				f.L1.Kind = pref.Fixed64Kind
 			case reflect.Float64:
-				f.Kind = pref.DoubleKind
+				f.L1.Kind = pref.DoubleKind
 			}
 		case s == "bytes":
 			switch {
 			case goType.Kind() == reflect.String:
-				f.Kind = pref.StringKind
+				f.L1.Kind = pref.StringKind
 			case goType.Kind() == reflect.Slice && goType.Elem() == byteType:
-				f.Kind = pref.BytesKind
+				f.L1.Kind = pref.BytesKind
 			default:
-				f.Kind = pref.MessageKind
+				f.L1.Kind = pref.MessageKind
 			}
 		case s == "group":
-			f.Kind = pref.GroupKind
+			f.L1.Kind = pref.GroupKind
 		case strings.HasPrefix(s, "enum="):
-			f.Kind = pref.EnumKind
+			f.L1.Kind = pref.EnumKind
 		case strings.HasPrefix(s, "json="):
-			f.JSONName = s[len("json="):]
+			f.L1.JSONName = fdesc.JSONName(s[len("json="):])
 		case s == "packed":
-			f.IsPacked = ptype.True
+			f.L1.HasPacked = true
+			f.L1.IsPacked = true
 		case strings.HasPrefix(s, "weak="):
-			f.IsWeak = true
-			f.MessageType = ptype.PlaceholderMessage(pref.FullName(s[len("weak="):]))
+			f.L1.IsWeak = true
+			f.L1.Message = fdesc.PlaceholderMessage(pref.FullName(s[len("weak="):]))
 		case strings.HasPrefix(s, "def="):
 			// The default tag is special in that everything afterwards is the
 			// default regardless of the presence of commas.
 			s, i = tag[len("def="):], len(tag)
-			f.Default, _ = defval.Unmarshal(s, f.Kind, defval.GoTag)
+			v, ev, _ := defval.Unmarshal(s, f.L1.Kind, evs, defval.GoTag)
+			f.L1.Default = fdesc.DefaultValue(v, ev)
+		case s == "proto3":
+			f.L0.ParentFile = fdesc.SurrogateProto3
 		}
 		tag = strings.TrimPrefix(tag[i:], ",")
 	}
 
 	// The generator uses the group message name instead of the field name.
 	// We obtain the real field name by lowercasing the group name.
-	if f.Kind == pref.GroupKind {
-		f.Name = pref.Name(strings.ToLower(string(f.Name)))
+	if f.L1.Kind == pref.GroupKind {
+		f.L0.FullName = pref.FullName(strings.ToLower(string(f.L0.FullName)))
 	}
 	return f
 }
@@ -169,7 +175,8 @@
 		name = string(fd.Message().Name())
 	}
 	tag = append(tag, "name="+name)
-	if jsonName := fd.JSONName(); jsonName != "" && jsonName != name {
+	if jsonName := fd.JSONName(); jsonName != "" && jsonName != name && !fd.IsExtension() {
+		// TODO: The jsonName != name condition looks wrong.
 		tag = append(tag, "json="+jsonName)
 	}
 	// The previous implementation does not tag extension fields as proto3,
@@ -186,7 +193,7 @@
 	}
 	// This must appear last in the tag, since commas in strings aren't escaped.
 	if fd.HasDefault() {
-		def, _ := defval.Marshal(fd.Default(), fd.Kind(), defval.GoTag)
+		def, _ := defval.Marshal(fd.Default(), fd.DefaultEnumValue(), fd.Kind(), defval.GoTag)
 		tag = append(tag, "def="+def)
 	}
 	return strings.Join(tag, ",")
diff --git a/internal/encoding/tag/tag_test.go b/internal/encoding/tag/tag_test.go
index 0433b42..680ba52 100644
--- a/internal/encoding/tag/tag_test.go
+++ b/internal/encoding/tag/tag_test.go
@@ -2,54 +2,40 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package tag
+package tag_test
 
 import (
 	"reflect"
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
-	"github.com/google/go-cmp/cmp/cmpopts"
-	ptype "google.golang.org/protobuf/internal/prototype"
+	"google.golang.org/protobuf/internal/encoding/tag"
+	fdesc "google.golang.org/protobuf/internal/filedesc"
+	"google.golang.org/protobuf/proto"
+	pdesc "google.golang.org/protobuf/reflect/protodesc"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
 )
 
 func Test(t *testing.T) {
-	m := &ptype.StandaloneMessage{
-		Syntax:   pref.Proto3,
-		FullName: "golang.org.example.FooMessage",
-		Fields: []ptype.Field{{
-			Name:        "foo_field",
-			Number:      1337,
-			Cardinality: pref.Repeated,
-			Kind:        pref.BytesKind,
-			JSONName:    "fooField",
-			Default:     pref.ValueOf([]byte("hello, \xde\xad\xbe\xef\n")),
-		}},
-	}
-	md, err := ptype.NewMessage(m)
-	if err != nil {
-		t.Fatalf("unexpected NewMessage error: %v", err)
-	}
+	fd := new(fdesc.Field)
+	fd.L0.ParentFile = fdesc.SurrogateProto3
+	fd.L0.FullName = "foo_field"
+	fd.L1.Number = 1337
+	fd.L1.Cardinality = pref.Repeated
+	fd.L1.Kind = pref.BytesKind
+	fd.L1.JSONName = fdesc.JSONName("fooField")
+	fd.L1.Default = fdesc.DefaultValue(pref.ValueOf([]byte("hello, \xde\xad\xbe\xef\n")), nil)
 
 	// Marshal test.
-	gotTag := Marshal(md.Fields().Get(0), "")
+	gotTag := tag.Marshal(fd, "")
 	wantTag := `bytes,1337,rep,name=foo_field,json=fooField,proto3,def=hello, \336\255\276\357\n`
 	if gotTag != wantTag {
 		t.Errorf("Marshal() = `%v`, want `%v`", gotTag, wantTag)
 	}
 
 	// Unmarshal test.
-	gotField := Unmarshal(wantTag, reflect.TypeOf([]byte{}))
-	wantField := m.Fields[0]
-	opts := cmp.Options{
-		cmp.Transformer("UnwrapValue", func(x pref.Value) interface{} {
-			return x.Interface()
-		}),
-		cmpopts.IgnoreUnexported(ptype.Field{}),
-		cmpopts.IgnoreFields(ptype.Field{}, "Options"),
-	}
-	if diff := cmp.Diff(wantField, gotField, opts); diff != "" {
-		t.Errorf("Unmarshal() mismatch (-want +got):\n%v", diff)
+	gotFD := tag.Unmarshal(wantTag, reflect.TypeOf([]byte{}), nil)
+	wantFD := fd
+	if !proto.Equal(pdesc.ToFieldDescriptorProto(gotFD), pdesc.ToFieldDescriptorProto(wantFD)) {
+		t.Errorf("Umarshal() mismatch:\ngot  %v\nwant %v", gotFD, wantFD)
 	}
 }