blob: 972eb859cebfbed33cc88a03476e8474fb17b91d [file] [log] [blame]
Damien Neil5322bdb2019-04-09 15:57:05 -07001// Copyright 2019 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
5package impl
6
7import (
8 "sync"
9
10 "google.golang.org/protobuf/proto"
11 pref "google.golang.org/protobuf/reflect/protoreflect"
12)
13
14type errRequiredNotSet struct{}
15
16func (errRequiredNotSet) Error() string { return "proto: required field not set" }
17func (errRequiredNotSet) RequiredNotSet() bool { return true }
18
19func (mi *MessageInfo) isInitialized(msg proto.Message) error {
20 return mi.isInitializedPointer(pointerOfIface(msg))
21}
22
23func (mi *MessageInfo) isInitializedPointer(p pointer) error {
24 mi.init()
25 if !mi.needsInitCheck {
26 return nil
27 }
28 if p.IsNil() {
29 return errRequiredNotSet{}
30 }
31 if mi.extensionOffset.IsValid() {
32 e := p.Apply(mi.extensionOffset).Extensions()
33 if err := mi.isInitExtensions(e); err != nil {
34 return err
35 }
36 }
37 for _, f := range mi.fieldsOrdered {
38 if !f.isRequired && f.funcs.isInit == nil {
39 continue
40 }
41 fptr := p.Apply(f.offset)
42 if f.isPointer && fptr.Elem().IsNil() {
43 if f.isRequired {
44 return errRequiredNotSet{}
45 }
46 continue
47 }
48 if f.funcs.isInit == nil {
49 continue
50 }
51 if err := f.funcs.isInit(fptr); err != nil {
52 return err
53 }
54 }
55 return nil
56}
57
58func (mi *MessageInfo) isInitExtensions(ext *map[int32]ExtensionField) error {
59 if ext == nil {
60 return nil
61 }
62 for _, x := range *ext {
63 ei := mi.extensionFieldInfo(x.GetType())
64 if ei.funcs.isInit == nil {
65 continue
66 }
67 v := x.GetValue()
68 if v == nil {
69 continue
70 }
71 if err := ei.funcs.isInit(v); err != nil {
72 return err
73 }
74 }
75 return nil
76}
77
78var (
79 needsInitCheckMu sync.Mutex
80 needsInitCheckMap sync.Map
81)
82
83// needsInitCheck reports whether a message needs to be checked for partial initialization.
84//
85// It returns true if the message transitively includes any required or extension fields.
86func needsInitCheck(md pref.MessageDescriptor) bool {
87 if v, ok := needsInitCheckMap.Load(md); ok {
88 if has, ok := v.(bool); ok {
89 return has
90 }
91 }
92 needsInitCheckMu.Lock()
93 defer needsInitCheckMu.Unlock()
94 return needsInitCheckLocked(md)
95}
96
97func needsInitCheckLocked(md pref.MessageDescriptor) (has bool) {
98 if v, ok := needsInitCheckMap.Load(md); ok {
99 // If has is true, we've previously determined that this message
100 // needs init checks.
101 //
102 // If has is false, we've previously determined that it can never
103 // be uninitialized.
104 //
105 // If has is not a bool, we've just encountered a cycle in the
106 // message graph. In this case, it is safe to return false: If
107 // the message does have required fields, we'll detect them later
108 // in the graph traversal.
109 has, ok := v.(bool)
110 return ok && has
111 }
112 needsInitCheckMap.Store(md, struct{}{}) // avoid cycles while descending into this message
113 defer func() {
114 needsInitCheckMap.Store(md, has)
115 }()
116 if md.RequiredNumbers().Len() > 0 {
117 return true
118 }
119 if md.ExtensionRanges().Len() > 0 {
120 return true
121 }
122 for i := 0; i < md.Fields().Len(); i++ {
123 fd := md.Fields().Get(i)
124 // Map keys are never messages, so just consider the map value.
125 if fd.IsMap() {
126 fd = fd.MapValue()
127 }
128 fmd := fd.Message()
129 if fmd != nil && needsInitCheckLocked(fmd) {
130 return true
131 }
132 }
133 return false
134}