github.com/xiaoshude/protoreflect@v1.16.1-0.20220310024924-8c94d7247598/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/xiaoshude/protoreflect/desc/internal" 11 intn "github.com/xiaoshude/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 }