github.com/tinyhole/pblib@v0.0.0-20191001142211-98c63f19cf59/protoreflect/desc/convert.go (about)

     1  package desc
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
     9  
    10  	"github.com/tinyhole/pblib/protoreflect/desc/internal"
    11  	intn "github.com/tinyhole/pblib/protoreflect/internal"
    12  )
    13  
    14  // CreateFileDescriptor instantiates a new file descriptor for the given descriptor proto.
    15  // The file's direct dependencies must be provided. If the given dependencies do not include
    16  // all of the file's dependencies or if the contents of the descriptors are internally
    17  // inconsistent (e.g. contain unresolvable symbols) then an error is returned.
    18  func CreateFileDescriptor(fd *dpb.FileDescriptorProto, deps ...*FileDescriptor) (*FileDescriptor, error) {
    19  	return createFileDescriptor(fd, deps, nil)
    20  }
    21  
    22  func createFileDescriptor(fd *dpb.FileDescriptorProto, deps []*FileDescriptor, r *ImportResolver) (*FileDescriptor, error) {
    23  	ret := &FileDescriptor{
    24  		proto:      fd,
    25  		symbols:    map[string]Descriptor{},
    26  		fieldIndex: map[string]map[int32]*FieldDescriptor{},
    27  	}
    28  	pkg := fd.GetPackage()
    29  
    30  	// populate references to file descriptor dependencies
    31  	files := map[string]*FileDescriptor{}
    32  	for _, f := range deps {
    33  		files[f.proto.GetName()] = f
    34  	}
    35  	ret.deps = make([]*FileDescriptor, len(fd.GetDependency()))
    36  	for i, d := range fd.GetDependency() {
    37  		resolved := r.ResolveImport(fd.GetName(), d)
    38  		ret.deps[i] = files[resolved]
    39  		if ret.deps[i] == nil {
    40  			if resolved != d {
    41  				ret.deps[i] = files[d]
    42  			}
    43  			if ret.deps[i] == nil {
    44  				return nil, intn.ErrNoSuchFile(d)
    45  			}
    46  		}
    47  	}
    48  	ret.publicDeps = make([]*FileDescriptor, len(fd.GetPublicDependency()))
    49  	for i, pd := range fd.GetPublicDependency() {
    50  		ret.publicDeps[i] = ret.deps[pd]
    51  	}
    52  	ret.weakDeps = make([]*FileDescriptor, len(fd.GetWeakDependency()))
    53  	for i, wd := range fd.GetWeakDependency() {
    54  		ret.weakDeps[i] = ret.deps[wd]
    55  	}
    56  	ret.isProto3 = fd.GetSyntax() == "proto3"
    57  
    58  	// populate all tables of child descriptors
    59  	for _, m := range fd.GetMessageType() {
    60  		md, n := createMessageDescriptor(ret, ret, pkg, m, ret.symbols)
    61  		ret.symbols[n] = md
    62  		ret.messages = append(ret.messages, md)
    63  	}
    64  	for _, e := range fd.GetEnumType() {
    65  		ed, n := createEnumDescriptor(ret, ret, pkg, e, ret.symbols)
    66  		ret.symbols[n] = ed
    67  		ret.enums = append(ret.enums, ed)
    68  	}
    69  	for _, ex := range fd.GetExtension() {
    70  		exd, n := createFieldDescriptor(ret, ret, pkg, ex)
    71  		ret.symbols[n] = exd
    72  		ret.extensions = append(ret.extensions, exd)
    73  	}
    74  	for _, s := range fd.GetService() {
    75  		sd, n := createServiceDescriptor(ret, pkg, s, ret.symbols)
    76  		ret.symbols[n] = sd
    77  		ret.services = append(ret.services, sd)
    78  	}
    79  
    80  	ret.sourceInfo = internal.CreateSourceInfoMap(fd)
    81  	ret.sourceInfoRecomputeFunc = ret.recomputeSourceInfo
    82  
    83  	// now we can resolve all type references and source code info
    84  	scopes := []scope{fileScope(ret)}
    85  	path := make([]int32, 1, 8)
    86  	path[0] = internal.File_messagesTag
    87  	for i, md := range ret.messages {
    88  		if err := md.resolve(append(path, int32(i)), scopes); err != nil {
    89  			return nil, err
    90  		}
    91  	}
    92  	path[0] = internal.File_enumsTag
    93  	for i, ed := range ret.enums {
    94  		ed.resolve(append(path, int32(i)))
    95  	}
    96  	path[0] = internal.File_extensionsTag
    97  	for i, exd := range ret.extensions {
    98  		if err := exd.resolve(append(path, int32(i)), scopes); err != nil {
    99  			return nil, err
   100  		}
   101  	}
   102  	path[0] = internal.File_servicesTag
   103  	for i, sd := range ret.services {
   104  		if err := sd.resolve(append(path, int32(i)), scopes); err != nil {
   105  			return nil, err
   106  		}
   107  	}
   108  
   109  	return ret, nil
   110  }
   111  
   112  // CreateFileDescriptors constructs a set of descriptors, one for each of the
   113  // given descriptor protos. The given set of descriptor protos must include all
   114  // transitive dependencies for every file.
   115  func CreateFileDescriptors(fds []*dpb.FileDescriptorProto) (map[string]*FileDescriptor, error) {
   116  	return createFileDescriptors(fds, nil)
   117  }
   118  
   119  func createFileDescriptors(fds []*dpb.FileDescriptorProto, r *ImportResolver) (map[string]*FileDescriptor, error) {
   120  	if len(fds) == 0 {
   121  		return nil, nil
   122  	}
   123  	files := map[string]*dpb.FileDescriptorProto{}
   124  	resolved := map[string]*FileDescriptor{}
   125  	var name string
   126  	for _, fd := range fds {
   127  		name = fd.GetName()
   128  		files[name] = fd
   129  	}
   130  	for _, fd := range fds {
   131  		_, err := createFromSet(fd.GetName(), r, nil, files, resolved)
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  	}
   136  	return resolved, nil
   137  }
   138  
   139  // ToFileDescriptorSet creates a FileDescriptorSet proto that contains all of the given
   140  // file descriptors and their transitive dependencies. The files are topologically sorted
   141  // so that a file will always appear after its dependencies.
   142  func ToFileDescriptorSet(fds ...*FileDescriptor) *dpb.FileDescriptorSet {
   143  	var fdps []*dpb.FileDescriptorProto
   144  	addAllFiles(fds, &fdps, map[string]struct{}{})
   145  	return &dpb.FileDescriptorSet{File: fdps}
   146  }
   147  
   148  func addAllFiles(src []*FileDescriptor, results *[]*dpb.FileDescriptorProto, seen map[string]struct{}) {
   149  	for _, fd := range src {
   150  		if _, ok := seen[fd.GetName()]; ok {
   151  			continue
   152  		}
   153  		seen[fd.GetName()] = struct{}{}
   154  		addAllFiles(fd.GetDependencies(), results, seen)
   155  		*results = append(*results, fd.AsFileDescriptorProto())
   156  	}
   157  }
   158  
   159  // CreateFileDescriptorFromSet creates a descriptor from the given file descriptor set. The
   160  // set's *last* file will be the returned descriptor. The set's remaining files must comprise
   161  // the full set of transitive dependencies of that last file. This is the same format and
   162  // order used by protoc when emitting a FileDescriptorSet file with an invocation like so:
   163  //    protoc --descriptor_set_out=./test.protoset --include_imports -I. test.proto
   164  func CreateFileDescriptorFromSet(fds *dpb.FileDescriptorSet) (*FileDescriptor, error) {
   165  	return createFileDescriptorFromSet(fds, nil)
   166  }
   167  
   168  func createFileDescriptorFromSet(fds *dpb.FileDescriptorSet, r *ImportResolver) (*FileDescriptor, error) {
   169  	result, err := createFileDescriptorsFromSet(fds, r)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	files := fds.GetFile()
   174  	lastFilename := files[len(files)-1].GetName()
   175  	return result[lastFilename], nil
   176  }
   177  
   178  // CreateFileDescriptorsFromSet creates file descriptors from the given file descriptor set.
   179  // The returned map includes all files in the set, keyed b name. The set must include the
   180  // full set of transitive dependencies for all files therein or else a link error will occur
   181  // and be returned instead of the slice of descriptors. This is the same format used by
   182  // protoc when a FileDescriptorSet file with an invocation like so:
   183  //    protoc --descriptor_set_out=./test.protoset --include_imports -I. test.proto
   184  func CreateFileDescriptorsFromSet(fds *dpb.FileDescriptorSet) (map[string]*FileDescriptor, error) {
   185  	return createFileDescriptorsFromSet(fds, nil)
   186  }
   187  
   188  func createFileDescriptorsFromSet(fds *dpb.FileDescriptorSet, r *ImportResolver) (map[string]*FileDescriptor, error) {
   189  	files := fds.GetFile()
   190  	if len(files) == 0 {
   191  		return nil, errors.New("file descriptor set is empty")
   192  	}
   193  	return createFileDescriptors(files, r)
   194  }
   195  
   196  // createFromSet creates a descriptor for the given filename. It recursively
   197  // creates descriptors for the given file's dependencies.
   198  func createFromSet(filename string, r *ImportResolver, seen []string, files map[string]*dpb.FileDescriptorProto, resolved map[string]*FileDescriptor) (*FileDescriptor, error) {
   199  	for _, s := range seen {
   200  		if filename == s {
   201  			return nil, fmt.Errorf("cycle in imports: %s", strings.Join(append(seen, filename), " -> "))
   202  		}
   203  	}
   204  	seen = append(seen, filename)
   205  
   206  	if d, ok := resolved[filename]; ok {
   207  		return d, nil
   208  	}
   209  	fdp := files[filename]
   210  	if fdp == nil {
   211  		return nil, intn.ErrNoSuchFile(filename)
   212  	}
   213  	deps := make([]*FileDescriptor, len(fdp.GetDependency()))
   214  	for i, depName := range fdp.GetDependency() {
   215  		resolvedDep := r.ResolveImport(filename, depName)
   216  		dep, err := createFromSet(resolvedDep, r, seen, files, resolved)
   217  		if _, ok := err.(intn.ErrNoSuchFile); ok && resolvedDep != depName {
   218  			dep, err = createFromSet(depName, r, seen, files, resolved)
   219  		}
   220  		if err != nil {
   221  			return nil, err
   222  		}
   223  		deps[i] = dep
   224  	}
   225  	d, err := createFileDescriptor(fdp, deps, r)
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	resolved[filename] = d
   230  	return d, nil
   231  }