registry.go (10502B)
1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package proto 6 7 import ( 8 "bytes" 9 "compress/gzip" 10 "fmt" 11 "io/ioutil" 12 "reflect" 13 "strings" 14 "sync" 15 16 "google.golang.org/protobuf/reflect/protodesc" 17 "google.golang.org/protobuf/reflect/protoreflect" 18 "google.golang.org/protobuf/reflect/protoregistry" 19 "google.golang.org/protobuf/runtime/protoimpl" 20 ) 21 22 // filePath is the path to the proto source file. 23 type filePath = string // e.g., "google/protobuf/descriptor.proto" 24 25 // fileDescGZIP is the compressed contents of the encoded FileDescriptorProto. 26 type fileDescGZIP = []byte 27 28 var fileCache sync.Map // map[filePath]fileDescGZIP 29 30 // RegisterFile is called from generated code to register the compressed 31 // FileDescriptorProto with the file path for a proto source file. 32 // 33 // Deprecated: Use protoregistry.GlobalFiles.RegisterFile instead. 34 func RegisterFile(s filePath, d fileDescGZIP) { 35 // Decompress the descriptor. 36 zr, err := gzip.NewReader(bytes.NewReader(d)) 37 if err != nil { 38 panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err)) 39 } 40 b, err := ioutil.ReadAll(zr) 41 if err != nil { 42 panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err)) 43 } 44 45 // Construct a protoreflect.FileDescriptor from the raw descriptor. 46 // Note that DescBuilder.Build automatically registers the constructed 47 // file descriptor with the v2 registry. 48 protoimpl.DescBuilder{RawDescriptor: b}.Build() 49 50 // Locally cache the raw descriptor form for the file. 51 fileCache.Store(s, d) 52 } 53 54 // FileDescriptor returns the compressed FileDescriptorProto given the file path 55 // for a proto source file. It returns nil if not found. 56 // 57 // Deprecated: Use protoregistry.GlobalFiles.FindFileByPath instead. 58 func FileDescriptor(s filePath) fileDescGZIP { 59 if v, ok := fileCache.Load(s); ok { 60 return v.(fileDescGZIP) 61 } 62 63 // Find the descriptor in the v2 registry. 64 var b []byte 65 if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil { 66 b, _ = Marshal(protodesc.ToFileDescriptorProto(fd)) 67 } 68 69 // Locally cache the raw descriptor form for the file. 70 if len(b) > 0 { 71 v, _ := fileCache.LoadOrStore(s, protoimpl.X.CompressGZIP(b)) 72 return v.(fileDescGZIP) 73 } 74 return nil 75 } 76 77 // enumName is the name of an enum. For historical reasons, the enum name is 78 // neither the full Go name nor the full protobuf name of the enum. 79 // The name is the dot-separated combination of just the proto package that the 80 // enum is declared within followed by the Go type name of the generated enum. 81 type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum" 82 83 // enumsByName maps enum values by name to their numeric counterpart. 84 type enumsByName = map[string]int32 85 86 // enumsByNumber maps enum values by number to their name counterpart. 87 type enumsByNumber = map[int32]string 88 89 var enumCache sync.Map // map[enumName]enumsByName 90 var numFilesCache sync.Map // map[protoreflect.FullName]int 91 92 // RegisterEnum is called from the generated code to register the mapping of 93 // enum value names to enum numbers for the enum identified by s. 94 // 95 // Deprecated: Use protoregistry.GlobalTypes.RegisterEnum instead. 96 func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) { 97 if _, ok := enumCache.Load(s); ok { 98 panic("proto: duplicate enum registered: " + s) 99 } 100 enumCache.Store(s, m) 101 102 // This does not forward registration to the v2 registry since this API 103 // lacks sufficient information to construct a complete v2 enum descriptor. 104 } 105 106 // EnumValueMap returns the mapping from enum value names to enum numbers for 107 // the enum of the given name. It returns nil if not found. 108 // 109 // Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead. 110 func EnumValueMap(s enumName) enumsByName { 111 if v, ok := enumCache.Load(s); ok { 112 return v.(enumsByName) 113 } 114 115 // Check whether the cache is stale. If the number of files in the current 116 // package differs, then it means that some enums may have been recently 117 // registered upstream that we do not know about. 118 var protoPkg protoreflect.FullName 119 if i := strings.LastIndexByte(s, '.'); i >= 0 { 120 protoPkg = protoreflect.FullName(s[:i]) 121 } 122 v, _ := numFilesCache.Load(protoPkg) 123 numFiles, _ := v.(int) 124 if protoregistry.GlobalFiles.NumFilesByPackage(protoPkg) == numFiles { 125 return nil // cache is up-to-date; was not found earlier 126 } 127 128 // Update the enum cache for all enums declared in the given proto package. 129 numFiles = 0 130 protoregistry.GlobalFiles.RangeFilesByPackage(protoPkg, func(fd protoreflect.FileDescriptor) bool { 131 walkEnums(fd, func(ed protoreflect.EnumDescriptor) { 132 name := protoimpl.X.LegacyEnumName(ed) 133 if _, ok := enumCache.Load(name); !ok { 134 m := make(enumsByName) 135 evs := ed.Values() 136 for i := evs.Len() - 1; i >= 0; i-- { 137 ev := evs.Get(i) 138 m[string(ev.Name())] = int32(ev.Number()) 139 } 140 enumCache.LoadOrStore(name, m) 141 } 142 }) 143 numFiles++ 144 return true 145 }) 146 numFilesCache.Store(protoPkg, numFiles) 147 148 // Check cache again for enum map. 149 if v, ok := enumCache.Load(s); ok { 150 return v.(enumsByName) 151 } 152 return nil 153 } 154 155 // walkEnums recursively walks all enums declared in d. 156 func walkEnums(d interface { 157 Enums() protoreflect.EnumDescriptors 158 Messages() protoreflect.MessageDescriptors 159 }, f func(protoreflect.EnumDescriptor)) { 160 eds := d.Enums() 161 for i := eds.Len() - 1; i >= 0; i-- { 162 f(eds.Get(i)) 163 } 164 mds := d.Messages() 165 for i := mds.Len() - 1; i >= 0; i-- { 166 walkEnums(mds.Get(i), f) 167 } 168 } 169 170 // messageName is the full name of protobuf message. 171 type messageName = string 172 173 var messageTypeCache sync.Map // map[messageName]reflect.Type 174 175 // RegisterType is called from generated code to register the message Go type 176 // for a message of the given name. 177 // 178 // Deprecated: Use protoregistry.GlobalTypes.RegisterMessage instead. 179 func RegisterType(m Message, s messageName) { 180 mt := protoimpl.X.LegacyMessageTypeOf(m, protoreflect.FullName(s)) 181 if err := protoregistry.GlobalTypes.RegisterMessage(mt); err != nil { 182 panic(err) 183 } 184 messageTypeCache.Store(s, reflect.TypeOf(m)) 185 } 186 187 // RegisterMapType is called from generated code to register the Go map type 188 // for a protobuf message representing a map entry. 189 // 190 // Deprecated: Do not use. 191 func RegisterMapType(m interface{}, s messageName) { 192 t := reflect.TypeOf(m) 193 if t.Kind() != reflect.Map { 194 panic(fmt.Sprintf("invalid map kind: %v", t)) 195 } 196 if _, ok := messageTypeCache.Load(s); ok { 197 panic(fmt.Errorf("proto: duplicate proto message registered: %s", s)) 198 } 199 messageTypeCache.Store(s, t) 200 } 201 202 // MessageType returns the message type for a named message. 203 // It returns nil if not found. 204 // 205 // Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead. 206 func MessageType(s messageName) reflect.Type { 207 if v, ok := messageTypeCache.Load(s); ok { 208 return v.(reflect.Type) 209 } 210 211 // Derive the message type from the v2 registry. 212 var t reflect.Type 213 if mt, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(s)); mt != nil { 214 t = messageGoType(mt) 215 } 216 217 // If we could not get a concrete type, it is possible that it is a 218 // pseudo-message for a map entry. 219 if t == nil { 220 d, _ := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(s)) 221 if md, _ := d.(protoreflect.MessageDescriptor); md != nil && md.IsMapEntry() { 222 kt := goTypeForField(md.Fields().ByNumber(1)) 223 vt := goTypeForField(md.Fields().ByNumber(2)) 224 t = reflect.MapOf(kt, vt) 225 } 226 } 227 228 // Locally cache the message type for the given name. 229 if t != nil { 230 v, _ := messageTypeCache.LoadOrStore(s, t) 231 return v.(reflect.Type) 232 } 233 return nil 234 } 235 236 func goTypeForField(fd protoreflect.FieldDescriptor) reflect.Type { 237 switch k := fd.Kind(); k { 238 case protoreflect.EnumKind: 239 if et, _ := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()); et != nil { 240 return enumGoType(et) 241 } 242 return reflect.TypeOf(protoreflect.EnumNumber(0)) 243 case protoreflect.MessageKind, protoreflect.GroupKind: 244 if mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()); mt != nil { 245 return messageGoType(mt) 246 } 247 return reflect.TypeOf((*protoreflect.Message)(nil)).Elem() 248 default: 249 return reflect.TypeOf(fd.Default().Interface()) 250 } 251 } 252 253 func enumGoType(et protoreflect.EnumType) reflect.Type { 254 return reflect.TypeOf(et.New(0)) 255 } 256 257 func messageGoType(mt protoreflect.MessageType) reflect.Type { 258 return reflect.TypeOf(MessageV1(mt.Zero().Interface())) 259 } 260 261 // MessageName returns the full protobuf name for the given message type. 262 // 263 // Deprecated: Use protoreflect.MessageDescriptor.FullName instead. 264 func MessageName(m Message) messageName { 265 if m == nil { 266 return "" 267 } 268 if m, ok := m.(interface{ XXX_MessageName() messageName }); ok { 269 return m.XXX_MessageName() 270 } 271 return messageName(protoimpl.X.MessageDescriptorOf(m).FullName()) 272 } 273 274 // RegisterExtension is called from the generated code to register 275 // the extension descriptor. 276 // 277 // Deprecated: Use protoregistry.GlobalTypes.RegisterExtension instead. 278 func RegisterExtension(d *ExtensionDesc) { 279 if err := protoregistry.GlobalTypes.RegisterExtension(d); err != nil { 280 panic(err) 281 } 282 } 283 284 type extensionsByNumber = map[int32]*ExtensionDesc 285 286 var extensionCache sync.Map // map[messageName]extensionsByNumber 287 288 // RegisteredExtensions returns a map of the registered extensions for the 289 // provided protobuf message, indexed by the extension field number. 290 // 291 // Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead. 292 func RegisteredExtensions(m Message) extensionsByNumber { 293 // Check whether the cache is stale. If the number of extensions for 294 // the given message differs, then it means that some extensions were 295 // recently registered upstream that we do not know about. 296 s := MessageName(m) 297 v, _ := extensionCache.Load(s) 298 xs, _ := v.(extensionsByNumber) 299 if protoregistry.GlobalTypes.NumExtensionsByMessage(protoreflect.FullName(s)) == len(xs) { 300 return xs // cache is up-to-date 301 } 302 303 // Cache is stale, re-compute the extensions map. 304 xs = make(extensionsByNumber) 305 protoregistry.GlobalTypes.RangeExtensionsByMessage(protoreflect.FullName(s), func(xt protoreflect.ExtensionType) bool { 306 if xd, ok := xt.(*ExtensionDesc); ok { 307 xs[int32(xt.TypeDescriptor().Number())] = xd 308 } else { 309 // TODO: This implies that the protoreflect.ExtensionType is a 310 // custom type not generated by protoc-gen-go. We could try and 311 // convert the type to an ExtensionDesc. 312 } 313 return true 314 }) 315 extensionCache.Store(s, xs) 316 return xs 317 }