2018-09-12 16:20:37 -07: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 impl
|
|
|
|
|
|
|
|
import (
|
2018-09-13 14:24:37 -07:00
|
|
|
"fmt"
|
2018-09-12 16:20:37 -07:00
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2018-09-13 14:24:37 -07:00
|
|
|
"sync"
|
2019-04-01 13:49:56 -07:00
|
|
|
"sync/atomic"
|
2018-09-12 16:20:37 -07:00
|
|
|
|
2019-05-13 23:55:40 -07:00
|
|
|
pref "google.golang.org/protobuf/reflect/protoreflect"
|
|
|
|
piface "google.golang.org/protobuf/runtime/protoiface"
|
2018-09-12 16:20:37 -07:00
|
|
|
)
|
|
|
|
|
2019-05-22 05:12:36 -04:00
|
|
|
// MessageInfo provides protobuf related functionality for a given Go type
|
|
|
|
// that represents a message. A given instance of MessageInfo is tied to
|
2018-09-13 14:24:37 -07:00
|
|
|
// exactly one Go type, which must be a pointer to a struct type.
|
2019-05-22 05:12:36 -04:00
|
|
|
type MessageInfo 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.
|
2018-09-13 14:24:37 -07:00
|
|
|
// 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
|
2018-09-13 14:24:37 -07: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
|
|
|
// PBType is the underlying message descriptor type and must be populated.
|
|
|
|
// Once set, this field must never be mutated.
|
|
|
|
PBType pref.MessageType
|
2018-09-13 14:24:37 -07:00
|
|
|
|
2019-07-06 13:05:11 -07:00
|
|
|
// Exporter must be provided in a purego environment in order to provide
|
|
|
|
// access to unexported fields.
|
|
|
|
Exporter exporter
|
|
|
|
|
2019-07-08 10:38:11 -07:00
|
|
|
// OneofWrappers is list of pointers to oneof wrapper struct types.
|
|
|
|
OneofWrappers []interface{}
|
|
|
|
|
2019-04-01 13:49:56 -07:00
|
|
|
initMu sync.Mutex // protects all unexported fields
|
|
|
|
initDone uint32
|
|
|
|
|
2019-06-20 03:09:57 -07:00
|
|
|
reflectMessageInfo
|
2019-04-25 23:48:08 -07:00
|
|
|
|
2019-06-20 10:12:23 -07:00
|
|
|
// Information used by the fast-path methods.
|
2019-06-17 12:30:25 -07:00
|
|
|
methods piface.Methods
|
2019-06-20 10:12:23 -07:00
|
|
|
coderMessageInfo
|
2019-04-01 13:49:56 -07:00
|
|
|
|
|
|
|
extensionFieldInfosMu sync.RWMutex
|
2019-06-04 16:20:00 -07:00
|
|
|
extensionFieldInfos map[pref.ExtensionType]*extensionFieldInfo
|
2019-04-01 13:49:56 -07:00
|
|
|
}
|
|
|
|
|
2019-06-20 03:09:57 -07:00
|
|
|
type reflectMessageInfo struct {
|
|
|
|
fields map[pref.FieldNumber]*fieldInfo
|
|
|
|
oneofs map[pref.Name]*oneofInfo
|
|
|
|
|
|
|
|
getUnknown func(pointer) pref.RawFields
|
|
|
|
setUnknown func(pointer, pref.RawFields)
|
|
|
|
extensionMap func(pointer) *extensionMap
|
|
|
|
|
|
|
|
nilMessage atomicNilMessage
|
|
|
|
}
|
|
|
|
|
2019-07-06 13:05:11 -07:00
|
|
|
// exporter is a function that returns a reference to the ith field of v,
|
|
|
|
// where v is a pointer to a struct. It returns nil if it does not support
|
|
|
|
// exporting the requested field (e.g., already exported).
|
|
|
|
type exporter func(v interface{}, i int) interface{}
|
|
|
|
|
2019-04-01 13:49:56 -07:00
|
|
|
var prefMessageType = reflect.TypeOf((*pref.Message)(nil)).Elem()
|
|
|
|
|
2019-05-22 05:12:36 -04:00
|
|
|
// getMessageInfo returns the MessageInfo (if any) for a type.
|
2019-04-01 13:49:56 -07:00
|
|
|
//
|
2019-05-22 05:12:36 -04:00
|
|
|
// We find the MessageInfo by calling the ProtoReflect method on the type's
|
2019-04-01 13:49:56 -07:00
|
|
|
// zero value and looking at the returned type to see if it is a
|
2019-05-22 05:12:36 -04:00
|
|
|
// messageReflectWrapper. Note that the MessageInfo may still be uninitialized
|
2019-04-01 13:49:56 -07:00
|
|
|
// at this point.
|
2019-05-22 05:12:36 -04:00
|
|
|
func getMessageInfo(mt reflect.Type) (mi *MessageInfo, ok bool) {
|
2019-04-01 13:49:56 -07:00
|
|
|
method, ok := mt.MethodByName("ProtoReflect")
|
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
if method.Type.NumIn() != 1 || method.Type.NumOut() != 1 || method.Type.Out(0) != prefMessageType {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
ret := reflect.Zero(mt).Method(method.Index).Call(nil)
|
|
|
|
m, ok := ret[0].Elem().Interface().(*messageReflectWrapper)
|
|
|
|
if !ok {
|
|
|
|
return nil, ok
|
|
|
|
}
|
|
|
|
return m.mi, true
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
|
2019-05-22 05:12:36 -04:00
|
|
|
func (mi *MessageInfo) init() {
|
2019-04-01 13:49:56 -07:00
|
|
|
// This function is called in the hot path. Inline the sync.Once
|
|
|
|
// logic, since allocating a closure for Once.Do is expensive.
|
|
|
|
// Keep init small to ensure that it can be inlined.
|
2019-06-20 03:09:57 -07:00
|
|
|
if atomic.LoadUint32(&mi.initDone) == 0 {
|
|
|
|
mi.initOnce()
|
2019-04-01 13:49:56 -07:00
|
|
|
}
|
|
|
|
}
|
2018-09-13 14:24:37 -07:00
|
|
|
|
2019-05-22 05:12:36 -04:00
|
|
|
func (mi *MessageInfo) initOnce() {
|
2019-04-01 13:49:56 -07:00
|
|
|
mi.initMu.Lock()
|
|
|
|
defer mi.initMu.Unlock()
|
|
|
|
if mi.initDone == 1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
t := mi.GoType
|
|
|
|
if t.Kind() != reflect.Ptr && t.Elem().Kind() != reflect.Struct {
|
|
|
|
panic(fmt.Sprintf("got %v, want *struct kind", t))
|
|
|
|
}
|
|
|
|
|
|
|
|
si := mi.makeStructInfo(t.Elem())
|
|
|
|
mi.makeKnownFieldsFunc(si)
|
2019-07-06 13:02:14 -07:00
|
|
|
mi.makeUnknownFieldsFunc(t.Elem(), si)
|
|
|
|
mi.makeExtensionFieldsFunc(t.Elem(), si)
|
2019-06-20 10:12:23 -07:00
|
|
|
mi.makeMethods(t.Elem(), si)
|
2019-04-01 13:49:56 -07:00
|
|
|
|
|
|
|
atomic.StoreUint32(&mi.initDone, 1)
|
|
|
|
}
|
|
|
|
|
2019-04-25 23:48:08 -07:00
|
|
|
type (
|
|
|
|
SizeCache = int32
|
2019-04-08 13:52:14 -07:00
|
|
|
WeakFields = map[int32]piface.MessageV1
|
2019-04-25 23:48:08 -07:00
|
|
|
UnknownFields = []byte
|
|
|
|
ExtensionFields = map[int32]ExtensionField
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
sizecacheType = reflect.TypeOf(SizeCache(0))
|
2019-04-08 13:52:14 -07:00
|
|
|
weakFieldsType = reflect.TypeOf(WeakFields(nil))
|
2019-04-25 23:48:08 -07:00
|
|
|
unknownFieldsType = reflect.TypeOf(UnknownFields(nil))
|
|
|
|
extensionFieldsType = reflect.TypeOf(ExtensionFields(nil))
|
|
|
|
)
|
2019-04-01 13:49:56 -07:00
|
|
|
|
2019-05-09 11:33:55 -07:00
|
|
|
type structInfo struct {
|
2019-07-06 13:02:14 -07:00
|
|
|
sizecacheOffset offset
|
2019-04-08 13:52:14 -07:00
|
|
|
weakOffset offset
|
2019-07-06 13:02:14 -07:00
|
|
|
unknownOffset offset
|
2019-07-06 13:05:11 -07:00
|
|
|
extensionOffset offset
|
2019-07-06 13:02:14 -07:00
|
|
|
|
2019-05-09 11:33:55 -07:00
|
|
|
fieldsByNumber map[pref.FieldNumber]reflect.StructField
|
|
|
|
oneofsByName map[pref.Name]reflect.StructField
|
|
|
|
oneofWrappersByType map[reflect.Type]pref.FieldNumber
|
|
|
|
oneofWrappersByNumber map[pref.FieldNumber]reflect.Type
|
|
|
|
}
|
|
|
|
|
2019-05-22 05:12:36 -04:00
|
|
|
func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
|
2019-05-09 11:33:55 -07:00
|
|
|
si := structInfo{
|
2019-07-06 13:02:14 -07:00
|
|
|
sizecacheOffset: invalidOffset,
|
2019-04-08 13:52:14 -07:00
|
|
|
weakOffset: invalidOffset,
|
2019-07-06 13:02:14 -07:00
|
|
|
unknownOffset: invalidOffset,
|
2019-07-06 13:05:11 -07:00
|
|
|
extensionOffset: invalidOffset,
|
2019-07-06 13:02:14 -07:00
|
|
|
|
2019-05-09 11:33:55 -07:00
|
|
|
fieldsByNumber: map[pref.FieldNumber]reflect.StructField{},
|
|
|
|
oneofsByName: map[pref.Name]reflect.StructField{},
|
|
|
|
oneofWrappersByType: map[reflect.Type]pref.FieldNumber{},
|
|
|
|
oneofWrappersByNumber: map[pref.FieldNumber]reflect.Type{},
|
|
|
|
}
|
2019-07-06 13:02:14 -07:00
|
|
|
|
2019-07-06 13:05:11 -07:00
|
|
|
if f, _ := t.FieldByName("sizeCache"); f.Type == sizecacheType {
|
|
|
|
si.sizecacheOffset = offsetOf(f, mi.Exporter)
|
|
|
|
}
|
2019-07-06 13:02:14 -07:00
|
|
|
if f, _ := t.FieldByName("XXX_sizecache"); f.Type == sizecacheType {
|
2019-07-06 13:05:11 -07:00
|
|
|
si.sizecacheOffset = offsetOf(f, mi.Exporter)
|
|
|
|
}
|
2019-04-08 13:52:14 -07:00
|
|
|
if f, _ := t.FieldByName("XXX_weak"); f.Type == weakFieldsType {
|
|
|
|
si.weakOffset = offsetOf(f, mi.Exporter)
|
|
|
|
}
|
2019-07-06 13:05:11 -07:00
|
|
|
if f, _ := t.FieldByName("unknownFields"); f.Type == unknownFieldsType {
|
|
|
|
si.unknownOffset = offsetOf(f, mi.Exporter)
|
|
|
|
}
|
|
|
|
if f, _ := t.FieldByName("XXX_unrecognized"); f.Type == unknownFieldsType {
|
|
|
|
si.unknownOffset = offsetOf(f, mi.Exporter)
|
|
|
|
}
|
|
|
|
if f, _ := t.FieldByName("extensionFields"); f.Type == extensionFieldsType {
|
|
|
|
si.extensionOffset = offsetOf(f, mi.Exporter)
|
2019-07-06 13:02:14 -07:00
|
|
|
}
|
|
|
|
if f, _ := t.FieldByName("XXX_InternalExtensions"); f.Type == extensionFieldsType {
|
2019-07-06 13:05:11 -07:00
|
|
|
si.extensionOffset = offsetOf(f, mi.Exporter)
|
2019-07-06 13:02:14 -07:00
|
|
|
}
|
|
|
|
if f, _ := t.FieldByName("XXX_extensions"); f.Type == extensionFieldsType {
|
2019-07-06 13:05:11 -07:00
|
|
|
si.extensionOffset = offsetOf(f, mi.Exporter)
|
2019-07-06 13:02:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a mapping of field numbers and names to Go struct field or type.
|
2018-09-12 16:20:37 -07:00
|
|
|
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)
|
2019-05-09 11:33:55 -07:00
|
|
|
si.fieldsByNumber[pref.FieldNumber(n)] = f
|
2018-09-12 16:20:37 -07:00
|
|
|
continue fieldLoop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 {
|
2019-05-09 11:33:55 -07:00
|
|
|
si.oneofsByName[pref.Name(s)] = f
|
2018-09-12 16:20:37 -07:00
|
|
|
continue fieldLoop
|
|
|
|
}
|
|
|
|
}
|
2019-07-06 13:02:14 -07:00
|
|
|
|
|
|
|
// Derive a mapping of oneof wrappers to fields.
|
2019-07-08 10:38:11 -07:00
|
|
|
oneofWrappers := mi.OneofWrappers
|
2018-10-17 11:46:52 -07:00
|
|
|
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
|
2018-11-26 12:57:27 -08:00
|
|
|
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)
|
2019-05-09 11:33:55 -07:00
|
|
|
si.oneofWrappersByType[tf] = pref.FieldNumber(n)
|
|
|
|
si.oneofWrappersByNumber[pref.FieldNumber(n)] = tf
|
2018-11-26 12:57:27 -08:00
|
|
|
break
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-06 13:02:14 -07:00
|
|
|
|
2019-05-09 11:33:55 -07:00
|
|
|
return si
|
|
|
|
}
|
2018-09-12 16:20:37 -07:00
|
|
|
|
2019-05-09 11:33:55 -07: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.
|
2019-05-22 05:12:36 -04:00
|
|
|
func (mi *MessageInfo) makeKnownFieldsFunc(si structInfo) {
|
2018-09-12 16:20:37 -07:00
|
|
|
mi.fields = map[pref.FieldNumber]*fieldInfo{}
|
2019-07-02 14:58:02 -07:00
|
|
|
for i := 0; i < mi.PBType.Fields().Len(); i++ {
|
|
|
|
fd := mi.PBType.Fields().Get(i)
|
2019-05-09 11:33:55 -07:00
|
|
|
fs := si.fieldsByNumber[fd.Number()]
|
2018-09-12 16:20:37 -07:00
|
|
|
var fi fieldInfo
|
|
|
|
switch {
|
2019-05-13 14:32:56 -07:00
|
|
|
case fd.ContainingOneof() != nil:
|
2019-07-06 13:05:11 -07:00
|
|
|
fi = fieldInfoForOneof(fd, si.oneofsByName[fd.ContainingOneof().Name()], mi.Exporter, si.oneofWrappersByNumber[fd.Number()])
|
2018-09-12 16:20:37 -07:00
|
|
|
case fd.IsMap():
|
2019-07-06 13:05:11 -07:00
|
|
|
fi = fieldInfoForMap(fd, fs, mi.Exporter)
|
2019-05-13 14:32:56 -07:00
|
|
|
case fd.IsList():
|
2019-07-06 13:05:11 -07:00
|
|
|
fi = fieldInfoForList(fd, fs, mi.Exporter)
|
2019-04-08 13:52:14 -07:00
|
|
|
case fd.IsWeak():
|
|
|
|
fi = fieldInfoForWeakMessage(fd, si.weakOffset)
|
2018-09-13 14:24:37 -07:00
|
|
|
case fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind:
|
2019-07-06 13:05:11 -07:00
|
|
|
fi = fieldInfoForMessage(fd, fs, mi.Exporter)
|
2018-09-13 14:24:37 -07:00
|
|
|
default:
|
2019-07-06 13:05:11 -07:00
|
|
|
fi = fieldInfoForScalar(fd, fs, mi.Exporter)
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
mi.fields[fd.Number()] = &fi
|
|
|
|
}
|
2019-04-03 13:40:53 -07:00
|
|
|
|
|
|
|
mi.oneofs = map[pref.Name]*oneofInfo{}
|
2019-07-02 14:58:02 -07:00
|
|
|
for i := 0; i < mi.PBType.Oneofs().Len(); i++ {
|
|
|
|
od := mi.PBType.Oneofs().Get(i)
|
2019-07-06 13:05:11 -07:00
|
|
|
mi.oneofs[od.Name()] = makeOneofInfo(od, si.oneofsByName[od.Name()], mi.Exporter, si.oneofWrappersByType)
|
2019-04-03 13:40:53 -07:00
|
|
|
}
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
2018-09-13 14:24:37 -07:00
|
|
|
|
2019-07-06 13:02:14 -07:00
|
|
|
func (mi *MessageInfo) makeUnknownFieldsFunc(t reflect.Type, si structInfo) {
|
2019-04-25 23:48:08 -07:00
|
|
|
mi.getUnknown = func(pointer) pref.RawFields { return nil }
|
|
|
|
mi.setUnknown = func(pointer, pref.RawFields) { return }
|
2019-07-06 13:02:14 -07:00
|
|
|
if si.unknownOffset.IsValid() {
|
2019-04-25 23:48:08 -07:00
|
|
|
mi.getUnknown = func(p pointer) pref.RawFields {
|
|
|
|
if p.IsNil() {
|
|
|
|
return nil
|
|
|
|
}
|
2019-07-06 13:02:14 -07:00
|
|
|
rv := p.Apply(si.unknownOffset).AsValueOf(unknownFieldsType)
|
2019-04-25 23:48:08 -07:00
|
|
|
return pref.RawFields(*rv.Interface().(*[]byte))
|
|
|
|
}
|
|
|
|
mi.setUnknown = func(p pointer, b pref.RawFields) {
|
|
|
|
if p.IsNil() {
|
|
|
|
panic("invalid SetUnknown on nil Message")
|
|
|
|
}
|
2019-07-06 13:02:14 -07:00
|
|
|
rv := p.Apply(si.unknownOffset).AsValueOf(unknownFieldsType)
|
2019-04-25 23:48:08 -07:00
|
|
|
*rv.Interface().(*[]byte) = []byte(b)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mi.getUnknown = func(pointer) pref.RawFields {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
mi.setUnknown = func(p pointer, _ pref.RawFields) {
|
|
|
|
if p.IsNil() {
|
|
|
|
panic("invalid SetUnknown on nil Message")
|
|
|
|
}
|
|
|
|
}
|
2018-10-23 18:31:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 13:02:14 -07:00
|
|
|
func (mi *MessageInfo) makeExtensionFieldsFunc(t reflect.Type, si structInfo) {
|
|
|
|
if si.extensionOffset.IsValid() {
|
2019-04-25 23:48:08 -07:00
|
|
|
mi.extensionMap = func(p pointer) *extensionMap {
|
2019-06-06 13:01:53 -07:00
|
|
|
if p.IsNil() {
|
|
|
|
return (*extensionMap)(nil)
|
|
|
|
}
|
2019-07-06 13:02:14 -07:00
|
|
|
v := p.Apply(si.extensionOffset).AsValueOf(extensionFieldsType)
|
2019-04-25 23:48:08 -07:00
|
|
|
return (*extensionMap)(v.Interface().(*map[int32]ExtensionField))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mi.extensionMap = func(pointer) *extensionMap {
|
|
|
|
return (*extensionMap)(nil)
|
|
|
|
}
|
2018-10-23 18:31:18 -07:00
|
|
|
}
|
|
|
|
}
|