aboutsummaryrefslogtreecommitdiff
path: root/internal/impl/extension.go
blob: e904fd993657c613484e0b3d127cd7cdb1548c87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 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 impl

import (
	"reflect"
	"sync"
	"sync/atomic"

	pref "google.golang.org/protobuf/reflect/protoreflect"
	piface "google.golang.org/protobuf/runtime/protoiface"
)

// ExtensionInfo implements ExtensionType.
//
// This type contains a number of exported fields for legacy compatibility.
// The only non-deprecated use of this type is through the methods of the
// ExtensionType interface.
type ExtensionInfo struct {
	// An ExtensionInfo may exist in several stages of initialization.
	//
	// extensionInfoUninitialized: Some or all of the legacy exported
	// fields may be set, but none of the unexported fields have been
	// initialized. This is the starting state for an ExtensionInfo
	// in legacy generated code.
	//
	// extensionInfoDescInit: The desc field is set, but other unexported fields
	// may not be initialized. Legacy exported fields may or may not be set.
	// This is the starting state for an ExtensionInfo in newly generated code.
	//
	// extensionInfoFullInit: The ExtensionInfo is fully initialized.
	// This state is only entered after lazy initialization is complete.
	init uint32
	mu   sync.Mutex

	goType reflect.Type
	desc   extensionTypeDescriptor
	conv   Converter
	info   *extensionFieldInfo // for fast-path method implementations

	// ExtendedType is a typed nil-pointer to the parent message type that
	// is being extended. It is possible for this to be unpopulated in v2
	// since the message may no longer implement the MessageV1 interface.
	//
	// Deprecated: Use the ExtendedType method instead.
	ExtendedType piface.MessageV1

	// ExtensionType is the zero value of the extension type.
	//
	// For historical reasons, reflect.TypeOf(ExtensionType) and the
	// type returned by InterfaceOf may not be identical.
	//
	// Deprecated: Use InterfaceOf(xt.Zero()) instead.
	ExtensionType interface{}

	// Field is the field number of the extension.
	//
	// Deprecated: Use the Descriptor().Number method instead.
	Field int32

	// Name is the fully qualified name of extension.
	//
	// Deprecated: Use the Descriptor().FullName method instead.
	Name string

	// Tag is the protobuf struct tag used in the v1 API.
	//
	// Deprecated: Do not use.
	Tag string

	// Filename is the proto filename in which the extension is defined.
	//
	// Deprecated: Use Descriptor().ParentFile().Path() instead.
	Filename string
}

// Stages of initialization: See the ExtensionInfo.init field.
const (
	extensionInfoUninitialized = 0
	extensionInfoDescInit      = 1
	extensionInfoFullInit      = 2
)

func InitExtensionInfo(xi *ExtensionInfo, xd pref.ExtensionDescriptor, goType reflect.Type) {
	xi.goType = goType
	xi.desc = extensionTypeDescriptor{xd, xi}
	xi.init = extensionInfoDescInit
}

func (xi *ExtensionInfo) New() pref.Value {
	return xi.lazyInit().New()
}
func (xi *ExtensionInfo) Zero() pref.Value {
	return xi.lazyInit().Zero()
}
func (xi *ExtensionInfo) ValueOf(v interface{}) pref.Value {
	return xi.lazyInit().PBValueOf(reflect.ValueOf(v))
}
func (xi *ExtensionInfo) InterfaceOf(v pref.Value) interface{} {
	return xi.lazyInit().GoValueOf(v).Interface()
}
func (xi *ExtensionInfo) IsValidValue(v pref.Value) bool {
	return xi.lazyInit().IsValidPB(v)
}
func (xi *ExtensionInfo) IsValidInterface(v interface{}) bool {
	return xi.lazyInit().IsValidGo(reflect.ValueOf(v))
}
func (xi *ExtensionInfo) TypeDescriptor() pref.ExtensionTypeDescriptor {
	if atomic.LoadUint32(&xi.init) < extensionInfoDescInit {
		xi.lazyInitSlow()
	}
	return &xi.desc
}

func (xi *ExtensionInfo) lazyInit() Converter {
	if atomic.LoadUint32(&xi.init) < extensionInfoFullInit {
		xi.lazyInitSlow()
	}
	return xi.conv
}

func (xi *ExtensionInfo) lazyInitSlow() {
	xi.mu.Lock()
	defer xi.mu.Unlock()

	if xi.init == extensionInfoFullInit {
		return
	}
	defer atomic.StoreUint32(&xi.init, extensionInfoFullInit)

	if xi.desc.ExtensionDescriptor == nil {
		xi.initFromLegacy()
	}
	if !xi.desc.ExtensionDescriptor.IsPlaceholder() {
		if xi.ExtensionType == nil {
			xi.initToLegacy()
		}
		xi.conv = NewConverter(xi.goType, xi.desc.ExtensionDescriptor)
		xi.info = makeExtensionFieldInfo(xi.desc.ExtensionDescriptor)
		xi.info.validation = newValidationInfo(xi.desc.ExtensionDescriptor, xi.goType)
	}
}

type extensionTypeDescriptor struct {
	pref.ExtensionDescriptor
	xi *ExtensionInfo
}

func (xtd *extensionTypeDescriptor) Type() pref.ExtensionType {
	return xtd.xi
}
func (xtd *extensionTypeDescriptor) Descriptor() pref.ExtensionDescriptor {
	return xtd.ExtensionDescriptor
}