344 lines
10 KiB
Go
Raw Normal View History

cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package internal_gengo
import (
"fmt"
"math"
"strings"
"unicode/utf8"
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
"google.golang.org/protobuf/types/descriptorpb"
)
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
g.P()
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
genFileDescriptor(gen, g, f)
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
if len(f.allEnums) > 0 {
g.P("var ", enumTypesVarName(f), " = make([]", prototypePackage.Ident("Enum"), ",", len(f.allEnums), ")")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
if len(f.allMessages) > 0 {
g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageInfo"), ",", len(f.allMessages), ")")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
// Generate a unique list of Go types for all declarations and dependencies,
// and the associated index into the type list for all dependencies.
var goTypes []string
var depIdxs []string
seen := map[protoreflect.FullName]int{}
genDep := func(name protoreflect.FullName, depSource string) {
if depSource != "" {
line := fmt.Sprintf("%d, // %s -> %s", seen[name], depSource, name)
depIdxs = append(depIdxs, line)
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
}
genEnum := func(e *protogen.Enum, depSource string) {
if e != nil {
name := e.Desc.FullName()
if _, ok := seen[name]; !ok {
line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
goTypes = append(goTypes, line)
seen[name] = len(seen)
}
if depSource != "" {
genDep(name, depSource)
}
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
genMessage := func(m *protogen.Message, depSource string) {
if m != nil {
name := m.Desc.FullName()
if _, ok := seen[name]; !ok {
line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
if m.Desc.IsMapEntry() {
// Map entry messages have no associated Go type.
line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
goTypes = append(goTypes, line)
seen[name] = len(seen)
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
if depSource != "" {
genDep(name, depSource)
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
}
}
// This ordering is significant.
// See filetype.TypeBuilder.DependencyIndexes.
var depOffsets []string
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
for _, enum := range f.allEnums {
genEnum(enum, "")
}
for _, message := range f.allMessages {
genMessage(message, "")
}
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of field type_name sub-list", len(depIdxs)))
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
for _, message := range f.allMessages {
for _, field := range message.Fields {
if field.Desc.IsWeak() {
continue
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
source := string(field.Desc.FullName())
genEnum(field.Enum, source+":type_name")
genMessage(field.Message, source+":type_name")
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
}
}
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of extension extendee sub-list", len(depIdxs)))
for _, extension := range f.allExtensions {
source := string(extension.Desc.FullName())
genMessage(extension.Extendee, source+":extendee")
}
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of extension type_name sub-list", len(depIdxs)))
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
for _, extension := range f.allExtensions {
source := string(extension.Desc.FullName())
genEnum(extension.Enum, source+":type_name")
genMessage(extension.Message, source+":type_name")
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
}
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of method input_type sub-list", len(depIdxs)))
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
for _, service := range f.Services {
for _, method := range service.Methods {
source := string(method.Desc.FullName())
genMessage(method.Input, source+":input_type")
}
}
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of method output_type sub-list", len(depIdxs)))
for _, service := range f.Services {
for _, method := range service.Methods {
source := string(method.Desc.FullName())
genMessage(method.Output, source+":output_type")
}
}
for i := len(depOffsets) - 1; i >= 0; i-- {
depIdxs = append(depIdxs, depOffsets[i])
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
if len(depIdxs) > math.MaxInt32 {
panic("too many dependencies") // sanity check
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
g.P("var ", goTypesVarName(f), " = []interface{}{")
for _, s := range goTypes {
g.P(s)
}
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
g.P("}")
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
g.P("var ", depIdxsVarName(f), " = []int32{")
for _, s := range depIdxs {
g.P(s)
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
g.P("}")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
g.P("func init() { ", initFuncName(f.File), "() }")
g.P("func ", initFuncName(f.File), "() {")
g.P("if ", f.GoDescriptorIdent, " != nil {")
g.P("return")
g.P("}")
// Ensure that initialization functions for different files in the same Go
// package run in the correct order: Call the init funcs for every .proto file
// imported by this one that is in the same Go package.
for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
impFile, _ := gen.FileByName(imps.Get(i).Path())
if impFile.GoImportPath != f.GoImportPath {
continue
}
g.P(initFuncName(impFile), "()")
}
if len(f.allMessages) > 0 {
// Populate MessageInfo.Exporters.
g.P("if !", protoimplPackage.Ident("UnsafeEnabled"), " {")
for _, message := range f.allMessages {
if sf := f.allMessageFieldsByPtr[message]; len(sf.unexported) > 0 {
idx := f.allMessagesByPtr[message]
typesVar := messageTypesVarName(f)
g.P(typesVar, "[", idx, "].Exporter = func(v interface{}, i int) interface{} {")
g.P("switch v := v.(*", message.GoIdent, "); i {")
for i := 0; i < sf.count; i++ {
if name := sf.unexported[i]; name != "" {
g.P("case ", i, ": return &v.", name)
}
}
g.P("default: return nil")
g.P("}")
g.P("}")
}
}
g.P("}")
// Populate MessageInfo.OneofWrappers.
for _, message := range f.allMessages {
if len(message.Oneofs) > 0 {
idx := f.allMessagesByPtr[message]
typesVar := messageTypesVarName(f)
// Associate the wrapper types by directly passing them to the MessageInfo.
g.P(typesVar, "[", idx, "].OneofWrappers = []interface{} {")
for _, oneof := range message.Oneofs {
for _, field := range oneof.Fields {
g.P("(*", fieldOneofType(field), ")(nil),")
}
}
g.P("}")
}
}
}
g.P("out := ", protoimplPackage.Ident("TypeBuilder"), "{")
g.P("File: ", protoimplPackage.Ident("DescBuilder"), "{")
g.P("RawDescriptor: ", rawDescVarName(f), ",")
g.P("NumEnums: ", len(f.allEnums), ",")
g.P("NumMessages: ", len(f.allMessages), ",")
g.P("NumExtensions: ", len(f.allExtensions), ",")
g.P("NumServices: ", len(f.Services), ",")
g.P("},")
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
g.P("GoTypes: ", goTypesVarName(f), ",")
g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
if len(f.allMessages) > 0 {
g.P("MessageInfos: ", messageTypesVarName(f), ",")
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
}
if len(f.allExtensions) > 0 {
g.P("LegacyExtensions: ", extDescsVarName(f), ",")
}
g.P("}.Build()")
g.P(f.GoDescriptorIdent, " = out.File")
if len(f.allEnums) > 0 {
g.P(enumTypesVarName(f), " = out.Enums")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
// Set inputs to nil to allow GC to reclaim resources.
g.P(rawDescVarName(f), " = nil")
g.P(goTypesVarName(f), " = nil")
g.P(depIdxsVarName(f), " = nil")
g.P("}")
}
func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
descProto := new(descriptorpb.FileDescriptorProto)
proto.Merge(descProto, f.Proto)
descProto.SourceCodeInfo = nil // drop source code information
b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(descProto)
if err != nil {
gen.Error(err)
return
}
g.P("var ", rawDescVarName(f), " = []byte{")
for len(b) > 0 {
n := 16
if n > len(b) {
n = len(b)
}
s := ""
for _, c := range b[:n] {
s += fmt.Sprintf("0x%02x,", c)
}
g.P(s)
b = b[n:]
}
g.P("}")
g.P()
if generateRawDescMethods {
onceVar := rawDescVarName(f) + "Once"
dataVar := rawDescVarName(f) + "Data"
g.P("var (")
g.P(onceVar, " ", syncPackage.Ident("Once"))
g.P(dataVar, " = ", rawDescVarName(f))
g.P(")")
g.P()
g.P("func ", rawDescVarName(f), "GZIP() []byte {")
g.P(onceVar, ".Do(func() {")
g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
g.P("})")
g.P("return ", dataVar)
g.P("}")
g.P()
}
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
idx := f.allEnumsByPtr[enum]
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
typesVar := enumTypesVarName(f)
// Descriptor method.
g.P("func (", enum.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
g.P("return ", typesVar, "[", idx, "].EnumDescriptor")
g.P("}")
g.P()
// Number method.
g.P("func (x ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
g.P("}")
g.P()
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
idx := f.allMessagesByPtr[message]
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
typesVar := messageTypesVarName(f)
// ProtoReflect method.
g.P("func (x *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
g.P("mi := &", typesVar, "[", idx, "]")
if generateMessateStateFields {
g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " && x != nil {")
g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
g.P("if ms.LoadMessageInfo() == nil {")
g.P("ms.StoreMessageInfo(mi)")
g.P("}")
g.P("return ms")
g.P("}")
}
g.P("return mi.MessageOf(x)")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
g.P("}")
g.P()
// XXX_Methods method.
g.P("func (x *", message.GoIdent, ") XXX_Methods() *", protoifacePackage.Ident("Methods"), " {")
g.P("return ", typesVar, "[", idx, "].Methods()")
g.P("}")
g.P()
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
func fileVarName(f *protogen.File, suffix string) string {
prefix := f.GoDescriptorIdent.GoName
_, n := utf8.DecodeRuneInString(prefix)
prefix = strings.ToLower(prefix[:n]) + prefix[n:]
return prefix + "_" + suffix
}
func rawDescVarName(f *fileInfo) string {
return fileVarName(f.File, "rawDesc")
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
func goTypesVarName(f *fileInfo) string {
return fileVarName(f.File, "goTypes")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
func depIdxsVarName(f *fileInfo) string {
return fileVarName(f.File, "depIdxs")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
internal/fileinit: generate reflect data structures from raw descriptors This CL takes a significantly different approach to generating support for protobuf reflection. The previous approach involved generating a large number of Go literals to represent the reflection information. While that approach was correct, it resulted in too much binary bloat. The approach taken here initializes the reflection information from the raw descriptor proto, which is a relatively dense representation of the protobuf reflection information. In order to keep initialization cost low, several measures were taken: * At program init, the bare minimum is parsed in order to initialize naming information for enums, messages, extensions, and services declared in the file. This is done because those top-level declarations are often relevant for registration. * Only upon first are most of the other data structures for protobuf reflection actually initialized. * Instead of using proto.Unmarshal, a hand-written unmarshaler is used. This allows us to avoid a dependendency on the descriptor proto and also because the API for the descriptor proto is fundamentally non-performant since it requires an allocation for every primitive field. At a high-level, the new implementation lives in internal/fileinit. Several changes were made to other parts of the repository: * cmd/protoc-gen-go: * Stop compressing the raw descriptors. While compression does reduce the size of the descriptors by approximately 2x, it is a pre-mature optimization since the descriptors themselves are around 1% of the total binary bloat that is due to generated protobufs. * Seeding protobuf reflection from the raw descriptor significantly simplifies the generator implementation since it is no longer responsible for constructing a tree of Go literals to represent the same information. * We remove the generation of the shadow types and instead call protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation for every call to ProtoReflect since we need to allocate a tuple that wraps a pointer to the message value, and a pointer to message type. * internal/impl: * We add a MessageType.GoType field and make it required that it is set prior to first use. This is done so that we can avoid calling MessageType.init except for when it is actually needed. The allows code to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the init code will run, possibly triggering a recursive deadlock (where the init code depends on getting the Type of some dependency which may be declared within the same file). * internal/cmd/generate-types: * The code to generate reflect/prototype/protofile_list_gen.go was copied and altered to generated internal/fileinit.desc_list_gen.go. At a high-level this CL adds significant technical complexity. However, this is offset by several possible future changes: * The prototype package can be drastically simplified. We can probably reimplement internal/legacy to use internal/fileinit instead, allowing us to drop another dependency on the prototype package. As a result, we can probably delete most of the constructor types in that package. * With the prototype package significantly pruned, and the fact that generated code no longer depend on depends on that package, we can consider merging what's left of prototype into protodesc. Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715 Reviewed-on: https://go-review.googlesource.com/c/158539 Reviewed-by: Herbie Ong <herbie@google.com> Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
func enumTypesVarName(f *fileInfo) string {
return fileVarName(f.File, "enumTypes")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
func messageTypesVarName(f *fileInfo) string {
return fileVarName(f.File, "msgTypes")
cmd/protoc-gen-go: add support for protobuf reflection Implement support in protoc-gen-go for generating messages and enums that satisfy the v2 protobuf reflection interfaces. Specifically, the following are added: * top-level variable representing the file descriptor * ProtoReflect method on enums (to implement protoV2.Enum) * ProtoReflect method on messages (to implement protoV2.Message) The following are not supported yet: * resolving transitive dependencies for file imports * Extension descriptors * Service descriptors The implementation approach creates a single array for all the message and enum declarations and references sections of that array to complete dependencies. Since protobuf declarations can form a graph (a message may depend on itself), it is difficult to construct a graph as a single literal. One way is to use placeholder descriptors, but that is not efficient as it requires encoding the full name of each dependent enum and message and then later resolving it; thus, both expanding the binary size and also increasing initialization cost. Instead, we add a prototype.{Enum,Message}.Reference method to obtain a descriptor reference for the purposes for satisfying dependencies. As such, nested declarations and dependencies are populated in an init function. Other changes to support the implementation: * Added a protoimpl package to expose the MessageType type and also the MessageTypeOf and EnumTypeOf helper functions. * Added a protogen.File.GoIdent field to provide a suggested variable name for the file descriptor. * Added prototype.{Enum,Message}.Reference that provides a descriptor reference for the purposes for satisfying cyclic dependencies. * Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source identifier that represents the given constant. Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d Reviewed-on: https://go-review.googlesource.com/c/150074 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
}
func extDescsVarName(f *fileInfo) string {
return fileVarName(f.File, "extDescs")
}
func initFuncName(f *protogen.File) string {
return fileVarName(f, "init")
}