github.com/jhump/protoreflect@v1.16.0/desc/sourceinfo/locations.go (about) 1 package sourceinfo 2 3 import ( 4 "math" 5 "sync" 6 7 "google.golang.org/protobuf/reflect/protoreflect" 8 "google.golang.org/protobuf/types/descriptorpb" 9 10 "github.com/jhump/protoreflect/desc/internal" 11 ) 12 13 // NB: forked from google.golang.org/protobuf/internal/filedesc 14 type sourceLocations struct { 15 protoreflect.SourceLocations 16 17 orig []*descriptorpb.SourceCodeInfo_Location 18 // locs is a list of sourceLocations. 19 // The SourceLocation.Next field does not need to be populated 20 // as it will be lazily populated upon first need. 21 locs []protoreflect.SourceLocation 22 23 // fd is the parent file descriptor that these locations are relative to. 24 // If non-nil, ByDescriptor verifies that the provided descriptor 25 // is a child of this file descriptor. 26 fd protoreflect.FileDescriptor 27 28 once sync.Once 29 byPath map[pathKey]int 30 } 31 32 func (p *sourceLocations) Len() int { return len(p.orig) } 33 func (p *sourceLocations) Get(i int) protoreflect.SourceLocation { 34 return p.lazyInit().locs[i] 35 } 36 func (p *sourceLocations) byKey(k pathKey) protoreflect.SourceLocation { 37 if i, ok := p.lazyInit().byPath[k]; ok { 38 return p.locs[i] 39 } 40 return protoreflect.SourceLocation{} 41 } 42 func (p *sourceLocations) ByPath(path protoreflect.SourcePath) protoreflect.SourceLocation { 43 return p.byKey(newPathKey(path)) 44 } 45 func (p *sourceLocations) ByDescriptor(desc protoreflect.Descriptor) protoreflect.SourceLocation { 46 if p.fd != nil && desc != nil && p.fd != desc.ParentFile() { 47 return protoreflect.SourceLocation{} // mismatching parent imports 48 } 49 var pathArr [16]int32 50 path := pathArr[:0] 51 for { 52 switch desc.(type) { 53 case protoreflect.FileDescriptor: 54 // Reverse the path since it was constructed in reverse. 55 for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 { 56 path[i], path[j] = path[j], path[i] 57 } 58 return p.byKey(newPathKey(path)) 59 case protoreflect.MessageDescriptor: 60 path = append(path, int32(desc.Index())) 61 desc = desc.Parent() 62 switch desc.(type) { 63 case protoreflect.FileDescriptor: 64 path = append(path, int32(internal.File_messagesTag)) 65 case protoreflect.MessageDescriptor: 66 path = append(path, int32(internal.Message_nestedMessagesTag)) 67 default: 68 return protoreflect.SourceLocation{} 69 } 70 case protoreflect.FieldDescriptor: 71 isExtension := desc.(protoreflect.FieldDescriptor).IsExtension() 72 path = append(path, int32(desc.Index())) 73 desc = desc.Parent() 74 if isExtension { 75 switch desc.(type) { 76 case protoreflect.FileDescriptor: 77 path = append(path, int32(internal.File_extensionsTag)) 78 case protoreflect.MessageDescriptor: 79 path = append(path, int32(internal.Message_extensionsTag)) 80 default: 81 return protoreflect.SourceLocation{} 82 } 83 } else { 84 switch desc.(type) { 85 case protoreflect.MessageDescriptor: 86 path = append(path, int32(internal.Message_fieldsTag)) 87 default: 88 return protoreflect.SourceLocation{} 89 } 90 } 91 case protoreflect.OneofDescriptor: 92 path = append(path, int32(desc.Index())) 93 desc = desc.Parent() 94 switch desc.(type) { 95 case protoreflect.MessageDescriptor: 96 path = append(path, int32(internal.Message_oneOfsTag)) 97 default: 98 return protoreflect.SourceLocation{} 99 } 100 case protoreflect.EnumDescriptor: 101 path = append(path, int32(desc.Index())) 102 desc = desc.Parent() 103 switch desc.(type) { 104 case protoreflect.FileDescriptor: 105 path = append(path, int32(internal.File_enumsTag)) 106 case protoreflect.MessageDescriptor: 107 path = append(path, int32(internal.Message_enumsTag)) 108 default: 109 return protoreflect.SourceLocation{} 110 } 111 case protoreflect.EnumValueDescriptor: 112 path = append(path, int32(desc.Index())) 113 desc = desc.Parent() 114 switch desc.(type) { 115 case protoreflect.EnumDescriptor: 116 path = append(path, int32(internal.Enum_valuesTag)) 117 default: 118 return protoreflect.SourceLocation{} 119 } 120 case protoreflect.ServiceDescriptor: 121 path = append(path, int32(desc.Index())) 122 desc = desc.Parent() 123 switch desc.(type) { 124 case protoreflect.FileDescriptor: 125 path = append(path, int32(internal.File_servicesTag)) 126 default: 127 return protoreflect.SourceLocation{} 128 } 129 case protoreflect.MethodDescriptor: 130 path = append(path, int32(desc.Index())) 131 desc = desc.Parent() 132 switch desc.(type) { 133 case protoreflect.ServiceDescriptor: 134 path = append(path, int32(internal.Service_methodsTag)) 135 default: 136 return protoreflect.SourceLocation{} 137 } 138 default: 139 return protoreflect.SourceLocation{} 140 } 141 } 142 } 143 func (p *sourceLocations) lazyInit() *sourceLocations { 144 p.once.Do(func() { 145 if len(p.orig) > 0 { 146 p.locs = make([]protoreflect.SourceLocation, len(p.orig)) 147 // Collect all the indexes for a given path. 148 pathIdxs := make(map[pathKey][]int, len(p.locs)) 149 for i := range p.orig { 150 l := asSourceLocation(p.orig[i]) 151 p.locs[i] = l 152 k := newPathKey(l.Path) 153 pathIdxs[k] = append(pathIdxs[k], i) 154 } 155 156 // Update the next index for all locations. 157 p.byPath = make(map[pathKey]int, len(p.locs)) 158 for k, idxs := range pathIdxs { 159 for i := 0; i < len(idxs)-1; i++ { 160 p.locs[idxs[i]].Next = idxs[i+1] 161 } 162 p.locs[idxs[len(idxs)-1]].Next = 0 163 p.byPath[k] = idxs[0] // record the first location for this path 164 } 165 } 166 }) 167 return p 168 } 169 170 func asSourceLocation(l *descriptorpb.SourceCodeInfo_Location) protoreflect.SourceLocation { 171 endLine := l.Span[0] 172 endCol := l.Span[2] 173 if len(l.Span) > 3 { 174 endLine = l.Span[2] 175 endCol = l.Span[3] 176 } 177 return protoreflect.SourceLocation{ 178 Path: l.Path, 179 StartLine: int(l.Span[0]), 180 StartColumn: int(l.Span[1]), 181 EndLine: int(endLine), 182 EndColumn: int(endCol), 183 LeadingDetachedComments: l.LeadingDetachedComments, 184 LeadingComments: l.GetLeadingComments(), 185 TrailingComments: l.GetTrailingComments(), 186 } 187 } 188 189 // pathKey is a comparable representation of protoreflect.SourcePath. 190 type pathKey struct { 191 arr [16]uint8 // first n-1 path segments; last element is the length 192 str string // used if the path does not fit in arr 193 } 194 195 func newPathKey(p protoreflect.SourcePath) (k pathKey) { 196 if len(p) < len(k.arr) { 197 for i, ps := range p { 198 if ps < 0 || math.MaxUint8 <= ps { 199 return pathKey{str: p.String()} 200 } 201 k.arr[i] = uint8(ps) 202 } 203 k.arr[len(k.arr)-1] = uint8(len(p)) 204 return k 205 } 206 return pathKey{str: p.String()} 207 }