vendor/google.golang.org/protobuf/internal/impl/legacy_enum.go
changeset 260 445e01aede7e
parent 256 6d9efbef00a9
equal deleted inserted replaced
259:db4911b0c721 260:445e01aede7e
    11 	"sync"
    11 	"sync"
    12 
    12 
    13 	"google.golang.org/protobuf/internal/filedesc"
    13 	"google.golang.org/protobuf/internal/filedesc"
    14 	"google.golang.org/protobuf/internal/strs"
    14 	"google.golang.org/protobuf/internal/strs"
    15 	"google.golang.org/protobuf/reflect/protoreflect"
    15 	"google.golang.org/protobuf/reflect/protoreflect"
    16 	pref "google.golang.org/protobuf/reflect/protoreflect"
       
    17 )
    16 )
    18 
    17 
    19 // legacyEnumName returns the name of enums used in legacy code.
    18 // legacyEnumName returns the name of enums used in legacy code.
    20 // It is neither the protobuf full name nor the qualified Go name,
    19 // It is neither the protobuf full name nor the qualified Go name,
    21 // but rather an odd hybrid of both.
    20 // but rather an odd hybrid of both.
    22 func legacyEnumName(ed pref.EnumDescriptor) string {
    21 func legacyEnumName(ed protoreflect.EnumDescriptor) string {
    23 	var protoPkg string
    22 	var protoPkg string
    24 	enumName := string(ed.FullName())
    23 	enumName := string(ed.FullName())
    25 	if fd := ed.ParentFile(); fd != nil {
    24 	if fd := ed.ParentFile(); fd != nil {
    26 		protoPkg = string(fd.Package())
    25 		protoPkg = string(fd.Package())
    27 		enumName = strings.TrimPrefix(enumName, protoPkg+".")
    26 		enumName = strings.TrimPrefix(enumName, protoPkg+".")
    32 	return protoPkg + "." + strs.GoCamelCase(enumName)
    31 	return protoPkg + "." + strs.GoCamelCase(enumName)
    33 }
    32 }
    34 
    33 
    35 // legacyWrapEnum wraps v as a protoreflect.Enum,
    34 // legacyWrapEnum wraps v as a protoreflect.Enum,
    36 // where v must be a int32 kind and not implement the v2 API already.
    35 // where v must be a int32 kind and not implement the v2 API already.
    37 func legacyWrapEnum(v reflect.Value) pref.Enum {
    36 func legacyWrapEnum(v reflect.Value) protoreflect.Enum {
    38 	et := legacyLoadEnumType(v.Type())
    37 	et := legacyLoadEnumType(v.Type())
    39 	return et.New(pref.EnumNumber(v.Int()))
    38 	return et.New(protoreflect.EnumNumber(v.Int()))
    40 }
    39 }
    41 
    40 
    42 var legacyEnumTypeCache sync.Map // map[reflect.Type]protoreflect.EnumType
    41 var legacyEnumTypeCache sync.Map // map[reflect.Type]protoreflect.EnumType
    43 
    42 
    44 // legacyLoadEnumType dynamically loads a protoreflect.EnumType for t,
    43 // legacyLoadEnumType dynamically loads a protoreflect.EnumType for t,
    45 // where t must be an int32 kind and not implement the v2 API already.
    44 // where t must be an int32 kind and not implement the v2 API already.
    46 func legacyLoadEnumType(t reflect.Type) pref.EnumType {
    45 func legacyLoadEnumType(t reflect.Type) protoreflect.EnumType {
    47 	// Fast-path: check if a EnumType is cached for this concrete type.
    46 	// Fast-path: check if a EnumType is cached for this concrete type.
    48 	if et, ok := legacyEnumTypeCache.Load(t); ok {
    47 	if et, ok := legacyEnumTypeCache.Load(t); ok {
    49 		return et.(pref.EnumType)
    48 		return et.(protoreflect.EnumType)
    50 	}
    49 	}
    51 
    50 
    52 	// Slow-path: derive enum descriptor and initialize EnumType.
    51 	// Slow-path: derive enum descriptor and initialize EnumType.
    53 	var et pref.EnumType
    52 	var et protoreflect.EnumType
    54 	ed := LegacyLoadEnumDesc(t)
    53 	ed := LegacyLoadEnumDesc(t)
    55 	et = &legacyEnumType{
    54 	et = &legacyEnumType{
    56 		desc:   ed,
    55 		desc:   ed,
    57 		goType: t,
    56 		goType: t,
    58 	}
    57 	}
    59 	if et, ok := legacyEnumTypeCache.LoadOrStore(t, et); ok {
    58 	if et, ok := legacyEnumTypeCache.LoadOrStore(t, et); ok {
    60 		return et.(pref.EnumType)
    59 		return et.(protoreflect.EnumType)
    61 	}
    60 	}
    62 	return et
    61 	return et
    63 }
    62 }
    64 
    63 
    65 type legacyEnumType struct {
    64 type legacyEnumType struct {
    66 	desc   pref.EnumDescriptor
    65 	desc   protoreflect.EnumDescriptor
    67 	goType reflect.Type
    66 	goType reflect.Type
    68 	m      sync.Map // map[protoreflect.EnumNumber]proto.Enum
    67 	m      sync.Map // map[protoreflect.EnumNumber]proto.Enum
    69 }
    68 }
    70 
    69 
    71 func (t *legacyEnumType) New(n pref.EnumNumber) pref.Enum {
    70 func (t *legacyEnumType) New(n protoreflect.EnumNumber) protoreflect.Enum {
    72 	if e, ok := t.m.Load(n); ok {
    71 	if e, ok := t.m.Load(n); ok {
    73 		return e.(pref.Enum)
    72 		return e.(protoreflect.Enum)
    74 	}
    73 	}
    75 	e := &legacyEnumWrapper{num: n, pbTyp: t, goTyp: t.goType}
    74 	e := &legacyEnumWrapper{num: n, pbTyp: t, goTyp: t.goType}
    76 	t.m.Store(n, e)
    75 	t.m.Store(n, e)
    77 	return e
    76 	return e
    78 }
    77 }
    79 func (t *legacyEnumType) Descriptor() pref.EnumDescriptor {
    78 func (t *legacyEnumType) Descriptor() protoreflect.EnumDescriptor {
    80 	return t.desc
    79 	return t.desc
    81 }
    80 }
    82 
    81 
    83 type legacyEnumWrapper struct {
    82 type legacyEnumWrapper struct {
    84 	num   pref.EnumNumber
    83 	num   protoreflect.EnumNumber
    85 	pbTyp pref.EnumType
    84 	pbTyp protoreflect.EnumType
    86 	goTyp reflect.Type
    85 	goTyp reflect.Type
    87 }
    86 }
    88 
    87 
    89 func (e *legacyEnumWrapper) Descriptor() pref.EnumDescriptor {
    88 func (e *legacyEnumWrapper) Descriptor() protoreflect.EnumDescriptor {
    90 	return e.pbTyp.Descriptor()
    89 	return e.pbTyp.Descriptor()
    91 }
    90 }
    92 func (e *legacyEnumWrapper) Type() pref.EnumType {
    91 func (e *legacyEnumWrapper) Type() protoreflect.EnumType {
    93 	return e.pbTyp
    92 	return e.pbTyp
    94 }
    93 }
    95 func (e *legacyEnumWrapper) Number() pref.EnumNumber {
    94 func (e *legacyEnumWrapper) Number() protoreflect.EnumNumber {
    96 	return e.num
    95 	return e.num
    97 }
    96 }
    98 func (e *legacyEnumWrapper) ProtoReflect() pref.Enum {
    97 func (e *legacyEnumWrapper) ProtoReflect() protoreflect.Enum {
    99 	return e
    98 	return e
   100 }
    99 }
   101 func (e *legacyEnumWrapper) protoUnwrap() interface{} {
   100 func (e *legacyEnumWrapper) protoUnwrap() interface{} {
   102 	v := reflect.New(e.goTyp).Elem()
   101 	v := reflect.New(e.goTyp).Elem()
   103 	v.SetInt(int64(e.num))
   102 	v.SetInt(int64(e.num))
   104 	return v.Interface()
   103 	return v.Interface()
   105 }
   104 }
   106 
   105 
   107 var (
   106 var (
   108 	_ pref.Enum = (*legacyEnumWrapper)(nil)
   107 	_ protoreflect.Enum = (*legacyEnumWrapper)(nil)
   109 	_ unwrapper = (*legacyEnumWrapper)(nil)
   108 	_ unwrapper         = (*legacyEnumWrapper)(nil)
   110 )
   109 )
   111 
   110 
   112 var legacyEnumDescCache sync.Map // map[reflect.Type]protoreflect.EnumDescriptor
   111 var legacyEnumDescCache sync.Map // map[reflect.Type]protoreflect.EnumDescriptor
   113 
   112 
   114 // LegacyLoadEnumDesc returns an EnumDescriptor derived from the Go type,
   113 // LegacyLoadEnumDesc returns an EnumDescriptor derived from the Go type,
   115 // which must be an int32 kind and not implement the v2 API already.
   114 // which must be an int32 kind and not implement the v2 API already.
   116 //
   115 //
   117 // This is exported for testing purposes.
   116 // This is exported for testing purposes.
   118 func LegacyLoadEnumDesc(t reflect.Type) pref.EnumDescriptor {
   117 func LegacyLoadEnumDesc(t reflect.Type) protoreflect.EnumDescriptor {
   119 	// Fast-path: check if an EnumDescriptor is cached for this concrete type.
   118 	// Fast-path: check if an EnumDescriptor is cached for this concrete type.
   120 	if ed, ok := legacyEnumDescCache.Load(t); ok {
   119 	if ed, ok := legacyEnumDescCache.Load(t); ok {
   121 		return ed.(pref.EnumDescriptor)
   120 		return ed.(protoreflect.EnumDescriptor)
   122 	}
   121 	}
   123 
   122 
   124 	// Slow-path: initialize EnumDescriptor from the raw descriptor.
   123 	// Slow-path: initialize EnumDescriptor from the raw descriptor.
   125 	ev := reflect.Zero(t).Interface()
   124 	ev := reflect.Zero(t).Interface()
   126 	if _, ok := ev.(pref.Enum); ok {
   125 	if _, ok := ev.(protoreflect.Enum); ok {
   127 		panic(fmt.Sprintf("%v already implements proto.Enum", t))
   126 		panic(fmt.Sprintf("%v already implements proto.Enum", t))
   128 	}
   127 	}
   129 	edV1, ok := ev.(enumV1)
   128 	edV1, ok := ev.(enumV1)
   130 	if !ok {
   129 	if !ok {
   131 		return aberrantLoadEnumDesc(t)
   130 		return aberrantLoadEnumDesc(t)
   132 	}
   131 	}
   133 	b, idxs := edV1.EnumDescriptor()
   132 	b, idxs := edV1.EnumDescriptor()
   134 
   133 
   135 	var ed pref.EnumDescriptor
   134 	var ed protoreflect.EnumDescriptor
   136 	if len(idxs) == 1 {
   135 	if len(idxs) == 1 {
   137 		ed = legacyLoadFileDesc(b).Enums().Get(idxs[0])
   136 		ed = legacyLoadFileDesc(b).Enums().Get(idxs[0])
   138 	} else {
   137 	} else {
   139 		md := legacyLoadFileDesc(b).Messages().Get(idxs[0])
   138 		md := legacyLoadFileDesc(b).Messages().Get(idxs[0])
   140 		for _, i := range idxs[1 : len(idxs)-1] {
   139 		for _, i := range idxs[1 : len(idxs)-1] {
   156 // If the type does not implement enumV1, then there is no reliable
   155 // If the type does not implement enumV1, then there is no reliable
   157 // way to derive the original protobuf type information.
   156 // way to derive the original protobuf type information.
   158 // We are unable to use the global enum registry since it is
   157 // We are unable to use the global enum registry since it is
   159 // unfortunately keyed by the protobuf full name, which we also do not know.
   158 // unfortunately keyed by the protobuf full name, which we also do not know.
   160 // Thus, this produces some bogus enum descriptor based on the Go type name.
   159 // Thus, this produces some bogus enum descriptor based on the Go type name.
   161 func aberrantLoadEnumDesc(t reflect.Type) pref.EnumDescriptor {
   160 func aberrantLoadEnumDesc(t reflect.Type) protoreflect.EnumDescriptor {
   162 	// Fast-path: check if an EnumDescriptor is cached for this concrete type.
   161 	// Fast-path: check if an EnumDescriptor is cached for this concrete type.
   163 	if ed, ok := aberrantEnumDescCache.Load(t); ok {
   162 	if ed, ok := aberrantEnumDescCache.Load(t); ok {
   164 		return ed.(pref.EnumDescriptor)
   163 		return ed.(protoreflect.EnumDescriptor)
   165 	}
   164 	}
   166 
   165 
   167 	// Slow-path: construct a bogus, but unique EnumDescriptor.
   166 	// Slow-path: construct a bogus, but unique EnumDescriptor.
   168 	ed := &filedesc.Enum{L2: new(filedesc.EnumL2)}
   167 	ed := &filedesc.Enum{L2: new(filedesc.EnumL2)}
   169 	ed.L0.FullName = AberrantDeriveFullName(t) // e.g., github_com.user.repo.MyEnum
   168 	ed.L0.FullName = AberrantDeriveFullName(t) // e.g., github_com.user.repo.MyEnum
   180 	// TODO: We could use the String method to obtain some enum value names by
   179 	// TODO: We could use the String method to obtain some enum value names by
   181 	// starting at 0 and print the enum until it produces invalid identifiers.
   180 	// starting at 0 and print the enum until it produces invalid identifiers.
   182 	// An exhaustive query is clearly impractical, but can be best-effort.
   181 	// An exhaustive query is clearly impractical, but can be best-effort.
   183 
   182 
   184 	if ed, ok := aberrantEnumDescCache.LoadOrStore(t, ed); ok {
   183 	if ed, ok := aberrantEnumDescCache.LoadOrStore(t, ed); ok {
   185 		return ed.(pref.EnumDescriptor)
   184 		return ed.(protoreflect.EnumDescriptor)
   186 	}
   185 	}
   187 	return ed
   186 	return ed
   188 }
   187 }
   189 
   188 
   190 // AberrantDeriveFullName derives a fully qualified protobuf name for the given Go type
   189 // AberrantDeriveFullName derives a fully qualified protobuf name for the given Go type
   191 // The provided name is not guaranteed to be stable nor universally unique.
   190 // The provided name is not guaranteed to be stable nor universally unique.
   192 // It should be sufficiently unique within a program.
   191 // It should be sufficiently unique within a program.
   193 //
   192 //
   194 // This is exported for testing purposes.
   193 // This is exported for testing purposes.
   195 func AberrantDeriveFullName(t reflect.Type) pref.FullName {
   194 func AberrantDeriveFullName(t reflect.Type) protoreflect.FullName {
   196 	sanitize := func(r rune) rune {
   195 	sanitize := func(r rune) rune {
   197 		switch {
   196 		switch {
   198 		case r == '/':
   197 		case r == '/':
   199 			return '.'
   198 			return '.'
   200 		case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', '0' <= r && r <= '9':
   199 		case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', '0' <= r && r <= '9':
   213 	for i, s := range ss {
   212 	for i, s := range ss {
   214 		if s == "" || ('0' <= s[0] && s[0] <= '9') {
   213 		if s == "" || ('0' <= s[0] && s[0] <= '9') {
   215 			ss[i] = "x" + s
   214 			ss[i] = "x" + s
   216 		}
   215 		}
   217 	}
   216 	}
   218 	return pref.FullName(strings.Join(ss, "."))
   217 	return protoreflect.FullName(strings.Join(ss, "."))
   219 }
   218 }