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 }