| package internal |
| |
| import ( |
| dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" |
| ) |
| |
| // SourceInfoMap is a map of paths in a descriptor to the corresponding source |
| // code info. |
| type SourceInfoMap map[string][]*dpb.SourceCodeInfo_Location |
| |
| // Get returns the source code info for the given path. If there are |
| // multiple locations for the same path, the first one is returned. |
| func (m SourceInfoMap) Get(path []int32) *dpb.SourceCodeInfo_Location { |
| v := m[asMapKey(path)] |
| if len(v) > 0 { |
| return v[0] |
| } |
| return nil |
| } |
| |
| // GetAll returns all source code info for the given path. |
| func (m SourceInfoMap) GetAll(path []int32) []*dpb.SourceCodeInfo_Location { |
| return m[asMapKey(path)] |
| } |
| |
| // Add stores the given source code info for the given path. |
| func (m SourceInfoMap) Add(path []int32, loc *dpb.SourceCodeInfo_Location) { |
| m[asMapKey(path)] = append(m[asMapKey(path)], loc) |
| } |
| |
| // PutIfAbsent stores the given source code info for the given path only if the |
| // given path does not exist in the map. This method returns true when the value |
| // is stored, false if the path already exists. |
| func (m SourceInfoMap) PutIfAbsent(path []int32, loc *dpb.SourceCodeInfo_Location) bool { |
| k := asMapKey(path) |
| if _, ok := m[k]; ok { |
| return false |
| } |
| m[k] = []*dpb.SourceCodeInfo_Location{loc} |
| return true |
| } |
| |
| func asMapKey(slice []int32) string { |
| // NB: arrays should be usable as map keys, but this does not |
| // work due to a bug: https://github.com/golang/go/issues/22605 |
| //rv := reflect.ValueOf(slice) |
| //arrayType := reflect.ArrayOf(rv.Len(), rv.Type().Elem()) |
| //array := reflect.New(arrayType).Elem() |
| //reflect.Copy(array, rv) |
| //return array.Interface() |
| |
| b := make([]byte, len(slice)*4) |
| j := 0 |
| for _, s := range slice { |
| b[j] = byte(s) |
| b[j+1] = byte(s >> 8) |
| b[j+2] = byte(s >> 16) |
| b[j+3] = byte(s >> 24) |
| j += 4 |
| } |
| return string(b) |
| } |
| |
| // CreateSourceInfoMap constructs a new SourceInfoMap and populates it with the |
| // source code info in the given file descriptor proto. |
| func CreateSourceInfoMap(fd *dpb.FileDescriptorProto) SourceInfoMap { |
| res := SourceInfoMap{} |
| PopulateSourceInfoMap(fd, res) |
| return res |
| } |
| |
| // PopulateSourceInfoMap populates the given SourceInfoMap with information from |
| // the given file descriptor. |
| func PopulateSourceInfoMap(fd *dpb.FileDescriptorProto, m SourceInfoMap) { |
| for _, l := range fd.GetSourceCodeInfo().GetLocation() { |
| m.Add(l.Path, l) |
| } |
| } |
| |
| // NB: This wonkiness allows desc.Descriptor impl to implement an interface that |
| // is only usable from this package, by embedding a SourceInfoComputeFunc that |
| // implements the actual logic (which must live in desc package to avoid a |
| // dependency cycle). |
| |
| // SourceInfoComputer is a single method which will be invoked to recompute |
| // source info. This is needed for the protoparse package, which needs to link |
| // descriptors without source info in order to interpret options, but then needs |
| // to re-compute source info after that interpretation so that final linked |
| // descriptors expose the right info. |
| type SourceInfoComputer interface { |
| recomputeSourceInfo() |
| } |
| |
| // SourceInfoComputeFunc is the type that a desc.Descriptor will embed. It will |
| // be aliased in the desc package to an unexported name so it is not marked as |
| // an exported field in reflection and not present in Go docs. |
| type SourceInfoComputeFunc func() |
| |
| func (f SourceInfoComputeFunc) recomputeSourceInfo() { |
| f() |
| } |
| |
| // RecomputeSourceInfo is used to initiate recomputation of source info. This is |
| // is used by the protoparse package, after it interprets options. |
| func RecomputeSourceInfo(c SourceInfoComputer) { |
| c.recomputeSourceInfo() |
| } |