build.go raw

   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 filedesc provides functionality for constructing descriptors.
   6  //
   7  // The types in this package implement interfaces in the protoreflect package
   8  // related to protobuf descripriptors.
   9  package filedesc
  10  
  11  import (
  12  	"google.golang.org/protobuf/encoding/protowire"
  13  	"google.golang.org/protobuf/internal/genid"
  14  	"google.golang.org/protobuf/reflect/protoreflect"
  15  	"google.golang.org/protobuf/reflect/protoregistry"
  16  )
  17  
  18  // Builder construct a protoreflect.FileDescriptor from the raw descriptor.
  19  type Builder struct {
  20  	// GoPackagePath is the Go package path that is invoking this builder.
  21  	GoPackagePath string
  22  
  23  	// RawDescriptor is the wire-encoded bytes of FileDescriptorProto
  24  	// and must be populated.
  25  	RawDescriptor []byte
  26  
  27  	// NumEnums is the total number of enums declared in the file.
  28  	NumEnums int32
  29  	// NumMessages is the total number of messages declared in the file.
  30  	// It includes the implicit message declarations for map entries.
  31  	NumMessages int32
  32  	// NumExtensions is the total number of extensions declared in the file.
  33  	NumExtensions int32
  34  	// NumServices is the total number of services declared in the file.
  35  	NumServices int32
  36  
  37  	// TypeResolver resolves extension field types for descriptor options.
  38  	// If nil, it uses protoregistry.GlobalTypes.
  39  	TypeResolver interface {
  40  		protoregistry.ExtensionTypeResolver
  41  	}
  42  
  43  	// FileRegistry is use to lookup file, enum, and message dependencies.
  44  	// Once constructed, the file descriptor is registered here.
  45  	// If nil, it uses protoregistry.GlobalFiles.
  46  	FileRegistry interface {
  47  		FindFileByPath(string) (protoreflect.FileDescriptor, error)
  48  		FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
  49  		RegisterFile(protoreflect.FileDescriptor) error
  50  	}
  51  }
  52  
  53  // resolverByIndex is an interface Builder.FileRegistry may implement.
  54  // If so, it permits looking up an enum or message dependency based on the
  55  // sub-list and element index into filetype.Builder.DependencyIndexes.
  56  type resolverByIndex interface {
  57  	FindEnumByIndex(int32, int32, []Enum, []Message) protoreflect.EnumDescriptor
  58  	FindMessageByIndex(int32, int32, []Enum, []Message) protoreflect.MessageDescriptor
  59  }
  60  
  61  // Indexes of each sub-list in filetype.Builder.DependencyIndexes.
  62  const (
  63  	listFieldDeps int32 = iota
  64  	listExtTargets
  65  	listExtDeps
  66  	listMethInDeps
  67  	listMethOutDeps
  68  )
  69  
  70  // Out is the output of the Builder.
  71  type Out struct {
  72  	File protoreflect.FileDescriptor
  73  
  74  	// Enums is all enum descriptors in "flattened ordering".
  75  	Enums []Enum
  76  	// Messages is all message descriptors in "flattened ordering".
  77  	// It includes the implicit message declarations for map entries.
  78  	Messages []Message
  79  	// Extensions is all extension descriptors in "flattened ordering".
  80  	Extensions []Extension
  81  	// Service is all service descriptors in "flattened ordering".
  82  	Services []Service
  83  }
  84  
  85  // Build constructs a FileDescriptor given the parameters set in Builder.
  86  // It assumes that the inputs are well-formed and panics if any inconsistencies
  87  // are encountered.
  88  //
  89  // If NumEnums+NumMessages+NumExtensions+NumServices is zero,
  90  // then Build automatically derives them from the raw descriptor.
  91  func (db Builder) Build() (out Out) {
  92  	// Populate the counts if uninitialized.
  93  	if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 {
  94  		db.unmarshalCounts(db.RawDescriptor, true)
  95  	}
  96  
  97  	// Initialize resolvers and registries if unpopulated.
  98  	if db.TypeResolver == nil {
  99  		db.TypeResolver = protoregistry.GlobalTypes
 100  	}
 101  	if db.FileRegistry == nil {
 102  		db.FileRegistry = protoregistry.GlobalFiles
 103  	}
 104  
 105  	fd := newRawFile(db)
 106  	out.File = fd
 107  	out.Enums = fd.allEnums
 108  	out.Messages = fd.allMessages
 109  	out.Extensions = fd.allExtensions
 110  	out.Services = fd.allServices
 111  
 112  	if err := db.FileRegistry.RegisterFile(fd); err != nil {
 113  		panic(err)
 114  	}
 115  	return out
 116  }
 117  
 118  // unmarshalCounts counts the number of enum, message, extension, and service
 119  // declarations in the raw message, which is either a FileDescriptorProto
 120  // or a MessageDescriptorProto depending on whether isFile is set.
 121  func (db *Builder) unmarshalCounts(b []byte, isFile bool) {
 122  	for len(b) > 0 {
 123  		num, typ, n := protowire.ConsumeTag(b)
 124  		b = b[n:]
 125  		switch typ {
 126  		case protowire.BytesType:
 127  			v, m := protowire.ConsumeBytes(b)
 128  			b = b[m:]
 129  			if isFile {
 130  				switch num {
 131  				case genid.FileDescriptorProto_EnumType_field_number:
 132  					db.NumEnums++
 133  				case genid.FileDescriptorProto_MessageType_field_number:
 134  					db.unmarshalCounts(v, false)
 135  					db.NumMessages++
 136  				case genid.FileDescriptorProto_Extension_field_number:
 137  					db.NumExtensions++
 138  				case genid.FileDescriptorProto_Service_field_number:
 139  					db.NumServices++
 140  				}
 141  			} else {
 142  				switch num {
 143  				case genid.DescriptorProto_EnumType_field_number:
 144  					db.NumEnums++
 145  				case genid.DescriptorProto_NestedType_field_number:
 146  					db.unmarshalCounts(v, false)
 147  					db.NumMessages++
 148  				case genid.DescriptorProto_Extension_field_number:
 149  					db.NumExtensions++
 150  				}
 151  			}
 152  		default:
 153  			m := protowire.ConsumeFieldValue(num, typ, b)
 154  			b = b[m:]
 155  		}
 156  	}
 157  }
 158