Skip to content

Commit

Permalink
all: update to wrap v2
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dsnet committed Jan 9, 2020
1 parent ff0ab7f commit cc376d7
Show file tree
Hide file tree
Showing 81 changed files with 6,863 additions and 17,106 deletions.
185 changes: 152 additions & 33 deletions descriptor/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,183 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

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

import (
"bytes"
"compress/gzip"
"fmt"
"io/ioutil"
"sync"

"github.com/golang/protobuf/proto"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/runtime/protoimpl"

descriptorpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
)

// extractFile extracts a FileDescriptorProto from a gzip'd buffer.
func extractFile(gz []byte) (*descriptorpb.FileDescriptorProto, error) {
r, err := gzip.NewReader(bytes.NewReader(gz))
if err != nil {
return nil, fmt.Errorf("failed to open gzip reader: %v", err)
// Message is proto.Message with a method to return its descriptor.
// Not every message is guaranteed to implement this interface.
type Message interface {
proto.Message
Descriptor() ([]byte, []int)
}

// ForMessage returns the file descriptor proto containing
// the message and the message descriptor proto for the message itself.
// The returned proto messages must not be mutated.
func ForMessage(m Message) (*descriptorpb.FileDescriptorProto, *descriptorpb.DescriptorProto) {
return MessageDescriptorProtoOf(m)
}

// GeneratedEnum is any enum type generated by protoc-gen-go
// which is a named int32 kind.
type GeneratedEnum interface{}

// GeneratedMessage is any message type generated by protoc-gen-go
// which is a pointer to a named struct kind.
type GeneratedMessage interface{}

type rawDesc struct {
fileDesc []byte
indexes []int
}

var rawDescCache sync.Map // map[protoreflect.Descriptor]*rawDesc

func deriveRawDescriptor(d protoreflect.Descriptor) ([]byte, []int) {
// Fast-path: check whether raw descriptors are already cached.
origDesc := d
if v, ok := rawDescCache.Load(origDesc); ok {
return v.(*rawDesc).fileDesc, v.(*rawDesc).indexes
}
defer r.Close()

b, err := ioutil.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("failed to uncompress descriptor: %v", err)
// Slow-path: derive the raw descriptor from the v2 descriptor.

// Start with the leaf (a given enum or message declaration) and
// ascend upwards until we hit the parent file descriptor.
var idxs []int
for {
idxs = append(idxs, d.Index())
d = d.Parent()
if d == nil {
// TODO: We could construct a FileDescriptor stub for standalone
// descriptors to satisfy the API.
return nil, nil
}
if _, ok := d.(protoreflect.FileDescriptor); ok {
break
}
}

fd := new(descriptorpb.FileDescriptorProto)
if err := proto.Unmarshal(b, fd); err != nil {
return nil, fmt.Errorf("malformed FileDescriptorProto: %v", err)
// Obtain the raw file descriptor.
var raw []byte
switch fd := d.(type) {
case interface{ ProtoLegacyRawDesc() []byte }:
raw = fd.ProtoLegacyRawDesc()
case protoreflect.FileDescriptor:
raw, _ = proto.Marshal(protodesc.ToFileDescriptorProto(fd))
}
file := protoimpl.X.CompressGZIP(raw)

return fd, nil
// Reverse the indexes, since we populated it in reverse.
for i, j := 0, len(idxs)-1; i < j; i, j = i+1, j-1 {
idxs[i], idxs[j] = idxs[j], idxs[i]
}

if v, ok := rawDescCache.LoadOrStore(origDesc, &rawDesc{file, idxs}); ok {
return v.(*rawDesc).fileDesc, v.(*rawDesc).indexes
}
return file, idxs
}

// Message is a proto.Message with a method to return its descriptor.
//
// Message types generated by the protocol compiler always satisfy
// the Message interface.
type Message interface {
proto.Message
Descriptor() ([]byte, []int)
// EnumRawDescriptorOf returns the GZIP'd raw file descriptor containing the
// enum and the index path to reach the enum declaration.
// The returned slices must not be mutated.
func EnumRawDescriptorOf(e GeneratedEnum) ([]byte, []int) {
if ev, ok := e.(interface{ EnumDescriptor() ([]byte, []int) }); ok {
return ev.EnumDescriptor()
}
ed := protoimpl.X.EnumTypeOf(e)
return deriveRawDescriptor(ed.Descriptor())
}

// MessageRawDescriptorOf returns the GZIP'd raw file descriptor containing
// the message and the index path to reach the message declaration.
// The returned slices must not be mutated.
func MessageRawDescriptorOf(m GeneratedMessage) ([]byte, []int) {
if mv, ok := m.(interface{ Descriptor() ([]byte, []int) }); ok {
return mv.Descriptor()
}
md := protoimpl.X.MessageTypeOf(m)
return deriveRawDescriptor(md.Descriptor())
}

// ForMessage returns a FileDescriptorProto and a DescriptorProto from within it
// describing the given message.
func ForMessage(msg Message) (fd *descriptorpb.FileDescriptorProto, md *descriptorpb.DescriptorProto) {
gz, path := msg.Descriptor()
fd, err := extractFile(gz)
var fileDescCache sync.Map // map[*byte]*descriptorpb.FileDescriptorProto

func deriveFileDescriptor(rawDesc []byte) *descriptorpb.FileDescriptorProto {
// Fast-path: check whether descriptor protos are already cached.
if v, ok := fileDescCache.Load(&rawDesc[0]); ok {
return v.(*descriptorpb.FileDescriptorProto)
}

// Slow-path: derive the descriptor proto from the GZIP'd message.
zr, err := gzip.NewReader(bytes.NewReader(rawDesc))
if err != nil {
panic(fmt.Sprintf("invalid FileDescriptorProto for %T: %v", msg, err))
panic(err)
}
b, err := ioutil.ReadAll(zr)
if err != nil {
panic(err)
}
fd := new(descriptorpb.FileDescriptorProto)
if err := proto.Unmarshal(b, fd); err != nil {
panic(err)
}
if v, ok := fileDescCache.LoadOrStore(&rawDesc[0], fd); ok {
return v.(*descriptorpb.FileDescriptorProto)
}
return fd
}

md = fd.MessageType[path[0]]
for _, i := range path[1:] {
// EnumDescriptorProtoOf returns the file descriptor proto containing
// the enum and the enum descriptor proto for the enum itself.
// The returned proto messages must not be mutated.
func EnumDescriptorProtoOf(e GeneratedEnum) (*descriptorpb.FileDescriptorProto, *descriptorpb.EnumDescriptorProto) {
rawDesc, idxs := EnumRawDescriptorOf(e)
if rawDesc == nil || idxs == nil {
return nil, nil
}
fd := deriveFileDescriptor(rawDesc)
if len(idxs) == 1 {
return fd, fd.EnumType[idxs[0]]
}
md := fd.MessageType[idxs[0]]
for _, i := range idxs[1 : len(idxs)-1] {
md = md.NestedType[i]
}
ed := md.EnumType[idxs[len(idxs)-1]]
return fd, ed
}

// MessageDescriptorProtoOf returns the file descriptor proto containing
// the message and the message descriptor proto for the message itself.
// The returned proto messages must not be mutated.
func MessageDescriptorProtoOf(m GeneratedMessage) (*descriptorpb.FileDescriptorProto, *descriptorpb.DescriptorProto) {
rawDesc, idxs := MessageRawDescriptorOf(m)
if rawDesc == nil || idxs == nil {
return nil, nil
}
fd := deriveFileDescriptor(rawDesc)
md := fd.MessageType[idxs[0]]
for _, i := range idxs[1:] {
md = md.NestedType[i]
}
return fd, md
Expand Down
85 changes: 85 additions & 0 deletions descriptor/descriptor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package descriptor

import (
"testing"

"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/reflect/protoreflect"

descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
)

func TestEnumDescriptorOf(t *testing.T) {
tests := []struct {
enum protoreflect.Enum
idxs []int
name string
}{{
enum: descpb.FieldDescriptorProto_Type(0),
idxs: []int{
new(descpb.FieldDescriptorProto).ProtoReflect().Descriptor().Index(),
new(descpb.FieldDescriptorProto_Type).Descriptor().Index(),
},
name: "Type",
}, {
enum: descpb.FieldOptions_CType(0),
idxs: []int{
new(descpb.FieldOptions).ProtoReflect().Descriptor().Index(),
new(descpb.FieldOptions_CType).Descriptor().Index(),
},
name: "CType",
}}

for _, tt := range tests {
e := struct{ protoreflect.Enum }{tt.enum} // v2-only enum

_, idxs := EnumRawDescriptorOf(e)
if diff := cmp.Diff(tt.idxs, idxs); diff != "" {
t.Errorf("path index mismatch (-want +got):\n%v", diff)
}

_, ed := EnumDescriptorProtoOf(e)
if ed.GetName() != tt.name {
t.Errorf("mismatching enum name: got %v, want %v", ed.GetName(), tt.name)
}
}
}

func TestMessageDescriptorOf(t *testing.T) {
tests := []struct {
message protoreflect.ProtoMessage
idxs []int
name string
}{{
message: (*descpb.SourceCodeInfo_Location)(nil),
idxs: []int{
new(descpb.SourceCodeInfo).ProtoReflect().Descriptor().Index(),
new(descpb.SourceCodeInfo_Location).ProtoReflect().Descriptor().Index(),
},
name: "Location",
}, {
message: (*descpb.FileDescriptorProto)(nil),
idxs: []int{
new(descpb.FileDescriptorProto).ProtoReflect().Descriptor().Index(),
},
name: "FileDescriptorProto",
}}

for _, tt := range tests {
m := struct{ protoreflect.ProtoMessage }{tt.message} // v2-only message

_, idxs := MessageRawDescriptorOf(m)
if diff := cmp.Diff(tt.idxs, idxs); diff != "" {
t.Errorf("path index mismatch (-want +got):\n%v", diff)
}

_, md := MessageDescriptorProtoOf(m)
if md.GetName() != tt.name {
t.Errorf("mismatching message name: got %v, want %v", md.GetName(), tt.name)
}
}
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ module github.com/golang/protobuf
go 1.9

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

0 comments on commit cc376d7

Please sign in to comment.