Skip to content

Commit cc376d7

Browse files
committed
all: update to wrap v2
All functionality has been re-written to either wrap v2 directly (e.g., binary serialization) or written to use v2 protobuf reflection (e.g., text and json serialization). This is to done to reduce the technical debt of maintaining the v1 module. Change-Id: I6749fa58a465df991c8fcf89e8d7077d64a2cfdb Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/213901 Reviewed-by: Damien Neil <dneil@google.com>
1 parent ff0ab7f commit cc376d7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+6863
-17106
lines changed

descriptor/descriptor.go

Lines changed: 152 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,64 +2,183 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// Package descriptor provides functions for obtaining protocol buffer
6-
// descriptors for generated Go types.
5+
// Package descriptor provides functions for obtaining the protocol buffer
6+
// descriptors of generated Go types.
77
//
8-
// Deprecated: Do not use. The new v2 Message interface provides direct support
9-
// for programmatically interacting with the descriptor information.
8+
// Deprecated: Use the "google.golang.org/protobuf/reflect/protoreflect"
9+
// package instead to obtain an EnumDescriptor or MessageDescriptor in order to
10+
// programatically interact with the protobuf type system.
1011
package descriptor
1112

1213
import (
1314
"bytes"
1415
"compress/gzip"
15-
"fmt"
1616
"io/ioutil"
17+
"sync"
1718

1819
"github.com/golang/protobuf/proto"
20+
"google.golang.org/protobuf/reflect/protodesc"
21+
"google.golang.org/protobuf/reflect/protoreflect"
22+
"google.golang.org/protobuf/runtime/protoimpl"
23+
1924
descriptorpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
2025
)
2126

22-
// extractFile extracts a FileDescriptorProto from a gzip'd buffer.
23-
func extractFile(gz []byte) (*descriptorpb.FileDescriptorProto, error) {
24-
r, err := gzip.NewReader(bytes.NewReader(gz))
25-
if err != nil {
26-
return nil, fmt.Errorf("failed to open gzip reader: %v", err)
27+
// Message is proto.Message with a method to return its descriptor.
28+
// Not every message is guaranteed to implement this interface.
29+
type Message interface {
30+
proto.Message
31+
Descriptor() ([]byte, []int)
32+
}
33+
34+
// ForMessage returns the file descriptor proto containing
35+
// the message and the message descriptor proto for the message itself.
36+
// The returned proto messages must not be mutated.
37+
func ForMessage(m Message) (*descriptorpb.FileDescriptorProto, *descriptorpb.DescriptorProto) {
38+
return MessageDescriptorProtoOf(m)
39+
}
40+
41+
// GeneratedEnum is any enum type generated by protoc-gen-go
42+
// which is a named int32 kind.
43+
type GeneratedEnum interface{}
44+
45+
// GeneratedMessage is any message type generated by protoc-gen-go
46+
// which is a pointer to a named struct kind.
47+
type GeneratedMessage interface{}
48+
49+
type rawDesc struct {
50+
fileDesc []byte
51+
indexes []int
52+
}
53+
54+
var rawDescCache sync.Map // map[protoreflect.Descriptor]*rawDesc
55+
56+
func deriveRawDescriptor(d protoreflect.Descriptor) ([]byte, []int) {
57+
// Fast-path: check whether raw descriptors are already cached.
58+
origDesc := d
59+
if v, ok := rawDescCache.Load(origDesc); ok {
60+
return v.(*rawDesc).fileDesc, v.(*rawDesc).indexes
2761
}
28-
defer r.Close()
2962

30-
b, err := ioutil.ReadAll(r)
31-
if err != nil {
32-
return nil, fmt.Errorf("failed to uncompress descriptor: %v", err)
63+
// Slow-path: derive the raw descriptor from the v2 descriptor.
64+
65+
// Start with the leaf (a given enum or message declaration) and
66+
// ascend upwards until we hit the parent file descriptor.
67+
var idxs []int
68+
for {
69+
idxs = append(idxs, d.Index())
70+
d = d.Parent()
71+
if d == nil {
72+
// TODO: We could construct a FileDescriptor stub for standalone
73+
// descriptors to satisfy the API.
74+
return nil, nil
75+
}
76+
if _, ok := d.(protoreflect.FileDescriptor); ok {
77+
break
78+
}
3379
}
3480

35-
fd := new(descriptorpb.FileDescriptorProto)
36-
if err := proto.Unmarshal(b, fd); err != nil {
37-
return nil, fmt.Errorf("malformed FileDescriptorProto: %v", err)
81+
// Obtain the raw file descriptor.
82+
var raw []byte
83+
switch fd := d.(type) {
84+
case interface{ ProtoLegacyRawDesc() []byte }:
85+
raw = fd.ProtoLegacyRawDesc()
86+
case protoreflect.FileDescriptor:
87+
raw, _ = proto.Marshal(protodesc.ToFileDescriptorProto(fd))
3888
}
89+
file := protoimpl.X.CompressGZIP(raw)
3990

40-
return fd, nil
91+
// Reverse the indexes, since we populated it in reverse.
92+
for i, j := 0, len(idxs)-1; i < j; i, j = i+1, j-1 {
93+
idxs[i], idxs[j] = idxs[j], idxs[i]
94+
}
95+
96+
if v, ok := rawDescCache.LoadOrStore(origDesc, &rawDesc{file, idxs}); ok {
97+
return v.(*rawDesc).fileDesc, v.(*rawDesc).indexes
98+
}
99+
return file, idxs
41100
}
42101

43-
// Message is a proto.Message with a method to return its descriptor.
44-
//
45-
// Message types generated by the protocol compiler always satisfy
46-
// the Message interface.
47-
type Message interface {
48-
proto.Message
49-
Descriptor() ([]byte, []int)
102+
// EnumRawDescriptorOf returns the GZIP'd raw file descriptor containing the
103+
// enum and the index path to reach the enum declaration.
104+
// The returned slices must not be mutated.
105+
func EnumRawDescriptorOf(e GeneratedEnum) ([]byte, []int) {
106+
if ev, ok := e.(interface{ EnumDescriptor() ([]byte, []int) }); ok {
107+
return ev.EnumDescriptor()
108+
}
109+
ed := protoimpl.X.EnumTypeOf(e)
110+
return deriveRawDescriptor(ed.Descriptor())
111+
}
112+
113+
// MessageRawDescriptorOf returns the GZIP'd raw file descriptor containing
114+
// the message and the index path to reach the message declaration.
115+
// The returned slices must not be mutated.
116+
func MessageRawDescriptorOf(m GeneratedMessage) ([]byte, []int) {
117+
if mv, ok := m.(interface{ Descriptor() ([]byte, []int) }); ok {
118+
return mv.Descriptor()
119+
}
120+
md := protoimpl.X.MessageTypeOf(m)
121+
return deriveRawDescriptor(md.Descriptor())
50122
}
51123

52-
// ForMessage returns a FileDescriptorProto and a DescriptorProto from within it
53-
// describing the given message.
54-
func ForMessage(msg Message) (fd *descriptorpb.FileDescriptorProto, md *descriptorpb.DescriptorProto) {
55-
gz, path := msg.Descriptor()
56-
fd, err := extractFile(gz)
124+
var fileDescCache sync.Map // map[*byte]*descriptorpb.FileDescriptorProto
125+
126+
func deriveFileDescriptor(rawDesc []byte) *descriptorpb.FileDescriptorProto {
127+
// Fast-path: check whether descriptor protos are already cached.
128+
if v, ok := fileDescCache.Load(&rawDesc[0]); ok {
129+
return v.(*descriptorpb.FileDescriptorProto)
130+
}
131+
132+
// Slow-path: derive the descriptor proto from the GZIP'd message.
133+
zr, err := gzip.NewReader(bytes.NewReader(rawDesc))
57134
if err != nil {
58-
panic(fmt.Sprintf("invalid FileDescriptorProto for %T: %v", msg, err))
135+
panic(err)
59136
}
137+
b, err := ioutil.ReadAll(zr)
138+
if err != nil {
139+
panic(err)
140+
}
141+
fd := new(descriptorpb.FileDescriptorProto)
142+
if err := proto.Unmarshal(b, fd); err != nil {
143+
panic(err)
144+
}
145+
if v, ok := fileDescCache.LoadOrStore(&rawDesc[0], fd); ok {
146+
return v.(*descriptorpb.FileDescriptorProto)
147+
}
148+
return fd
149+
}
60150

61-
md = fd.MessageType[path[0]]
62-
for _, i := range path[1:] {
151+
// EnumDescriptorProtoOf returns the file descriptor proto containing
152+
// the enum and the enum descriptor proto for the enum itself.
153+
// The returned proto messages must not be mutated.
154+
func EnumDescriptorProtoOf(e GeneratedEnum) (*descriptorpb.FileDescriptorProto, *descriptorpb.EnumDescriptorProto) {
155+
rawDesc, idxs := EnumRawDescriptorOf(e)
156+
if rawDesc == nil || idxs == nil {
157+
return nil, nil
158+
}
159+
fd := deriveFileDescriptor(rawDesc)
160+
if len(idxs) == 1 {
161+
return fd, fd.EnumType[idxs[0]]
162+
}
163+
md := fd.MessageType[idxs[0]]
164+
for _, i := range idxs[1 : len(idxs)-1] {
165+
md = md.NestedType[i]
166+
}
167+
ed := md.EnumType[idxs[len(idxs)-1]]
168+
return fd, ed
169+
}
170+
171+
// MessageDescriptorProtoOf returns the file descriptor proto containing
172+
// the message and the message descriptor proto for the message itself.
173+
// The returned proto messages must not be mutated.
174+
func MessageDescriptorProtoOf(m GeneratedMessage) (*descriptorpb.FileDescriptorProto, *descriptorpb.DescriptorProto) {
175+
rawDesc, idxs := MessageRawDescriptorOf(m)
176+
if rawDesc == nil || idxs == nil {
177+
return nil, nil
178+
}
179+
fd := deriveFileDescriptor(rawDesc)
180+
md := fd.MessageType[idxs[0]]
181+
for _, i := range idxs[1:] {
63182
md = md.NestedType[i]
64183
}
65184
return fd, md

descriptor/descriptor_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package descriptor
6+
7+
import (
8+
"testing"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"google.golang.org/protobuf/reflect/protoreflect"
12+
13+
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
14+
)
15+
16+
func TestEnumDescriptorOf(t *testing.T) {
17+
tests := []struct {
18+
enum protoreflect.Enum
19+
idxs []int
20+
name string
21+
}{{
22+
enum: descpb.FieldDescriptorProto_Type(0),
23+
idxs: []int{
24+
new(descpb.FieldDescriptorProto).ProtoReflect().Descriptor().Index(),
25+
new(descpb.FieldDescriptorProto_Type).Descriptor().Index(),
26+
},
27+
name: "Type",
28+
}, {
29+
enum: descpb.FieldOptions_CType(0),
30+
idxs: []int{
31+
new(descpb.FieldOptions).ProtoReflect().Descriptor().Index(),
32+
new(descpb.FieldOptions_CType).Descriptor().Index(),
33+
},
34+
name: "CType",
35+
}}
36+
37+
for _, tt := range tests {
38+
e := struct{ protoreflect.Enum }{tt.enum} // v2-only enum
39+
40+
_, idxs := EnumRawDescriptorOf(e)
41+
if diff := cmp.Diff(tt.idxs, idxs); diff != "" {
42+
t.Errorf("path index mismatch (-want +got):\n%v", diff)
43+
}
44+
45+
_, ed := EnumDescriptorProtoOf(e)
46+
if ed.GetName() != tt.name {
47+
t.Errorf("mismatching enum name: got %v, want %v", ed.GetName(), tt.name)
48+
}
49+
}
50+
}
51+
52+
func TestMessageDescriptorOf(t *testing.T) {
53+
tests := []struct {
54+
message protoreflect.ProtoMessage
55+
idxs []int
56+
name string
57+
}{{
58+
message: (*descpb.SourceCodeInfo_Location)(nil),
59+
idxs: []int{
60+
new(descpb.SourceCodeInfo).ProtoReflect().Descriptor().Index(),
61+
new(descpb.SourceCodeInfo_Location).ProtoReflect().Descriptor().Index(),
62+
},
63+
name: "Location",
64+
}, {
65+
message: (*descpb.FileDescriptorProto)(nil),
66+
idxs: []int{
67+
new(descpb.FileDescriptorProto).ProtoReflect().Descriptor().Index(),
68+
},
69+
name: "FileDescriptorProto",
70+
}}
71+
72+
for _, tt := range tests {
73+
m := struct{ protoreflect.ProtoMessage }{tt.message} // v2-only message
74+
75+
_, idxs := MessageRawDescriptorOf(m)
76+
if diff := cmp.Diff(tt.idxs, idxs); diff != "" {
77+
t.Errorf("path index mismatch (-want +got):\n%v", diff)
78+
}
79+
80+
_, md := MessageDescriptorProtoOf(m)
81+
if md.GetName() != tt.name {
82+
t.Errorf("mismatching message name: got %v, want %v", md.GetName(), tt.name)
83+
}
84+
}
85+
}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ module github.com/golang/protobuf
33
go 1.9
44

55
require (
6-
github.com/google/go-cmp v0.3.1 // indirect
7-
google.golang.org/protobuf v0.0.0-20191108002925-e9187326c3ff
6+
github.com/google/go-cmp v0.3.1
7+
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd
88
)

go.sum

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,5 @@
1-
github.com/golang/protobuf v1.2.1-0.20190514181236-7800af189d76/go.mod h1:Zfz6qcDoDBESdv6JsKsGpgNHnkvwJAJwcA9eL+mOkgc=
2-
github.com/golang/protobuf v1.2.1-0.20190515194842-7574ba03306e/go.mod h1:GjgUz9uwrRQmdPBBrFqiVbojAmlpy6ryM6DCzC+20rE=
3-
github.com/golang/protobuf v1.2.1-0.20190516201927-a2cd3ac1b343/go.mod h1:PScGDF2x230A126tLt9Ol9RjhXzbiPJrt/CogooD2mE=
4-
github.com/golang/protobuf v1.2.1-0.20190516215712-ae2eaafab405/go.mod h1:UmP8hhPKR5WWIjbT9v0JEVT+U0DBSjbW8KaZVeyFfRE=
5-
github.com/golang/protobuf v1.2.1-0.20190523175523-a1331f0b4ab4/go.mod h1:G+fNMoyvKWZDB7PCDHF+dXbH9OeE3+JoozCd9V7i66U=
6-
github.com/golang/protobuf v1.2.1-0.20190605195750-76c9e09470ba/go.mod h1:S1YIJXvYHGRCG2UmZsOcElkAYfvZLg2sDRr9+Xu8JXU=
7-
github.com/golang/protobuf v1.2.1-0.20190617175902-f94016f5239f/go.mod h1:G+HpKX7pYZAVkElkAWZkr08MToW6pTp/vs+E9osFfbg=
8-
github.com/golang/protobuf v1.2.1-0.20190620192300-1ee46dfd80dd/go.mod h1:+CMAsi9jpYf/wAltLUKlg++CWXqxCJyD8iLDbQONsJs=
9-
github.com/golang/protobuf v1.2.1-0.20190806214225-7037721e6de0/go.mod h1:tDQPRlaHYu9yt1wPgdx85inRiLvUCuJZXsYjC0mwc1c=
10-
github.com/golang/protobuf v1.2.1-0.20190820204156-2da1b93405dd/go.mod h1:x87I3ou7ehf/yR6iQ88MkyDogdxXN04TELJ7HVy7V7I=
11-
github.com/golang/protobuf v1.2.1-0.20190820213554-ae1d65bc5435/go.mod h1:k7dGkiTZ3rjVDhKSpGt+x1zDzAePJk4jdhoBwIkQgBo=
12-
github.com/golang/protobuf v1.2.1-0.20191004062209-62f67f1ea9df/go.mod h1:o4el5ABfDjqFlwwvAq2OIgAPeNXQYUkhtrjNPXy6T6I=
13-
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
141
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
152
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
163
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
17-
google.golang.org/protobuf v0.0.0-20190514172829-e89e6244e0e8/go.mod h1:791zQGC15vDqjpmPRn1uGPu5oHy/Jzw/Q1n5JsgIIcY=
18-
google.golang.org/protobuf v0.0.0-20190514231807-cdb777356907/go.mod h1:HeRLsKXv4+wE27dOIGwnqcOgq6a1O/GJ7mGhiEPnBrU=
19-
google.golang.org/protobuf v0.0.0-20190516201745-40b83d67fc75/go.mod h1:jf+u8AHuKtkib+0J4/bQXPNzCmT3V9a02hVzYKtatuw=
20-
google.golang.org/protobuf v0.0.0-20190516215540-a95b29fbf623/go.mod h1:cWWmz5lsCWIcqGLROrKq5Lu231IJw2PzqOZ8cgspbfY=
21-
google.golang.org/protobuf v0.0.0-20190522194032-21ade498bd69/go.mod h1:cJytyYi/6qdwy/+gD49hmgHcwD7zhWxE/1KPEslaZ3M=
22-
google.golang.org/protobuf v0.0.0-20190605195314-89d49632e5cf/go.mod h1:Btug4TBaP5wNYcb2zGKDTS7WMcaPPLuqEAKfEAZWYbo=
23-
google.golang.org/protobuf v0.0.0-20190617175724-bd7b7a9e0c26/go.mod h1:+FOB8T5/Yw4ywwdyeun9/KlDeuwFYBkNQ+kVuwj9C94=
24-
google.golang.org/protobuf v0.0.0-20190620020611-d888139e7b59/go.mod h1:of3pt14Y+dOxz2tBOHXEoapPpKFC15/0zWhPAddkfsU=
25-
google.golang.org/protobuf v0.0.0-20190717230113-f647c82cc3c7 h1:U6U+Hb+UKNGJB0eMAjUGk0wTmy73kduTIvdsEgA4Gf8=
26-
google.golang.org/protobuf v0.0.0-20190717230113-f647c82cc3c7/go.mod h1:yGm7aNHn9Bp1NIvj6+CVUkcJshu+Usshfd3A+YxEuI8=
27-
google.golang.org/protobuf v0.0.0-20190820203659-c0f8c0a24ece h1:AFYGmds8FWBGNw0zddlFiGtDvkVFSnQ7J2bAdH4X9Xk=
28-
google.golang.org/protobuf v0.0.0-20190820203659-c0f8c0a24ece/go.mod h1:tRqhEyKwbKqwt5CQZAuOtj09RfhLNklDOhndhYA9blU=
29-
google.golang.org/protobuf v0.0.0-20190820213257-f1e905b04207 h1:ulV4hvtdAg7XsymkxyxHtKYxQoSq88XU1bmtCELxG38=
30-
google.golang.org/protobuf v0.0.0-20190820213257-f1e905b04207/go.mod h1:UJqt2ZERO8/qk5A9t8Ujq6OJ+MNvOQpg9X4RKyYz9Ho=
31-
google.golang.org/protobuf v0.0.0-20190828183429-79bfdbe45be2 h1:g52BKWg08C9Z3iSV/mBCfoP+ObYXKCgdXF+p1EB9zD0=
32-
google.golang.org/protobuf v0.0.0-20190828183429-79bfdbe45be2/go.mod h1:fYMzYhnMXLj/kGDPzNOptS3IFFlQjWTlu2j3ZPET2lw=
33-
google.golang.org/protobuf v0.0.0-20191108002925-e9187326c3ff h1:owlmxbzMXWq78QqeFT2Xj1ySspP/ot+aFzxHoz79YLQ=
34-
google.golang.org/protobuf v0.0.0-20191108002925-e9187326c3ff/go.mod h1:qKrTvhhUFcTIUF6KuejTfRdHXKeBPoa4mtynR6usTss=
4+
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd h1:zSMqFwpTkfj+1nNFgmgu4B+Qv5Kpf4jpd11lCmHKuwQ=
5+
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=

0 commit comments

Comments
 (0)