vendor/google.golang.org/protobuf/internal/filetype/build.go
changeset 256 6d9efbef00a9
child 260 445e01aede7e
equal deleted inserted replaced
255:4f153a23adab 256:6d9efbef00a9
       
     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 filetype provides functionality for wrapping descriptors
       
     6 // with Go type information.
       
     7 package filetype
       
     8 
       
     9 import (
       
    10 	"reflect"
       
    11 
       
    12 	"google.golang.org/protobuf/internal/descopts"
       
    13 	fdesc "google.golang.org/protobuf/internal/filedesc"
       
    14 	pimpl "google.golang.org/protobuf/internal/impl"
       
    15 	pref "google.golang.org/protobuf/reflect/protoreflect"
       
    16 	preg "google.golang.org/protobuf/reflect/protoregistry"
       
    17 )
       
    18 
       
    19 // Builder constructs type descriptors from a raw file descriptor
       
    20 // and associated Go types for each enum and message declaration.
       
    21 //
       
    22 //
       
    23 // Flattened Ordering
       
    24 //
       
    25 // The protobuf type system represents declarations as a tree. Certain nodes in
       
    26 // the tree require us to either associate it with a concrete Go type or to
       
    27 // resolve a dependency, which is information that must be provided separately
       
    28 // since it cannot be derived from the file descriptor alone.
       
    29 //
       
    30 // However, representing a tree as Go literals is difficult to simply do in a
       
    31 // space and time efficient way. Thus, we store them as a flattened list of
       
    32 // objects where the serialization order from the tree-based form is important.
       
    33 //
       
    34 // The "flattened ordering" is defined as a tree traversal of all enum, message,
       
    35 // extension, and service declarations using the following algorithm:
       
    36 //
       
    37 //	def VisitFileDecls(fd):
       
    38 //		for e in fd.Enums:      yield e
       
    39 //		for m in fd.Messages:   yield m
       
    40 //		for x in fd.Extensions: yield x
       
    41 //		for s in fd.Services:   yield s
       
    42 //		for m in fd.Messages:   yield from VisitMessageDecls(m)
       
    43 //
       
    44 //	def VisitMessageDecls(md):
       
    45 //		for e in md.Enums:      yield e
       
    46 //		for m in md.Messages:   yield m
       
    47 //		for x in md.Extensions: yield x
       
    48 //		for m in md.Messages:   yield from VisitMessageDecls(m)
       
    49 //
       
    50 // The traversal starts at the root file descriptor and yields each direct
       
    51 // declaration within each node before traversing into sub-declarations
       
    52 // that children themselves may have.
       
    53 type Builder struct {
       
    54 	// File is the underlying file descriptor builder.
       
    55 	File fdesc.Builder
       
    56 
       
    57 	// GoTypes is a unique set of the Go types for all declarations and
       
    58 	// dependencies. Each type is represented as a zero value of the Go type.
       
    59 	//
       
    60 	// Declarations are Go types generated for enums and messages directly
       
    61 	// declared (not publicly imported) in the proto source file.
       
    62 	// Messages for map entries are accounted for, but represented by nil.
       
    63 	// Enum declarations in "flattened ordering" come first, followed by
       
    64 	// message declarations in "flattened ordering".
       
    65 	//
       
    66 	// Dependencies are Go types for enums or messages referenced by
       
    67 	// message fields (excluding weak fields), for parent extended messages of
       
    68 	// extension fields, for enums or messages referenced by extension fields,
       
    69 	// and for input and output messages referenced by service methods.
       
    70 	// Dependencies must come after declarations, but the ordering of
       
    71 	// dependencies themselves is unspecified.
       
    72 	GoTypes []interface{}
       
    73 
       
    74 	// DependencyIndexes is an ordered list of indexes into GoTypes for the
       
    75 	// dependencies of messages, extensions, or services.
       
    76 	//
       
    77 	// There are 5 sub-lists in "flattened ordering" concatenated back-to-back:
       
    78 	//	0. Message field dependencies: list of the enum or message type
       
    79 	//	referred to by every message field.
       
    80 	//	1. Extension field targets: list of the extended parent message of
       
    81 	//	every extension.
       
    82 	//	2. Extension field dependencies: list of the enum or message type
       
    83 	//	referred to by every extension field.
       
    84 	//	3. Service method inputs: list of the input message type
       
    85 	//	referred to by every service method.
       
    86 	//	4. Service method outputs: list of the output message type
       
    87 	//	referred to by every service method.
       
    88 	//
       
    89 	// The offset into DependencyIndexes for the start of each sub-list
       
    90 	// is appended to the end in reverse order.
       
    91 	DependencyIndexes []int32
       
    92 
       
    93 	// EnumInfos is a list of enum infos in "flattened ordering".
       
    94 	EnumInfos []pimpl.EnumInfo
       
    95 
       
    96 	// MessageInfos is a list of message infos in "flattened ordering".
       
    97 	// If provided, the GoType and PBType for each element is populated.
       
    98 	//
       
    99 	// Requirement: len(MessageInfos) == len(Build.Messages)
       
   100 	MessageInfos []pimpl.MessageInfo
       
   101 
       
   102 	// ExtensionInfos is a list of extension infos in "flattened ordering".
       
   103 	// Each element is initialized and registered with the protoregistry package.
       
   104 	//
       
   105 	// Requirement: len(LegacyExtensions) == len(Build.Extensions)
       
   106 	ExtensionInfos []pimpl.ExtensionInfo
       
   107 
       
   108 	// TypeRegistry is the registry to register each type descriptor.
       
   109 	// If nil, it uses protoregistry.GlobalTypes.
       
   110 	TypeRegistry interface {
       
   111 		RegisterMessage(pref.MessageType) error
       
   112 		RegisterEnum(pref.EnumType) error
       
   113 		RegisterExtension(pref.ExtensionType) error
       
   114 	}
       
   115 }
       
   116 
       
   117 // Out is the output of the builder.
       
   118 type Out struct {
       
   119 	File pref.FileDescriptor
       
   120 }
       
   121 
       
   122 func (tb Builder) Build() (out Out) {
       
   123 	// Replace the resolver with one that resolves dependencies by index,
       
   124 	// which is faster and more reliable than relying on the global registry.
       
   125 	if tb.File.FileRegistry == nil {
       
   126 		tb.File.FileRegistry = preg.GlobalFiles
       
   127 	}
       
   128 	tb.File.FileRegistry = &resolverByIndex{
       
   129 		goTypes:      tb.GoTypes,
       
   130 		depIdxs:      tb.DependencyIndexes,
       
   131 		fileRegistry: tb.File.FileRegistry,
       
   132 	}
       
   133 
       
   134 	// Initialize registry if unpopulated.
       
   135 	if tb.TypeRegistry == nil {
       
   136 		tb.TypeRegistry = preg.GlobalTypes
       
   137 	}
       
   138 
       
   139 	fbOut := tb.File.Build()
       
   140 	out.File = fbOut.File
       
   141 
       
   142 	// Process enums.
       
   143 	enumGoTypes := tb.GoTypes[:len(fbOut.Enums)]
       
   144 	if len(tb.EnumInfos) != len(fbOut.Enums) {
       
   145 		panic("mismatching enum lengths")
       
   146 	}
       
   147 	if len(fbOut.Enums) > 0 {
       
   148 		for i := range fbOut.Enums {
       
   149 			tb.EnumInfos[i] = pimpl.EnumInfo{
       
   150 				GoReflectType: reflect.TypeOf(enumGoTypes[i]),
       
   151 				Desc:          &fbOut.Enums[i],
       
   152 			}
       
   153 			// Register enum types.
       
   154 			if err := tb.TypeRegistry.RegisterEnum(&tb.EnumInfos[i]); err != nil {
       
   155 				panic(err)
       
   156 			}
       
   157 		}
       
   158 	}
       
   159 
       
   160 	// Process messages.
       
   161 	messageGoTypes := tb.GoTypes[len(fbOut.Enums):][:len(fbOut.Messages)]
       
   162 	if len(tb.MessageInfos) != len(fbOut.Messages) {
       
   163 		panic("mismatching message lengths")
       
   164 	}
       
   165 	if len(fbOut.Messages) > 0 {
       
   166 		for i := range fbOut.Messages {
       
   167 			if messageGoTypes[i] == nil {
       
   168 				continue // skip map entry
       
   169 			}
       
   170 
       
   171 			tb.MessageInfos[i].GoReflectType = reflect.TypeOf(messageGoTypes[i])
       
   172 			tb.MessageInfos[i].Desc = &fbOut.Messages[i]
       
   173 
       
   174 			// Register message types.
       
   175 			if err := tb.TypeRegistry.RegisterMessage(&tb.MessageInfos[i]); err != nil {
       
   176 				panic(err)
       
   177 			}
       
   178 		}
       
   179 
       
   180 		// As a special-case for descriptor.proto,
       
   181 		// locally register concrete message type for the options.
       
   182 		if out.File.Path() == "google/protobuf/descriptor.proto" && out.File.Package() == "google.protobuf" {
       
   183 			for i := range fbOut.Messages {
       
   184 				switch fbOut.Messages[i].Name() {
       
   185 				case "FileOptions":
       
   186 					descopts.File = messageGoTypes[i].(pref.ProtoMessage)
       
   187 				case "EnumOptions":
       
   188 					descopts.Enum = messageGoTypes[i].(pref.ProtoMessage)
       
   189 				case "EnumValueOptions":
       
   190 					descopts.EnumValue = messageGoTypes[i].(pref.ProtoMessage)
       
   191 				case "MessageOptions":
       
   192 					descopts.Message = messageGoTypes[i].(pref.ProtoMessage)
       
   193 				case "FieldOptions":
       
   194 					descopts.Field = messageGoTypes[i].(pref.ProtoMessage)
       
   195 				case "OneofOptions":
       
   196 					descopts.Oneof = messageGoTypes[i].(pref.ProtoMessage)
       
   197 				case "ExtensionRangeOptions":
       
   198 					descopts.ExtensionRange = messageGoTypes[i].(pref.ProtoMessage)
       
   199 				case "ServiceOptions":
       
   200 					descopts.Service = messageGoTypes[i].(pref.ProtoMessage)
       
   201 				case "MethodOptions":
       
   202 					descopts.Method = messageGoTypes[i].(pref.ProtoMessage)
       
   203 				}
       
   204 			}
       
   205 		}
       
   206 	}
       
   207 
       
   208 	// Process extensions.
       
   209 	if len(tb.ExtensionInfos) != len(fbOut.Extensions) {
       
   210 		panic("mismatching extension lengths")
       
   211 	}
       
   212 	var depIdx int32
       
   213 	for i := range fbOut.Extensions {
       
   214 		// For enum and message kinds, determine the referent Go type so
       
   215 		// that we can construct their constructors.
       
   216 		const listExtDeps = 2
       
   217 		var goType reflect.Type
       
   218 		switch fbOut.Extensions[i].L1.Kind {
       
   219 		case pref.EnumKind:
       
   220 			j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx)
       
   221 			goType = reflect.TypeOf(tb.GoTypes[j])
       
   222 			depIdx++
       
   223 		case pref.MessageKind, pref.GroupKind:
       
   224 			j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx)
       
   225 			goType = reflect.TypeOf(tb.GoTypes[j])
       
   226 			depIdx++
       
   227 		default:
       
   228 			goType = goTypeForPBKind[fbOut.Extensions[i].L1.Kind]
       
   229 		}
       
   230 		if fbOut.Extensions[i].IsList() {
       
   231 			goType = reflect.SliceOf(goType)
       
   232 		}
       
   233 
       
   234 		pimpl.InitExtensionInfo(&tb.ExtensionInfos[i], &fbOut.Extensions[i], goType)
       
   235 
       
   236 		// Register extension types.
       
   237 		if err := tb.TypeRegistry.RegisterExtension(&tb.ExtensionInfos[i]); err != nil {
       
   238 			panic(err)
       
   239 		}
       
   240 	}
       
   241 
       
   242 	return out
       
   243 }
       
   244 
       
   245 var goTypeForPBKind = map[pref.Kind]reflect.Type{
       
   246 	pref.BoolKind:     reflect.TypeOf(bool(false)),
       
   247 	pref.Int32Kind:    reflect.TypeOf(int32(0)),
       
   248 	pref.Sint32Kind:   reflect.TypeOf(int32(0)),
       
   249 	pref.Sfixed32Kind: reflect.TypeOf(int32(0)),
       
   250 	pref.Int64Kind:    reflect.TypeOf(int64(0)),
       
   251 	pref.Sint64Kind:   reflect.TypeOf(int64(0)),
       
   252 	pref.Sfixed64Kind: reflect.TypeOf(int64(0)),
       
   253 	pref.Uint32Kind:   reflect.TypeOf(uint32(0)),
       
   254 	pref.Fixed32Kind:  reflect.TypeOf(uint32(0)),
       
   255 	pref.Uint64Kind:   reflect.TypeOf(uint64(0)),
       
   256 	pref.Fixed64Kind:  reflect.TypeOf(uint64(0)),
       
   257 	pref.FloatKind:    reflect.TypeOf(float32(0)),
       
   258 	pref.DoubleKind:   reflect.TypeOf(float64(0)),
       
   259 	pref.StringKind:   reflect.TypeOf(string("")),
       
   260 	pref.BytesKind:    reflect.TypeOf([]byte(nil)),
       
   261 }
       
   262 
       
   263 type depIdxs []int32
       
   264 
       
   265 // Get retrieves the jth element of the ith sub-list.
       
   266 func (x depIdxs) Get(i, j int32) int32 {
       
   267 	return x[x[int32(len(x))-i-1]+j]
       
   268 }
       
   269 
       
   270 type (
       
   271 	resolverByIndex struct {
       
   272 		goTypes []interface{}
       
   273 		depIdxs depIdxs
       
   274 		fileRegistry
       
   275 	}
       
   276 	fileRegistry interface {
       
   277 		FindFileByPath(string) (pref.FileDescriptor, error)
       
   278 		FindDescriptorByName(pref.FullName) (pref.Descriptor, error)
       
   279 		RegisterFile(pref.FileDescriptor) error
       
   280 	}
       
   281 )
       
   282 
       
   283 func (r *resolverByIndex) FindEnumByIndex(i, j int32, es []fdesc.Enum, ms []fdesc.Message) pref.EnumDescriptor {
       
   284 	if depIdx := int(r.depIdxs.Get(i, j)); int(depIdx) < len(es)+len(ms) {
       
   285 		return &es[depIdx]
       
   286 	} else {
       
   287 		return pimpl.Export{}.EnumDescriptorOf(r.goTypes[depIdx])
       
   288 	}
       
   289 }
       
   290 
       
   291 func (r *resolverByIndex) FindMessageByIndex(i, j int32, es []fdesc.Enum, ms []fdesc.Message) pref.MessageDescriptor {
       
   292 	if depIdx := int(r.depIdxs.Get(i, j)); depIdx < len(es)+len(ms) {
       
   293 		return &ms[depIdx-len(es)]
       
   294 	} else {
       
   295 		return pimpl.Export{}.MessageDescriptorOf(r.goTypes[depIdx])
       
   296 	}
       
   297 }