github.com/Big-big-orange/protoreflect@v0.0.0-20240408141420-285cedfdf6a4/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/Big-big-orange/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  }