github.com/Big-big-orange/protoreflect@v0.0.0-20240408141420-285cedfdf6a4/desc/convert.go (about)

     1  package desc
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"google.golang.org/protobuf/reflect/protodesc"
     9  	"google.golang.org/protobuf/reflect/protoreflect"
    10  	"google.golang.org/protobuf/reflect/protoregistry"
    11  	"google.golang.org/protobuf/types/descriptorpb"
    12  
    13  	"github.com/Big-big-orange/protoreflect/desc/internal"
    14  	intn "github.com/Big-big-orange/protoreflect/internal"
    15  )
    16  
    17  // CreateFileDescriptor instantiates a new file descriptor for the given descriptor proto.
    18  // The file's direct dependencies must be provided. If the given dependencies do not include
    19  // all of the file's dependencies or if the contents of the descriptors are internally
    20  // inconsistent (e.g. contain unresolvable symbols) then an error is returned.
    21  func CreateFileDescriptor(fd *descriptorpb.FileDescriptorProto, deps ...*FileDescriptor) (*FileDescriptor, error) {
    22  	return createFileDescriptor(fd, deps, nil)
    23  }
    24  
    25  type descResolver struct {
    26  	files          []*FileDescriptor
    27  	importResolver *ImportResolver
    28  	fromPath       string
    29  }
    30  
    31  func (r *descResolver) FindFileByPath(path string) (protoreflect.FileDescriptor, error) {
    32  	resolvedPath := r.importResolver.ResolveImport(r.fromPath, path)
    33  	d := r.findFileByPath(resolvedPath)
    34  	if d != nil {
    35  		return d, nil
    36  	}
    37  	if resolvedPath != path {
    38  		d := r.findFileByPath(path)
    39  		if d != nil {
    40  			return d, nil
    41  		}
    42  	}
    43  	return nil, protoregistry.NotFound
    44  }
    45  
    46  func (r *descResolver) findFileByPath(path string) protoreflect.FileDescriptor {
    47  	for _, fd := range r.files {
    48  		if fd.GetName() == path {
    49  			return fd.UnwrapFile()
    50  		}
    51  	}
    52  	return nil
    53  }
    54  
    55  func (r *descResolver) FindDescriptorByName(n protoreflect.FullName) (protoreflect.Descriptor, error) {
    56  	for _, fd := range r.files {
    57  		d := fd.FindSymbol(string(n))
    58  		if d != nil {
    59  			return d.(DescriptorWrapper).Unwrap(), nil
    60  		}
    61  	}
    62  	return nil, protoregistry.NotFound
    63  }
    64  
    65  func createFileDescriptor(fd *descriptorpb.FileDescriptorProto, deps []*FileDescriptor, r *ImportResolver) (*FileDescriptor, error) {
    66  	dr := &descResolver{files: deps, importResolver: r, fromPath: fd.GetName()}
    67  	d, err := protodesc.NewFile(fd, dr)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	// make sure cache has dependencies populated
    73  	cache := mapCache{}
    74  	for _, dep := range deps {
    75  		fd, err := dr.FindFileByPath(dep.GetName())
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		cache.put(fd, dep)
    80  	}
    81  
    82  	return convertFile(d, fd, cache)
    83  }
    84  
    85  func convertFile(d protoreflect.FileDescriptor, fd *descriptorpb.FileDescriptorProto, cache descriptorCache) (*FileDescriptor, error) {
    86  	ret := &FileDescriptor{
    87  		wrapped:    d,
    88  		proto:      fd,
    89  		symbols:    map[string]Descriptor{},
    90  		fieldIndex: map[string]map[int32]*FieldDescriptor{},
    91  	}
    92  	cache.put(d, ret)
    93  
    94  	// populate references to file descriptor dependencies
    95  	ret.deps = make([]*FileDescriptor, len(fd.GetDependency()))
    96  	for i := 0; i < d.Imports().Len(); i++ {
    97  		f := d.Imports().Get(i).FileDescriptor
    98  		if c := cache.get(f); c != nil {
    99  			ret.deps[i] = c.(*FileDescriptor)
   100  		} else if c, err := wrapFile(f, cache); err != nil {
   101  			return nil, err
   102  		} else {
   103  			ret.deps[i] = c
   104  		}
   105  	}
   106  	ret.publicDeps = make([]*FileDescriptor, len(fd.GetPublicDependency()))
   107  	for i, pd := range fd.GetPublicDependency() {
   108  		ret.publicDeps[i] = ret.deps[pd]
   109  	}
   110  	ret.weakDeps = make([]*FileDescriptor, len(fd.GetWeakDependency()))
   111  	for i, wd := range fd.GetWeakDependency() {
   112  		ret.weakDeps[i] = ret.deps[wd]
   113  	}
   114  
   115  	// populate all tables of child descriptors
   116  	path := make([]int32, 1, 8)
   117  	path[0] = internal.File_messagesTag
   118  	for i := 0; i < d.Messages().Len(); i++ {
   119  		src := d.Messages().Get(i)
   120  		srcProto := fd.GetMessageType()[src.Index()]
   121  		md := createMessageDescriptor(ret, ret, src, srcProto, ret.symbols, cache, append(path, int32(i)))
   122  		ret.symbols[string(src.FullName())] = md
   123  		ret.messages = append(ret.messages, md)
   124  	}
   125  	path[0] = internal.File_enumsTag
   126  	for i := 0; i < d.Enums().Len(); i++ {
   127  		src := d.Enums().Get(i)
   128  		srcProto := fd.GetEnumType()[src.Index()]
   129  		ed := createEnumDescriptor(ret, ret, src, srcProto, ret.symbols, cache, append(path, int32(i)))
   130  		ret.symbols[string(src.FullName())] = ed
   131  		ret.enums = append(ret.enums, ed)
   132  	}
   133  	path[0] = internal.File_extensionsTag
   134  	for i := 0; i < d.Extensions().Len(); i++ {
   135  		src := d.Extensions().Get(i)
   136  		srcProto := fd.GetExtension()[src.Index()]
   137  		exd := createFieldDescriptor(ret, ret, src, srcProto, cache, append(path, int32(i)))
   138  		ret.symbols[string(src.FullName())] = exd
   139  		ret.extensions = append(ret.extensions, exd)
   140  	}
   141  	path[0] = internal.File_servicesTag
   142  	for i := 0; i < d.Services().Len(); i++ {
   143  		src := d.Services().Get(i)
   144  		srcProto := fd.GetService()[src.Index()]
   145  		sd := createServiceDescriptor(ret, src, srcProto, ret.symbols, append(path, int32(i)))
   146  		ret.symbols[string(src.FullName())] = sd
   147  		ret.services = append(ret.services, sd)
   148  	}
   149  
   150  	ret.sourceInfo = internal.CreateSourceInfoMap(fd)
   151  	ret.sourceInfoRecomputeFunc = ret.recomputeSourceInfo
   152  
   153  	// now we can resolve all type references and source code info
   154  	for _, md := range ret.messages {
   155  		if err := md.resolve(cache); err != nil {
   156  			return nil, err
   157  		}
   158  	}
   159  	path[0] = internal.File_extensionsTag
   160  	for _, exd := range ret.extensions {
   161  		if err := exd.resolve(cache); err != nil {
   162  			return nil, err
   163  		}
   164  	}
   165  	path[0] = internal.File_servicesTag
   166  	for _, sd := range ret.services {
   167  		if err := sd.resolve(cache); err != nil {
   168  			return nil, err
   169  		}
   170  	}
   171  
   172  	return ret, nil
   173  }
   174  
   175  // CreateFileDescriptors constructs a set of descriptors, one for each of the
   176  // given descriptor protos. The given set of descriptor protos must include all
   177  // transitive dependencies for every file.
   178  func CreateFileDescriptors(fds []*descriptorpb.FileDescriptorProto) (map[string]*FileDescriptor, error) {
   179  	return createFileDescriptors(fds, nil)
   180  }
   181  
   182  func createFileDescriptors(fds []*descriptorpb.FileDescriptorProto, r *ImportResolver) (map[string]*FileDescriptor, error) {
   183  	if len(fds) == 0 {
   184  		return nil, nil
   185  	}
   186  	files := map[string]*descriptorpb.FileDescriptorProto{}
   187  	resolved := map[string]*FileDescriptor{}
   188  	var name string
   189  	for _, fd := range fds {
   190  		name = fd.GetName()
   191  		files[name] = fd
   192  	}
   193  	for _, fd := range fds {
   194  		_, err := createFromSet(fd.GetName(), r, nil, files, resolved)
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  	}
   199  	return resolved, nil
   200  }
   201  
   202  // ToFileDescriptorSet creates a FileDescriptorSet proto that contains all of the given
   203  // file descriptors and their transitive dependencies. The files are topologically sorted
   204  // so that a file will always appear after its dependencies.
   205  func ToFileDescriptorSet(fds ...*FileDescriptor) *descriptorpb.FileDescriptorSet {
   206  	var fdps []*descriptorpb.FileDescriptorProto
   207  	addAllFiles(fds, &fdps, map[string]struct{}{})
   208  	return &descriptorpb.FileDescriptorSet{File: fdps}
   209  }
   210  
   211  func addAllFiles(src []*FileDescriptor, results *[]*descriptorpb.FileDescriptorProto, seen map[string]struct{}) {
   212  	for _, fd := range src {
   213  		if _, ok := seen[fd.GetName()]; ok {
   214  			continue
   215  		}
   216  		seen[fd.GetName()] = struct{}{}
   217  		addAllFiles(fd.GetDependencies(), results, seen)
   218  		*results = append(*results, fd.AsFileDescriptorProto())
   219  	}
   220  }
   221  
   222  // CreateFileDescriptorFromSet creates a descriptor from the given file descriptor set. The
   223  // set's *last* file will be the returned descriptor. The set's remaining files must comprise
   224  // the full set of transitive dependencies of that last file. This is the same format and
   225  // order used by protoc when emitting a FileDescriptorSet file with an invocation like so:
   226  //
   227  //	protoc --descriptor_set_out=./test.protoset --include_imports -I. test.proto
   228  func CreateFileDescriptorFromSet(fds *descriptorpb.FileDescriptorSet) (*FileDescriptor, error) {
   229  	return createFileDescriptorFromSet(fds, nil)
   230  }
   231  
   232  func createFileDescriptorFromSet(fds *descriptorpb.FileDescriptorSet, r *ImportResolver) (*FileDescriptor, error) {
   233  	result, err := createFileDescriptorsFromSet(fds, r)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	files := fds.GetFile()
   238  	lastFilename := files[len(files)-1].GetName()
   239  	return result[lastFilename], nil
   240  }
   241  
   242  // CreateFileDescriptorsFromSet creates file descriptors from the given file descriptor set.
   243  // The returned map includes all files in the set, keyed b name. The set must include the
   244  // full set of transitive dependencies for all files therein or else a link error will occur
   245  // and be returned instead of the slice of descriptors. This is the same format used by
   246  // protoc when a FileDescriptorSet file with an invocation like so:
   247  //
   248  //	protoc --descriptor_set_out=./test.protoset --include_imports -I. test.proto
   249  func CreateFileDescriptorsFromSet(fds *descriptorpb.FileDescriptorSet) (map[string]*FileDescriptor, error) {
   250  	return createFileDescriptorsFromSet(fds, nil)
   251  }
   252  
   253  func createFileDescriptorsFromSet(fds *descriptorpb.FileDescriptorSet, r *ImportResolver) (map[string]*FileDescriptor, error) {
   254  	files := fds.GetFile()
   255  	if len(files) == 0 {
   256  		return nil, errors.New("file descriptor set is empty")
   257  	}
   258  	return createFileDescriptors(files, r)
   259  }
   260  
   261  // createFromSet creates a descriptor for the given filename. It recursively
   262  // creates descriptors for the given file's dependencies.
   263  func createFromSet(filename string, r *ImportResolver, seen []string, files map[string]*descriptorpb.FileDescriptorProto, resolved map[string]*FileDescriptor) (*FileDescriptor, error) {
   264  	for _, s := range seen {
   265  		if filename == s {
   266  			return nil, fmt.Errorf("cycle in imports: %s", strings.Join(append(seen, filename), " -> "))
   267  		}
   268  	}
   269  	seen = append(seen, filename)
   270  
   271  	if d, ok := resolved[filename]; ok {
   272  		return d, nil
   273  	}
   274  	fdp := files[filename]
   275  	if fdp == nil {
   276  		return nil, intn.ErrNoSuchFile(filename)
   277  	}
   278  	deps := make([]*FileDescriptor, len(fdp.GetDependency()))
   279  	for i, depName := range fdp.GetDependency() {
   280  		resolvedDep := r.ResolveImport(filename, depName)
   281  		dep, err := createFromSet(resolvedDep, r, seen, files, resolved)
   282  		if _, ok := err.(intn.ErrNoSuchFile); ok && resolvedDep != depName {
   283  			dep, err = createFromSet(depName, r, seen, files, resolved)
   284  		}
   285  		if err != nil {
   286  			return nil, err
   287  		}
   288  		deps[i] = dep
   289  	}
   290  	d, err := createFileDescriptor(fdp, deps, r)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  	resolved[filename] = d
   295  	return d, nil
   296  }