312 lines
10 KiB
Go
Raw Normal View History

// 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 impl
import (
"fmt"
"reflect"
"strconv"
"strings"
"sync"
pvalue "github.com/golang/protobuf/v2/internal/value"
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
)
// MessageType provides protobuf related functionality for a given Go type
// that represents a message. A given instance of MessageType is tied to
// exactly one Go type, which must be a pointer to a struct type.
type MessageType struct {
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
// GoType is the underlying message Go type and must be populated.
// Once set, this field must never be mutated.
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
GoType reflect.Type // pointer to struct
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
// PBType is the underlying message descriptor type and must be populated.
// Once set, this field must never be mutated.
PBType pref.MessageType
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
once sync.Once // protects all unexported fields
// TODO: Split fields into dense and sparse maps similar to the current
// table-driven implementation in v1?
fields map[pref.FieldNumber]*fieldInfo
unknownFields func(*messageDataType) pref.UnknownFields
extensionFields func(*messageDataType) pref.KnownFields
}
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 (mi *MessageType) init() {
mi.once.Do(func() {
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
t := mi.GoType
if t.Kind() != reflect.Ptr && t.Elem().Kind() != reflect.Struct {
panic(fmt.Sprintf("got %v, want *struct kind", t))
}
mi.makeKnownFieldsFunc(t.Elem())
mi.makeUnknownFieldsFunc(t.Elem())
mi.makeExtensionFieldsFunc(t.Elem())
})
}
internal/impl: support legacy extension fields Implement support for extension fields for messages that use the v1 data structures for extensions. The legacyExtensionFields type wraps a v1 map to implement the v2 protoreflect.KnownFields interface. Working on this change revealed a bug in the dynamic construction of message types for protobuf messages that had cyclic dependencies (e.g., message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo). In such a situation, a deadlock occurs because initialization code depends on the very initialization code that is currently running. To break these cycles, we make some systematic changes listed in the following paragraphs. Generally speaking, we separate the logic for construction and wrapping, where constuction does not recursively rely on dependencies, while wrapping may recursively inspect dependencies. Promote the MessageType.MessageOf method as a standalone MessageOf function that dynamically finds the proper *MessageType to use. We make it such that MessageType only supports two forms of messages types: * Those that fully implement the v2 API. * Those that do not implement the v2 API at all. This removes support for the hybrid form that was exploited by message_test.go In impl/message_test.go, switch each message to look more like how future generated messages will look like. This is done in reaction to the fact that MessageType.MessageOf no longer exists. In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying reflect.Value is addressable reference value, not a pointer value. In value/convert.go, split the logic apart so that obtaining a v2 type and wrapping a type as v2 are distinct operations. Wrapping requires further initialization than simply creating the initial message type, and calling it during initial construction would lead to a deadlock. In protoreflect/go_type.go, we switch back to a lazy initialization of GoType to avoid a deadlock since the user-provided fn may rely on the fact that prototype.GoMessage returned. Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420 Reviewed-on: https://go-review.googlesource.com/c/148826 Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
// makeKnownFieldsFunc generates functions for operations that can be performed
// on each protobuf message field. It takes in a reflect.Type representing the
// Go struct and matches message fields with struct fields.
//
// This code assumes that the struct is well-formed and panics if there are
// any discrepancies.
func (mi *MessageType) makeKnownFieldsFunc(t reflect.Type) {
// Generate a mapping of field numbers and names to Go struct field or type.
fields := map[pref.FieldNumber]reflect.StructField{}
oneofs := map[pref.Name]reflect.StructField{}
oneofFields := map[pref.FieldNumber]reflect.Type{}
special := map[string]reflect.StructField{}
fieldLoop:
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
n, _ := strconv.ParseUint(s, 10, 64)
fields[pref.FieldNumber(n)] = f
continue fieldLoop
}
}
if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 {
oneofs[pref.Name(s)] = f
continue fieldLoop
}
switch f.Name {
case "XXX_weak", "XXX_unrecognized", "XXX_sizecache", "XXX_extensions", "XXX_InternalExtensions":
special[f.Name] = f
continue fieldLoop
}
}
var oneofWrappers []interface{}
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
}
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
}
for _, v := range oneofWrappers {
tf := reflect.TypeOf(v).Elem()
f := tf.Field(0)
for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
n, _ := strconv.ParseUint(s, 10, 64)
oneofFields[pref.FieldNumber(n)] = tf
break
}
}
}
mi.fields = map[pref.FieldNumber]*fieldInfo{}
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 i := 0; i < mi.PBType.Fields().Len(); i++ {
fd := mi.PBType.Fields().Get(i)
fs := fields[fd.Number()]
var fi fieldInfo
switch {
case fd.IsWeak():
fi = fieldInfoForWeak(fd, special["XXX_weak"])
case fd.OneofType() != nil:
fi = fieldInfoForOneof(fd, oneofs[fd.OneofType().Name()], oneofFields[fd.Number()])
case fd.IsMap():
fi = fieldInfoForMap(fd, fs)
case fd.Cardinality() == pref.Repeated:
all: rename Vector as List The terminology Vector does not occur in protobuf documentation at all, so we should rename the Go use of the term to something more recognizable. As such, all instances that match the regexp "[Vv]ect(or)?" were replaced. The C++ documentation uses the term "Repeated", which is a reasonable name. However, the term became overloaded in 2014, when maps were added as a feature and implementated under the hood as repeated fields. This is confusing as it means "repeated" could either refer to repeated fields proper (i.e., explicitly marked with the "repeated" label in the proto file) or map fields. In the case of the C++ reflective API, this is not a problem since repeated fields proper and map fields are interacted with through the same RepeatedField type. In Go, we do not use a single type to handle both types of repeated fields: 1) We are coming up with the Go protobuf reflection API for the first time and so do not need to piggy-back on the repeated fields API to remain backwards compatible since no former usages of Go protobuf reflection exists. 2) Map fields are commonly represented in Go as the Go map type, which do not preserve ordering information. As such it is fundamentally impossible to present an unordered map as a consistently ordered list. Thus, Go needs two different interfaces for lists and maps. Given the above situation, "Repeated" is not a great term to use since it refers to two different things (when we only want one of the meanings). To distinguish between the two, we'll use the terms "List" and "Map" instead. There is some precedence for the term "List" in the protobuf codebase (e.g., "getRepeatedInt32List"). Change-Id: Iddcdb6b78e1e60c14fa4ca213c15f45e214b967b Reviewed-on: https://go-review.googlesource.com/c/149657 Reviewed-by: Damien Neil <dneil@google.com>
2018-11-14 14:05:19 -08:00
fi = fieldInfoForList(fd, fs)
case fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind:
fi = fieldInfoForMessage(fd, fs)
default:
fi = fieldInfoForScalar(fd, fs)
}
mi.fields[fd.Number()] = &fi
}
}
func (mi *MessageType) makeUnknownFieldsFunc(t reflect.Type) {
if f := makeLegacyUnknownFieldsFunc(t); f != nil {
mi.unknownFields = f
return
}
mi.unknownFields = func(*messageDataType) pref.UnknownFields {
return emptyUnknownFields{}
}
}
func (mi *MessageType) makeExtensionFieldsFunc(t reflect.Type) {
internal/impl: support legacy extension fields Implement support for extension fields for messages that use the v1 data structures for extensions. The legacyExtensionFields type wraps a v1 map to implement the v2 protoreflect.KnownFields interface. Working on this change revealed a bug in the dynamic construction of message types for protobuf messages that had cyclic dependencies (e.g., message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo). In such a situation, a deadlock occurs because initialization code depends on the very initialization code that is currently running. To break these cycles, we make some systematic changes listed in the following paragraphs. Generally speaking, we separate the logic for construction and wrapping, where constuction does not recursively rely on dependencies, while wrapping may recursively inspect dependencies. Promote the MessageType.MessageOf method as a standalone MessageOf function that dynamically finds the proper *MessageType to use. We make it such that MessageType only supports two forms of messages types: * Those that fully implement the v2 API. * Those that do not implement the v2 API at all. This removes support for the hybrid form that was exploited by message_test.go In impl/message_test.go, switch each message to look more like how future generated messages will look like. This is done in reaction to the fact that MessageType.MessageOf no longer exists. In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying reflect.Value is addressable reference value, not a pointer value. In value/convert.go, split the logic apart so that obtaining a v2 type and wrapping a type as v2 are distinct operations. Wrapping requires further initialization than simply creating the initial message type, and calling it during initial construction would lead to a deadlock. In protoreflect/go_type.go, we switch back to a lazy initialization of GoType to avoid a deadlock since the user-provided fn may rely on the fact that prototype.GoMessage returned. Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420 Reviewed-on: https://go-review.googlesource.com/c/148826 Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
if f := makeLegacyExtensionFieldsFunc(t); f != nil {
mi.extensionFields = f
return
}
mi.extensionFields = func(*messageDataType) pref.KnownFields {
return emptyExtensionFields{}
}
}
func (mi *MessageType) MessageOf(p interface{}) pref.Message {
return (*messageWrapper)(mi.dataTypeOf(p))
}
func (mi *MessageType) dataTypeOf(p interface{}) *messageDataType {
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
// TODO: Remove this check? This API is primarily used by generated code,
// and should not violate this assumption. Leave this check in for now to
// provide some sanity checks during development. This can be removed if
// it proves to be detrimental to performance.
if reflect.TypeOf(p) != mi.GoType {
panic(fmt.Sprintf("type mismatch: got %T, want %v", p, mi.GoType))
}
return &messageDataType{pointerOfIface(p), mi}
}
// messageDataType is a tuple of a pointer to the message data and
// a pointer to the message type.
//
// TODO: Unfortunately, we need to close over a pointer and MessageType,
// which incurs an an allocation. This pair is similar to a Go interface,
// which is essentially a tuple of the same thing. We can make this efficient
// with reflect.NamedOf (see https://golang.org/issues/16522).
//
// With that hypothetical API, we could dynamically create a new named type
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
// that has the same underlying type as MessageType.GoType, and
// dynamically create methods that close over MessageType.
// Since the new type would have the same underlying type, we could directly
// convert between pointers of those types, giving us an efficient way to swap
// out the method set.
//
// Barring the ability to dynamically create named types, the workaround is
// 1. either to accept the cost of an allocation for this wrapper struct or
// 2. generate more types and methods, at the expense of binary size increase.
type messageDataType struct {
p pointer
mi *MessageType
}
type messageWrapper messageDataType
func (m *messageWrapper) Type() pref.MessageType {
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
return m.mi.PBType
}
func (m *messageWrapper) KnownFields() pref.KnownFields {
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
m.mi.init()
return (*knownFields)(m)
}
func (m *messageWrapper) UnknownFields() pref.UnknownFields {
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
m.mi.init()
return m.mi.unknownFields((*messageDataType)(m))
}
func (m *messageWrapper) Interface() pref.ProtoMessage {
if m, ok := m.ProtoUnwrap().(pref.ProtoMessage); ok {
return m
}
return m
}
func (m *messageWrapper) ProtoReflect() pref.Message {
return m
}
func (m *messageWrapper) ProtoUnwrap() interface{} {
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
return m.p.AsIfaceOf(m.mi.GoType.Elem())
}
func (m *messageWrapper) ProtoMutable() {}
var _ pvalue.Unwrapper = (*messageWrapper)(nil)
type knownFields messageDataType
func (fs *knownFields) Len() (cnt int) {
for _, fi := range fs.mi.fields {
if fi.has(fs.p) {
cnt++
}
}
return cnt + fs.extensionFields().Len()
}
func (fs *knownFields) Has(n pref.FieldNumber) bool {
if fi := fs.mi.fields[n]; fi != nil {
return fi.has(fs.p)
}
return fs.extensionFields().Has(n)
}
func (fs *knownFields) Get(n pref.FieldNumber) pref.Value {
if fi := fs.mi.fields[n]; fi != nil {
return fi.get(fs.p)
}
return fs.extensionFields().Get(n)
}
func (fs *knownFields) Set(n pref.FieldNumber, v pref.Value) {
if fi := fs.mi.fields[n]; fi != nil {
fi.set(fs.p, v)
return
}
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 fs.mi.PBType.ExtensionRanges().Has(n) {
internal/impl: support legacy extension fields Implement support for extension fields for messages that use the v1 data structures for extensions. The legacyExtensionFields type wraps a v1 map to implement the v2 protoreflect.KnownFields interface. Working on this change revealed a bug in the dynamic construction of message types for protobuf messages that had cyclic dependencies (e.g., message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo). In such a situation, a deadlock occurs because initialization code depends on the very initialization code that is currently running. To break these cycles, we make some systematic changes listed in the following paragraphs. Generally speaking, we separate the logic for construction and wrapping, where constuction does not recursively rely on dependencies, while wrapping may recursively inspect dependencies. Promote the MessageType.MessageOf method as a standalone MessageOf function that dynamically finds the proper *MessageType to use. We make it such that MessageType only supports two forms of messages types: * Those that fully implement the v2 API. * Those that do not implement the v2 API at all. This removes support for the hybrid form that was exploited by message_test.go In impl/message_test.go, switch each message to look more like how future generated messages will look like. This is done in reaction to the fact that MessageType.MessageOf no longer exists. In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying reflect.Value is addressable reference value, not a pointer value. In value/convert.go, split the logic apart so that obtaining a v2 type and wrapping a type as v2 are distinct operations. Wrapping requires further initialization than simply creating the initial message type, and calling it during initial construction would lead to a deadlock. In protoreflect/go_type.go, we switch back to a lazy initialization of GoType to avoid a deadlock since the user-provided fn may rely on the fact that prototype.GoMessage returned. Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420 Reviewed-on: https://go-review.googlesource.com/c/148826 Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
fs.extensionFields().Set(n, v)
return
}
panic(fmt.Sprintf("invalid field: %d", n))
}
func (fs *knownFields) Clear(n pref.FieldNumber) {
if fi := fs.mi.fields[n]; fi != nil {
fi.clear(fs.p)
return
}
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 fs.mi.PBType.ExtensionRanges().Has(n) {
internal/impl: support legacy extension fields Implement support for extension fields for messages that use the v1 data structures for extensions. The legacyExtensionFields type wraps a v1 map to implement the v2 protoreflect.KnownFields interface. Working on this change revealed a bug in the dynamic construction of message types for protobuf messages that had cyclic dependencies (e.g., message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo). In such a situation, a deadlock occurs because initialization code depends on the very initialization code that is currently running. To break these cycles, we make some systematic changes listed in the following paragraphs. Generally speaking, we separate the logic for construction and wrapping, where constuction does not recursively rely on dependencies, while wrapping may recursively inspect dependencies. Promote the MessageType.MessageOf method as a standalone MessageOf function that dynamically finds the proper *MessageType to use. We make it such that MessageType only supports two forms of messages types: * Those that fully implement the v2 API. * Those that do not implement the v2 API at all. This removes support for the hybrid form that was exploited by message_test.go In impl/message_test.go, switch each message to look more like how future generated messages will look like. This is done in reaction to the fact that MessageType.MessageOf no longer exists. In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying reflect.Value is addressable reference value, not a pointer value. In value/convert.go, split the logic apart so that obtaining a v2 type and wrapping a type as v2 are distinct operations. Wrapping requires further initialization than simply creating the initial message type, and calling it during initial construction would lead to a deadlock. In protoreflect/go_type.go, we switch back to a lazy initialization of GoType to avoid a deadlock since the user-provided fn may rely on the fact that prototype.GoMessage returned. Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420 Reviewed-on: https://go-review.googlesource.com/c/148826 Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
fs.extensionFields().Clear(n)
return
}
}
func (fs *knownFields) Range(f func(pref.FieldNumber, pref.Value) bool) {
for n, fi := range fs.mi.fields {
if fi.has(fs.p) {
if !f(n, fi.get(fs.p)) {
return
}
}
}
fs.extensionFields().Range(f)
}
func (fs *knownFields) NewMessage(n pref.FieldNumber) pref.Message {
if fi := fs.mi.fields[n]; fi != nil {
return fi.newMessage()
}
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 fs.mi.PBType.ExtensionRanges().Has(n) {
return fs.extensionFields().NewMessage(n)
}
panic(fmt.Sprintf("invalid field: %d", n))
}
func (fs *knownFields) ExtensionTypes() pref.ExtensionFieldTypes {
return fs.extensionFields().ExtensionTypes()
}
func (fs *knownFields) extensionFields() pref.KnownFields {
return fs.mi.extensionFields((*messageDataType)(fs))
}
type emptyUnknownFields struct{}
func (emptyUnknownFields) Len() int { return 0 }
func (emptyUnknownFields) Get(pref.FieldNumber) pref.RawFields { return nil }
func (emptyUnknownFields) Set(pref.FieldNumber, pref.RawFields) { return } // noop
func (emptyUnknownFields) Range(func(pref.FieldNumber, pref.RawFields) bool) { return }
func (emptyUnknownFields) IsSupported() bool { return false }
type emptyExtensionFields struct{}
internal/impl: support legacy extension fields Implement support for extension fields for messages that use the v1 data structures for extensions. The legacyExtensionFields type wraps a v1 map to implement the v2 protoreflect.KnownFields interface. Working on this change revealed a bug in the dynamic construction of message types for protobuf messages that had cyclic dependencies (e.g., message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo). In such a situation, a deadlock occurs because initialization code depends on the very initialization code that is currently running. To break these cycles, we make some systematic changes listed in the following paragraphs. Generally speaking, we separate the logic for construction and wrapping, where constuction does not recursively rely on dependencies, while wrapping may recursively inspect dependencies. Promote the MessageType.MessageOf method as a standalone MessageOf function that dynamically finds the proper *MessageType to use. We make it such that MessageType only supports two forms of messages types: * Those that fully implement the v2 API. * Those that do not implement the v2 API at all. This removes support for the hybrid form that was exploited by message_test.go In impl/message_test.go, switch each message to look more like how future generated messages will look like. This is done in reaction to the fact that MessageType.MessageOf no longer exists. In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying reflect.Value is addressable reference value, not a pointer value. In value/convert.go, split the logic apart so that obtaining a v2 type and wrapping a type as v2 are distinct operations. Wrapping requires further initialization than simply creating the initial message type, and calling it during initial construction would lead to a deadlock. In protoreflect/go_type.go, we switch back to a lazy initialization of GoType to avoid a deadlock since the user-provided fn may rely on the fact that prototype.GoMessage returned. Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420 Reviewed-on: https://go-review.googlesource.com/c/148826 Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
func (emptyExtensionFields) Len() int { return 0 }
func (emptyExtensionFields) Has(pref.FieldNumber) bool { return false }
func (emptyExtensionFields) Get(pref.FieldNumber) pref.Value { return pref.Value{} }
func (emptyExtensionFields) Set(pref.FieldNumber, pref.Value) { panic("extensions not supported") }
func (emptyExtensionFields) Clear(pref.FieldNumber) { return } // noop
func (emptyExtensionFields) Range(func(pref.FieldNumber, pref.Value) bool) { return }
func (emptyExtensionFields) NewMessage(pref.FieldNumber) pref.Message {
panic("extensions not supported")
}
func (emptyExtensionFields) ExtensionTypes() pref.ExtensionFieldTypes { return emptyExtensionTypes{} }
type emptyExtensionTypes struct{}
func (emptyExtensionTypes) Len() int { return 0 }
func (emptyExtensionTypes) Register(pref.ExtensionType) { panic("extensions not supported") }
func (emptyExtensionTypes) Remove(pref.ExtensionType) { return } // noop
func (emptyExtensionTypes) ByNumber(pref.FieldNumber) pref.ExtensionType { return nil }
func (emptyExtensionTypes) ByName(pref.FullName) pref.ExtensionType { return nil }
func (emptyExtensionTypes) Range(func(pref.ExtensionType) bool) { return }