|
|
@@ -0,0 +1,358 @@
|
|
|
+// Copyright 2019 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 prototype provides constructors for protoreflect.EnumType,
|
|
|
+// protoreflect.MessageType, and protoreflect.ExtensionType.
|
|
|
+package prototype
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "reflect"
|
|
|
+ "sync"
|
|
|
+
|
|
|
+ "google.golang.org/protobuf/internal/descfmt"
|
|
|
+ "google.golang.org/protobuf/internal/value"
|
|
|
+ "google.golang.org/protobuf/reflect/protoreflect"
|
|
|
+)
|
|
|
+
|
|
|
+// Enum is a protoreflect.EnumType which combines a
|
|
|
+// protoreflect.EnumDescriptor with a constructor function.
|
|
|
+//
|
|
|
+// Both EnumDescriptor and NewEnum must be populated.
|
|
|
+// Once constructed, the exported fields must not be modified.
|
|
|
+type Enum struct {
|
|
|
+ protoreflect.EnumDescriptor
|
|
|
+
|
|
|
+ // NewEnum constructs a new protoreflect.Enum representing the provided
|
|
|
+ // enum number. The returned Go type must be identical for every call.
|
|
|
+ NewEnum func(protoreflect.EnumNumber) protoreflect.Enum
|
|
|
+
|
|
|
+ once sync.Once
|
|
|
+ goType reflect.Type
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Enum) New(n protoreflect.EnumNumber) protoreflect.Enum {
|
|
|
+ e := t.NewEnum(n)
|
|
|
+ t.once.Do(func() {
|
|
|
+ t.goType = reflect.TypeOf(e)
|
|
|
+ if e.Descriptor() != t.Descriptor() {
|
|
|
+ panic(fmt.Sprintf("mismatching enum descriptor: got %v, want %v", e.Descriptor(), t.Descriptor()))
|
|
|
+ }
|
|
|
+ if e.Descriptor().IsPlaceholder() {
|
|
|
+ panic("enum descriptor must not be a placeholder")
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if t.goType != reflect.TypeOf(e) {
|
|
|
+ panic(fmt.Sprintf("mismatching types for enum: got %T, want %v", e, t.goType))
|
|
|
+ }
|
|
|
+ return e
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Enum) GoType() reflect.Type {
|
|
|
+ t.New(0) // initialize t.typ
|
|
|
+ return t.goType
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Enum) Descriptor() protoreflect.EnumDescriptor {
|
|
|
+ return t.EnumDescriptor
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Enum) Format(s fmt.State, r rune) {
|
|
|
+ descfmt.FormatDesc(s, r, t)
|
|
|
+}
|
|
|
+
|
|
|
+// Message is a protoreflect.MessageType which combines a
|
|
|
+// protoreflect.MessageDescriptor with a constructor function.
|
|
|
+//
|
|
|
+// Both MessageDescriptor and NewMessage must be populated.
|
|
|
+// Once constructed, the exported fields must not be modified.
|
|
|
+type Message struct {
|
|
|
+ protoreflect.MessageDescriptor
|
|
|
+
|
|
|
+ // NewMessage constructs an empty, newly allocated protoreflect.Message.
|
|
|
+ // The returned Go type must be identical for every call.
|
|
|
+ NewMessage func() protoreflect.Message
|
|
|
+
|
|
|
+ once sync.Once
|
|
|
+ goType reflect.Type
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Message) New() protoreflect.Message {
|
|
|
+ m := t.NewMessage()
|
|
|
+ mi := m.Interface()
|
|
|
+ t.once.Do(func() {
|
|
|
+ t.goType = reflect.TypeOf(mi)
|
|
|
+ if m.Descriptor() != t.Descriptor() {
|
|
|
+ panic(fmt.Sprintf("mismatching message descriptor: got %v, want %v", m.Descriptor(), t.Descriptor()))
|
|
|
+ }
|
|
|
+ if m.Descriptor().IsPlaceholder() {
|
|
|
+ panic("message descriptor must not be a placeholder")
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if t.goType != reflect.TypeOf(mi) {
|
|
|
+ panic(fmt.Sprintf("mismatching types for message: got %T, want %v", mi, t.goType))
|
|
|
+ }
|
|
|
+ return m
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Message) GoType() reflect.Type {
|
|
|
+ t.New() // initialize t.goType
|
|
|
+ return t.goType
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Message) Descriptor() protoreflect.MessageDescriptor {
|
|
|
+ return t.MessageDescriptor
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Message) Format(s fmt.State, r rune) {
|
|
|
+ descfmt.FormatDesc(s, r, t)
|
|
|
+}
|
|
|
+
|
|
|
+// Extension is a protoreflect.ExtensionType which combines a
|
|
|
+// protoreflect.ExtensionDescriptor with a constructor function.
|
|
|
+//
|
|
|
+// ExtensionDescriptor must be populated, while NewEnum or NewMessage must
|
|
|
+// populated depending on the kind of the extension field.
|
|
|
+// Once constructed, the exported fields must not be modified.
|
|
|
+type Extension struct {
|
|
|
+ protoreflect.ExtensionDescriptor
|
|
|
+
|
|
|
+ // NewEnum constructs a new enum (see Enum.NewEnum).
|
|
|
+ // This must be populated if and only if ExtensionDescriptor.Kind
|
|
|
+ // is a protoreflect.EnumKind.
|
|
|
+ NewEnum func(protoreflect.EnumNumber) protoreflect.Enum
|
|
|
+
|
|
|
+ // NewMessage constructs a new message (see Enum.NewMessage).
|
|
|
+ // This must be populated if and only if ExtensionDescriptor.Kind
|
|
|
+ // is a protoreflect.MessageKind or protoreflect.GroupKind.
|
|
|
+ NewMessage func() protoreflect.Message
|
|
|
+
|
|
|
+ // TODO: Allow users to manually set new, valueOf, and interfaceOf.
|
|
|
+ // This allows users to implement custom composite types (e.g., List) or
|
|
|
+ // custom Go types for primitives (e.g., int32).
|
|
|
+
|
|
|
+ once sync.Once
|
|
|
+ goType reflect.Type
|
|
|
+ new func() protoreflect.Value
|
|
|
+ valueOf func(v interface{}) protoreflect.Value
|
|
|
+ interfaceOf func(v protoreflect.Value) interface{}
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Extension) New() protoreflect.Value {
|
|
|
+ t.lazyInit()
|
|
|
+ pv := t.new()
|
|
|
+ v := t.interfaceOf(pv)
|
|
|
+ if reflect.TypeOf(v) != t.goType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.goType))
|
|
|
+ }
|
|
|
+ return pv
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Extension) ValueOf(v interface{}) protoreflect.Value {
|
|
|
+ t.lazyInit()
|
|
|
+ if reflect.TypeOf(v) != t.goType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.goType))
|
|
|
+ }
|
|
|
+ return t.valueOf(v)
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Extension) InterfaceOf(v protoreflect.Value) interface{} {
|
|
|
+ t.lazyInit()
|
|
|
+ vi := t.interfaceOf(v)
|
|
|
+ if reflect.TypeOf(vi) != t.goType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %T, want %v", vi, t.goType))
|
|
|
+ }
|
|
|
+ return vi
|
|
|
+}
|
|
|
+
|
|
|
+// GoType is the type of the extension field.
|
|
|
+// The type is T for scalars and *[]T for lists (maps are not allowed).
|
|
|
+// The type T is determined as follows:
|
|
|
+//
|
|
|
+// +------------+-------------------------------------+
|
|
|
+// | Go type | Protobuf kind |
|
|
|
+// +------------+-------------------------------------+
|
|
|
+// | bool | BoolKind |
|
|
|
+// | int32 | Int32Kind, Sint32Kind, Sfixed32Kind |
|
|
|
+// | int64 | Int64Kind, Sint64Kind, Sfixed64Kind |
|
|
|
+// | uint32 | Uint32Kind, Fixed32Kind |
|
|
|
+// | uint64 | Uint64Kind, Fixed64Kind |
|
|
|
+// | float32 | FloatKind |
|
|
|
+// | float64 | DoubleKind |
|
|
|
+// | string | StringKind |
|
|
|
+// | []byte | BytesKind |
|
|
|
+// | E | EnumKind |
|
|
|
+// | M | MessageKind, GroupKind |
|
|
|
+// +------------+-------------------------------------+
|
|
|
+//
|
|
|
+// The type E is the concrete enum type returned by NewEnum,
|
|
|
+// which is often, but not required to be, a named int32 type.
|
|
|
+// The type M is the concrete message type returned by NewMessage,
|
|
|
+// which is often, but not required to be, a pointer to a named struct type.
|
|
|
+func (t *Extension) GoType() reflect.Type {
|
|
|
+ t.lazyInit()
|
|
|
+ return t.goType
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Extension) Descriptor() protoreflect.ExtensionDescriptor {
|
|
|
+ return t.ExtensionDescriptor
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Extension) Format(s fmt.State, r rune) {
|
|
|
+ descfmt.FormatDesc(s, r, t)
|
|
|
+}
|
|
|
+
|
|
|
+func (t *Extension) lazyInit() {
|
|
|
+ t.once.Do(func() {
|
|
|
+ switch t.Kind() {
|
|
|
+ case protoreflect.EnumKind:
|
|
|
+ if t.NewEnum == nil || t.NewMessage != nil {
|
|
|
+ panic("NewEnum alone must be set")
|
|
|
+ }
|
|
|
+ e := t.NewEnum(0)
|
|
|
+ if e.Descriptor() != t.Enum() {
|
|
|
+ panic(fmt.Sprintf("mismatching enum descriptor: got %v, want %v", e.Descriptor(), t.Enum()))
|
|
|
+ }
|
|
|
+ t.goType = reflect.TypeOf(e)
|
|
|
+ case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
|
+ if t.NewEnum != nil || t.NewMessage == nil {
|
|
|
+ panic("NewMessage alone must be set")
|
|
|
+ }
|
|
|
+ m := t.NewMessage()
|
|
|
+ if m.Descriptor() != t.Message() {
|
|
|
+ panic(fmt.Sprintf("mismatching message descriptor: got %v, want %v", m.Descriptor(), t.Message()))
|
|
|
+ }
|
|
|
+ t.goType = reflect.TypeOf(m.Interface())
|
|
|
+ default:
|
|
|
+ if t.NewEnum != nil || t.NewMessage != nil {
|
|
|
+ panic("neither NewEnum nor NewMessage may be set")
|
|
|
+ }
|
|
|
+ t.goType = goTypeForPBKind[t.Kind()]
|
|
|
+ }
|
|
|
+
|
|
|
+ switch t.Cardinality() {
|
|
|
+ case protoreflect.Optional:
|
|
|
+ switch t.Kind() {
|
|
|
+ case protoreflect.EnumKind:
|
|
|
+ t.new = func() protoreflect.Value {
|
|
|
+ return t.Default()
|
|
|
+ }
|
|
|
+ t.valueOf = func(v interface{}) protoreflect.Value {
|
|
|
+ ev := v.(protoreflect.Enum)
|
|
|
+ return protoreflect.ValueOf(ev.Number())
|
|
|
+ }
|
|
|
+ t.interfaceOf = func(v protoreflect.Value) interface{} {
|
|
|
+ return t.NewEnum(v.Enum())
|
|
|
+ }
|
|
|
+ case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
|
+ t.new = func() protoreflect.Value {
|
|
|
+ return protoreflect.ValueOf(t.NewMessage())
|
|
|
+ }
|
|
|
+ t.valueOf = func(v interface{}) protoreflect.Value {
|
|
|
+ mv := v.(protoreflect.ProtoMessage).ProtoReflect()
|
|
|
+ return protoreflect.ValueOf(mv)
|
|
|
+ }
|
|
|
+ t.interfaceOf = func(v protoreflect.Value) interface{} {
|
|
|
+ return v.Message().Interface()
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ t.new = func() protoreflect.Value {
|
|
|
+ v := t.Default()
|
|
|
+ if t.Kind() == protoreflect.BytesKind {
|
|
|
+ // Copy default bytes to avoid aliasing the original.
|
|
|
+ v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...))
|
|
|
+ }
|
|
|
+ return v
|
|
|
+ }
|
|
|
+ t.valueOf = func(v interface{}) protoreflect.Value {
|
|
|
+ return protoreflect.ValueOf(v)
|
|
|
+ }
|
|
|
+ t.interfaceOf = func(v protoreflect.Value) interface{} {
|
|
|
+ return v.Interface()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case protoreflect.Repeated:
|
|
|
+ var conv value.Converter
|
|
|
+ elemType := t.goType
|
|
|
+ switch t.Kind() {
|
|
|
+ case protoreflect.EnumKind:
|
|
|
+ conv = value.Converter{
|
|
|
+ PBValueOf: func(v reflect.Value) protoreflect.Value {
|
|
|
+ if v.Type() != elemType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), elemType))
|
|
|
+ }
|
|
|
+ e := v.Interface().(protoreflect.Enum)
|
|
|
+ return protoreflect.ValueOf(e.Number())
|
|
|
+ },
|
|
|
+ GoValueOf: func(v protoreflect.Value) reflect.Value {
|
|
|
+ rv := reflect.ValueOf(t.NewEnum(v.Enum()))
|
|
|
+ if rv.Type() != elemType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), elemType))
|
|
|
+ }
|
|
|
+ return rv
|
|
|
+ },
|
|
|
+ NewEnum: t.NewEnum,
|
|
|
+ }
|
|
|
+ case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
|
+ conv = value.Converter{
|
|
|
+ PBValueOf: func(v reflect.Value) protoreflect.Value {
|
|
|
+ if v.Type() != elemType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), elemType))
|
|
|
+ }
|
|
|
+ m := v.Interface().(protoreflect.ProtoMessage).ProtoReflect()
|
|
|
+ return protoreflect.ValueOf(m)
|
|
|
+ },
|
|
|
+ GoValueOf: func(v protoreflect.Value) reflect.Value {
|
|
|
+ rv := reflect.ValueOf(v.Message().Interface())
|
|
|
+ if rv.Type() != elemType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), elemType))
|
|
|
+ }
|
|
|
+ return rv
|
|
|
+ },
|
|
|
+ NewMessage: t.NewMessage,
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ conv = value.NewConverter(elemType, t.Kind())
|
|
|
+ }
|
|
|
+
|
|
|
+ t.goType = reflect.PtrTo(reflect.SliceOf(elemType))
|
|
|
+ t.new = func() protoreflect.Value {
|
|
|
+ v := reflect.New(t.goType.Elem()).Interface()
|
|
|
+ return protoreflect.ValueOf(value.ListOf(v, conv))
|
|
|
+ }
|
|
|
+ t.valueOf = func(v interface{}) protoreflect.Value {
|
|
|
+ return protoreflect.ValueOf(value.ListOf(v, conv))
|
|
|
+ }
|
|
|
+ t.interfaceOf = func(v protoreflect.Value) interface{} {
|
|
|
+ return v.List().(value.Unwrapper).ProtoUnwrap()
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ panic(fmt.Sprintf("invalid cardinality: %v", t.Cardinality()))
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{
|
|
|
+ protoreflect.BoolKind: reflect.TypeOf(bool(false)),
|
|
|
+ protoreflect.Int32Kind: reflect.TypeOf(int32(0)),
|
|
|
+ protoreflect.Sint32Kind: reflect.TypeOf(int32(0)),
|
|
|
+ protoreflect.Sfixed32Kind: reflect.TypeOf(int32(0)),
|
|
|
+ protoreflect.Int64Kind: reflect.TypeOf(int64(0)),
|
|
|
+ protoreflect.Sint64Kind: reflect.TypeOf(int64(0)),
|
|
|
+ protoreflect.Sfixed64Kind: reflect.TypeOf(int64(0)),
|
|
|
+ protoreflect.Uint32Kind: reflect.TypeOf(uint32(0)),
|
|
|
+ protoreflect.Fixed32Kind: reflect.TypeOf(uint32(0)),
|
|
|
+ protoreflect.Uint64Kind: reflect.TypeOf(uint64(0)),
|
|
|
+ protoreflect.Fixed64Kind: reflect.TypeOf(uint64(0)),
|
|
|
+ protoreflect.FloatKind: reflect.TypeOf(float32(0)),
|
|
|
+ protoreflect.DoubleKind: reflect.TypeOf(float64(0)),
|
|
|
+ protoreflect.StringKind: reflect.TypeOf(string("")),
|
|
|
+ protoreflect.BytesKind: reflect.TypeOf([]byte(nil)),
|
|
|
+}
|
|
|
+
|
|
|
+var (
|
|
|
+ _ protoreflect.EnumType = (*Enum)(nil)
|
|
|
+ _ protoreflect.MessageType = (*Message)(nil)
|
|
|
+ _ protoreflect.ExtensionType = (*Extension)(nil)
|
|
|
+)
|