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