blob: 5f09273e999354c4df53cca4dc2bfb7ae79e371e [file] [log] [blame]
Joe Tsai90fe9962018-10-18 11:06:29 -07001// Copyright 2018 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
Joe Tsai08e00302018-11-26 22:32:06 -08005package legacy
Joe Tsai90fe9962018-10-18 11:06:29 -07006
7import (
8 "bytes"
9 "compress/gzip"
10 "io/ioutil"
11 "sync"
12
13 // TODO: Avoid reliance on old API. However, there is currently a
14 // chicken and egg problem where we need the descriptor protos to implement
15 // the new API.
16 protoV1 "github.com/golang/protobuf/proto"
17 descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
Joe Tsai08e00302018-11-26 22:32:06 -080018 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
Joe Tsai90fe9962018-10-18 11:06:29 -070019)
20
21// Every enum and message type generated by protoc-gen-go since commit 2fc053c5
22// on February 25th, 2016 has had a method to get the raw descriptor.
23// Types that were not generated by protoc-gen-go or were generated prior
24// to that version are not supported.
25//
26// The []byte returned is the encoded form of a FileDescriptorProto message
27// compressed using GZIP. The []int is the path from the top-level file
28// to the specific message or enum declaration.
29type (
Joe Tsai6dbffb72018-12-04 14:06:19 -080030 enumV1 interface {
Joe Tsai90fe9962018-10-18 11:06:29 -070031 EnumDescriptor() ([]byte, []int)
32 }
Joe Tsai6dbffb72018-12-04 14:06:19 -080033 messageV1 interface {
Joe Tsai90fe9962018-10-18 11:06:29 -070034 Descriptor() ([]byte, []int)
35 }
36)
37
38var fileDescCache sync.Map // map[*byte]*descriptorV1.FileDescriptorProto
39
Joe Tsai6dbffb72018-12-04 14:06:19 -080040// loadFileDesc unmarshals b as a compressed FileDescriptorProto message.
Joe Tsai90fe9962018-10-18 11:06:29 -070041//
42// This assumes that b is immutable and that b does not refer to part of a
43// concatenated series of GZIP files (which would require shenanigans that
44// rely on the concatenation properties of both protobufs and GZIP).
45// File descriptors generated by protoc-gen-go do not rely on that property.
Joe Tsai6dbffb72018-12-04 14:06:19 -080046func loadFileDesc(b []byte) *descriptorV1.FileDescriptorProto {
Joe Tsai90fe9962018-10-18 11:06:29 -070047 // Fast-path: check whether we already have a cached file descriptor.
48 if v, ok := fileDescCache.Load(&b[0]); ok {
49 return v.(*descriptorV1.FileDescriptorProto)
50 }
51
52 // Slow-path: decompress and unmarshal the file descriptor proto.
53 m := new(descriptorV1.FileDescriptorProto)
54 zr, err := gzip.NewReader(bytes.NewReader(b))
55 if err != nil {
56 panic(err)
57 }
58 b, err = ioutil.ReadAll(zr)
59 if err != nil {
60 panic(err)
61 }
62 // TODO: What about extensions?
63 // The protoV1 API does not eagerly unmarshal extensions.
64 if err := protoV1.Unmarshal(b, m); err != nil {
65 panic(err)
66 }
67 fileDescCache.Store(&b[0], m)
68 return m
69}
Joe Tsai08e00302018-11-26 22:32:06 -080070
71// parentFileDescriptor returns the parent protoreflect.FileDescriptor for the
72// provide descriptor. It returns nil if there is no parent.
73func parentFileDescriptor(d pref.Descriptor) pref.FileDescriptor {
74 for ok := true; ok; d, ok = d.Parent() {
75 if fd, _ := d.(pref.FileDescriptor); fd != nil {
76 return fd
77 }
78 }
79 return nil
80}