blob: 7cd05bf94c52a227daff049706142b1c9dcef7e3 [file] [log] [blame]
Joe Tsai90fe9962018-10-18 11:06:29 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Joe Tsai21ade492019-05-22 13:42:54 -04005package impl
Joe Tsai90fe9962018-10-18 11:06:29 -07006
7import (
8 "fmt"
Joe Tsai90fe9962018-10-18 11:06:29 -07009 "reflect"
10 "sync"
11
Damien Neile89e6242019-05-13 23:55:40 -070012 pvalue "google.golang.org/protobuf/internal/value"
Joe Tsaid8881392019-06-06 13:01:53 -070013 "google.golang.org/protobuf/reflect/protoreflect"
Damien Neile89e6242019-05-13 23:55:40 -070014 pref "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsaib2f66be2019-05-22 00:42:45 -040015 "google.golang.org/protobuf/reflect/prototype"
Joe Tsai90fe9962018-10-18 11:06:29 -070016)
17
Joe Tsai21ade492019-05-22 13:42:54 -040018// legacyWrapEnum wraps v as a protoreflect.Enum,
Joe Tsai08e00302018-11-26 22:32:06 -080019// where v must be a int32 kind and not implement the v2 API already.
Joe Tsai21ade492019-05-22 13:42:54 -040020func legacyWrapEnum(v reflect.Value) pref.Enum {
21 et := legacyLoadEnumType(v.Type())
Joe Tsaif0c01e42018-11-06 13:05:20 -080022 return et.New(pref.EnumNumber(v.Int()))
23}
24
Joe Tsai21ade492019-05-22 13:42:54 -040025var legacyEnumTypeCache sync.Map // map[reflect.Type]protoreflect.EnumType
Joe Tsai6f9095c2018-11-10 14:12:21 -080026
Joe Tsai21ade492019-05-22 13:42:54 -040027// legacyLoadEnumType dynamically loads a protoreflect.EnumType for t,
Joe Tsaif0c01e42018-11-06 13:05:20 -080028// where t must be an int32 kind and not implement the v2 API already.
Joe Tsai21ade492019-05-22 13:42:54 -040029func legacyLoadEnumType(t reflect.Type) pref.EnumType {
Joe Tsai6f9095c2018-11-10 14:12:21 -080030 // Fast-path: check if a EnumType is cached for this concrete type.
Joe Tsai21ade492019-05-22 13:42:54 -040031 if et, ok := legacyEnumTypeCache.Load(t); ok {
Joe Tsaif0c01e42018-11-06 13:05:20 -080032 return et.(pref.EnumType)
Joe Tsai6f9095c2018-11-10 14:12:21 -080033 }
34
35 // Slow-path: derive enum descriptor and initialize EnumType.
Joe Tsaib2f66be2019-05-22 00:42:45 -040036 var et pref.EnumType
Joe Tsai6f9095c2018-11-10 14:12:21 -080037 var m sync.Map // map[protoreflect.EnumNumber]proto.Enum
Joe Tsai21ade492019-05-22 13:42:54 -040038 ed := LegacyLoadEnumDesc(t)
Joe Tsaib2f66be2019-05-22 00:42:45 -040039 et = &prototype.Enum{
40 EnumDescriptor: ed,
41 NewEnum: func(n pref.EnumNumber) pref.Enum {
42 if e, ok := m.Load(n); ok {
43 return e.(pref.Enum)
44 }
Joe Tsai21ade492019-05-22 13:42:54 -040045 e := &legacyEnumWrapper{num: n, pbTyp: et, goTyp: t}
Joe Tsaib2f66be2019-05-22 00:42:45 -040046 m.Store(n, e)
47 return e
48 },
49 }
Joe Tsai21ade492019-05-22 13:42:54 -040050 if et, ok := legacyEnumTypeCache.LoadOrStore(t, et); ok {
Joe Tsaib9365042019-03-19 14:14:29 -070051 return et.(pref.EnumType)
52 }
53 return et
Joe Tsai6f9095c2018-11-10 14:12:21 -080054}
55
Joe Tsai21ade492019-05-22 13:42:54 -040056type legacyEnumWrapper struct {
Joe Tsai6f9095c2018-11-10 14:12:21 -080057 num pref.EnumNumber
58 pbTyp pref.EnumType
59 goTyp reflect.Type
60}
61
Joe Tsai21ade492019-05-22 13:42:54 -040062func (e *legacyEnumWrapper) Descriptor() pref.EnumDescriptor {
Joe Tsai0fc49f82019-05-01 12:29:25 -070063 return e.pbTyp.Descriptor()
64}
Joe Tsai21ade492019-05-22 13:42:54 -040065func (e *legacyEnumWrapper) Number() pref.EnumNumber {
Joe Tsai0fc49f82019-05-01 12:29:25 -070066 return e.num
67}
Joe Tsai21ade492019-05-22 13:42:54 -040068func (e *legacyEnumWrapper) ProtoReflect() pref.Enum {
Joe Tsai6f9095c2018-11-10 14:12:21 -080069 return e
70}
Joe Tsai21ade492019-05-22 13:42:54 -040071func (e *legacyEnumWrapper) ProtoUnwrap() interface{} {
Joe Tsai6f9095c2018-11-10 14:12:21 -080072 v := reflect.New(e.goTyp).Elem()
73 v.SetInt(int64(e.num))
74 return v.Interface()
75}
76
77var (
Joe Tsai21ade492019-05-22 13:42:54 -040078 _ pref.Enum = (*legacyEnumWrapper)(nil)
79 _ pvalue.Unwrapper = (*legacyEnumWrapper)(nil)
Joe Tsai6f9095c2018-11-10 14:12:21 -080080)
81
Joe Tsai21ade492019-05-22 13:42:54 -040082var legacyEnumDescCache sync.Map // map[reflect.Type]protoreflect.EnumDescriptor
Joe Tsai90fe9962018-10-18 11:06:29 -070083
Joe Tsai21ade492019-05-22 13:42:54 -040084var legacyEnumNumberType = reflect.TypeOf(pref.EnumNumber(0))
Joe Tsaif0c01e42018-11-06 13:05:20 -080085
Joe Tsai21ade492019-05-22 13:42:54 -040086// LegacyLoadEnumDesc returns an EnumDescriptor derived from the Go type,
Joe Tsai90fe9962018-10-18 11:06:29 -070087// which must be an int32 kind and not implement the v2 API already.
Joe Tsai35ec98f2019-03-25 14:41:32 -070088//
89// This is exported for testing purposes.
Joe Tsai21ade492019-05-22 13:42:54 -040090func LegacyLoadEnumDesc(t reflect.Type) pref.EnumDescriptor {
Joe Tsai90fe9962018-10-18 11:06:29 -070091 // Fast-path: check if an EnumDescriptor is cached for this concrete type.
Joe Tsai21ade492019-05-22 13:42:54 -040092 if ed, ok := legacyEnumDescCache.Load(t); ok {
Joe Tsaib9365042019-03-19 14:14:29 -070093 return ed.(pref.EnumDescriptor)
Joe Tsai90fe9962018-10-18 11:06:29 -070094 }
95
Joe Tsaid8881392019-06-06 13:01:53 -070096 // Slow-path: initialize EnumDescriptor from the raw descriptor.
Joe Tsai90fe9962018-10-18 11:06:29 -070097 ev := reflect.Zero(t).Interface()
Damien Neila8593ba2019-01-08 16:18:07 -080098 if _, ok := ev.(pref.Enum); ok {
Joe Tsai90fe9962018-10-18 11:06:29 -070099 panic(fmt.Sprintf("%v already implements proto.Enum", t))
100 }
Joe Tsaid8881392019-06-06 13:01:53 -0700101 edV1, ok := ev.(enumV1)
102 if !ok {
103 panic(fmt.Sprintf("enum %v is no longer supported; please regenerate", t))
Joe Tsai90fe9962018-10-18 11:06:29 -0700104 }
Joe Tsaid8881392019-06-06 13:01:53 -0700105 b, idxs := edV1.EnumDescriptor()
Joe Tsai90fe9962018-10-18 11:06:29 -0700106
Joe Tsaid8881392019-06-06 13:01:53 -0700107 var ed pref.EnumDescriptor
108 if len(idxs) == 1 {
109 ed = legacyLoadFileDesc(b).Enums().Get(idxs[0])
110 } else {
111 md := legacyLoadFileDesc(b).Messages().Get(idxs[0])
112 for _, i := range idxs[1 : len(idxs)-1] {
113 md = md.Messages().Get(i)
114 }
115 ed = md.Enums().Get(idxs[len(idxs)-1])
Joe Tsai90fe9962018-10-18 11:06:29 -0700116 }
Joe Tsai21ade492019-05-22 13:42:54 -0400117 if ed, ok := legacyEnumDescCache.LoadOrStore(t, ed); ok {
Joe Tsaid8881392019-06-06 13:01:53 -0700118 return ed.(protoreflect.EnumDescriptor)
Joe Tsaib9365042019-03-19 14:14:29 -0700119 }
Joe Tsai90fe9962018-10-18 11:06:29 -0700120 return ed
121}