...

Source file src/google.golang.org/protobuf/internal/filetype/build.go

Documentation: google.golang.org/protobuf/internal/filetype

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

View as plain text