khenaidoo | f333355 | 2021-12-15 16:52:31 -0500 | [diff] [blame] | 1 | package internal |
| 2 | |
| 3 | import ( |
| 4 | dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" |
| 5 | ) |
| 6 | |
| 7 | // SourceInfoMap is a map of paths in a descriptor to the corresponding source |
| 8 | // code info. |
| 9 | type SourceInfoMap map[string][]*dpb.SourceCodeInfo_Location |
| 10 | |
| 11 | // Get returns the source code info for the given path. If there are |
| 12 | // multiple locations for the same path, the first one is returned. |
| 13 | func (m SourceInfoMap) Get(path []int32) *dpb.SourceCodeInfo_Location { |
| 14 | v := m[asMapKey(path)] |
| 15 | if len(v) > 0 { |
| 16 | return v[0] |
| 17 | } |
| 18 | return nil |
| 19 | } |
| 20 | |
| 21 | // GetAll returns all source code info for the given path. |
| 22 | func (m SourceInfoMap) GetAll(path []int32) []*dpb.SourceCodeInfo_Location { |
| 23 | return m[asMapKey(path)] |
| 24 | } |
| 25 | |
| 26 | // Add stores the given source code info for the given path. |
| 27 | func (m SourceInfoMap) Add(path []int32, loc *dpb.SourceCodeInfo_Location) { |
| 28 | m[asMapKey(path)] = append(m[asMapKey(path)], loc) |
| 29 | } |
| 30 | |
| 31 | // PutIfAbsent stores the given source code info for the given path only if the |
| 32 | // given path does not exist in the map. This method returns true when the value |
| 33 | // is stored, false if the path already exists. |
| 34 | func (m SourceInfoMap) PutIfAbsent(path []int32, loc *dpb.SourceCodeInfo_Location) bool { |
| 35 | k := asMapKey(path) |
| 36 | if _, ok := m[k]; ok { |
| 37 | return false |
| 38 | } |
| 39 | m[k] = []*dpb.SourceCodeInfo_Location{loc} |
| 40 | return true |
| 41 | } |
| 42 | |
| 43 | func asMapKey(slice []int32) string { |
| 44 | // NB: arrays should be usable as map keys, but this does not |
| 45 | // work due to a bug: https://github.com/golang/go/issues/22605 |
| 46 | //rv := reflect.ValueOf(slice) |
| 47 | //arrayType := reflect.ArrayOf(rv.Len(), rv.Type().Elem()) |
| 48 | //array := reflect.New(arrayType).Elem() |
| 49 | //reflect.Copy(array, rv) |
| 50 | //return array.Interface() |
| 51 | |
| 52 | b := make([]byte, len(slice)*4) |
| 53 | j := 0 |
| 54 | for _, s := range slice { |
| 55 | b[j] = byte(s) |
| 56 | b[j+1] = byte(s >> 8) |
| 57 | b[j+2] = byte(s >> 16) |
| 58 | b[j+3] = byte(s >> 24) |
| 59 | j += 4 |
| 60 | } |
| 61 | return string(b) |
| 62 | } |
| 63 | |
| 64 | // CreateSourceInfoMap constructs a new SourceInfoMap and populates it with the |
| 65 | // source code info in the given file descriptor proto. |
| 66 | func CreateSourceInfoMap(fd *dpb.FileDescriptorProto) SourceInfoMap { |
| 67 | res := SourceInfoMap{} |
| 68 | PopulateSourceInfoMap(fd, res) |
| 69 | return res |
| 70 | } |
| 71 | |
| 72 | // PopulateSourceInfoMap populates the given SourceInfoMap with information from |
| 73 | // the given file descriptor. |
| 74 | func PopulateSourceInfoMap(fd *dpb.FileDescriptorProto, m SourceInfoMap) { |
| 75 | for _, l := range fd.GetSourceCodeInfo().GetLocation() { |
| 76 | m.Add(l.Path, l) |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | // NB: This wonkiness allows desc.Descriptor impl to implement an interface that |
| 81 | // is only usable from this package, by embedding a SourceInfoComputeFunc that |
| 82 | // implements the actual logic (which must live in desc package to avoid a |
| 83 | // dependency cycle). |
| 84 | |
| 85 | // SourceInfoComputer is a single method which will be invoked to recompute |
| 86 | // source info. This is needed for the protoparse package, which needs to link |
| 87 | // descriptors without source info in order to interpret options, but then needs |
| 88 | // to re-compute source info after that interpretation so that final linked |
| 89 | // descriptors expose the right info. |
| 90 | type SourceInfoComputer interface { |
| 91 | recomputeSourceInfo() |
| 92 | } |
| 93 | |
| 94 | // SourceInfoComputeFunc is the type that a desc.Descriptor will embed. It will |
| 95 | // be aliased in the desc package to an unexported name so it is not marked as |
| 96 | // an exported field in reflection and not present in Go docs. |
| 97 | type SourceInfoComputeFunc func() |
| 98 | |
| 99 | func (f SourceInfoComputeFunc) recomputeSourceInfo() { |
| 100 | f() |
| 101 | } |
| 102 | |
| 103 | // RecomputeSourceInfo is used to initiate recomputation of source info. This is |
| 104 | // is used by the protoparse package, after it interprets options. |
| 105 | func RecomputeSourceInfo(c SourceInfoComputer) { |
| 106 | c.recomputeSourceInfo() |
| 107 | } |