blob: b82341e575cb334ed4381504b2b516509201ccac [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 pref "google.golang.org/protobuf/reflect/protoreflect"
Damien Neil466dd772020-02-14 14:49:35 -080012 piface "google.golang.org/protobuf/runtime/protoiface"
Damien Neil5322bdb2019-04-09 15:57:05 -070013)
14
Joe Tsaif26a9e72020-02-20 10:05:37 -080015func (mi *MessageInfo) checkInitialized(in piface.CheckInitializedInput) (piface.CheckInitializedOutput, error) {
Joe Tsai0f81b382019-07-10 23:14:31 -070016 var p pointer
Damien Neil466dd772020-02-14 14:49:35 -080017 if ms, ok := in.Message.(*messageState); ok {
Joe Tsai0f81b382019-07-10 23:14:31 -070018 p = ms.pointer()
19 } else {
Damien Neil466dd772020-02-14 14:49:35 -080020 p = in.Message.(*messageReflectWrapper).pointer()
Joe Tsai0f81b382019-07-10 23:14:31 -070021 }
Joe Tsaif26a9e72020-02-20 10:05:37 -080022 return piface.CheckInitializedOutput{}, mi.checkInitializedPointer(p)
Damien Neil5322bdb2019-04-09 15:57:05 -070023}
24
Joe Tsaif26a9e72020-02-20 10:05:37 -080025func (mi *MessageInfo) checkInitializedPointer(p pointer) error {
Damien Neil5322bdb2019-04-09 15:57:05 -070026 mi.init()
27 if !mi.needsInitCheck {
28 return nil
29 }
30 if p.IsNil() {
Damien Neil3d0706a2019-07-09 11:40:49 -070031 for _, f := range mi.orderedCoderFields {
32 if f.isRequired {
Damien Neil16163b42019-08-06 15:43:25 -070033 return errors.RequiredNotSet(string(mi.Desc.Fields().ByNumber(f.num).FullName()))
Damien Neil3d0706a2019-07-09 11:40:49 -070034 }
35 }
36 return nil
Damien Neil5322bdb2019-04-09 15:57:05 -070037 }
38 if mi.extensionOffset.IsValid() {
39 e := p.Apply(mi.extensionOffset).Extensions()
40 if err := mi.isInitExtensions(e); err != nil {
41 return err
42 }
43 }
Damien Neil4ae30bb2019-06-20 10:12:23 -070044 for _, f := range mi.orderedCoderFields {
Damien Neil5322bdb2019-04-09 15:57:05 -070045 if !f.isRequired && f.funcs.isInit == nil {
46 continue
47 }
48 fptr := p.Apply(f.offset)
49 if f.isPointer && fptr.Elem().IsNil() {
50 if f.isRequired {
Damien Neil16163b42019-08-06 15:43:25 -070051 return errors.RequiredNotSet(string(mi.Desc.Fields().ByNumber(f.num).FullName()))
Damien Neil5322bdb2019-04-09 15:57:05 -070052 }
53 continue
54 }
55 if f.funcs.isInit == nil {
56 continue
57 }
Damien Neil316febd2020-02-09 12:26:50 -080058 if err := f.funcs.isInit(fptr, f); err != nil {
Damien Neil5322bdb2019-04-09 15:57:05 -070059 return err
60 }
61 }
62 return nil
63}
64
65func (mi *MessageInfo) isInitExtensions(ext *map[int32]ExtensionField) error {
66 if ext == nil {
67 return nil
68 }
69 for _, x := range *ext {
Damien Neil79571e92019-12-09 10:24:36 -080070 ei := getExtensionFieldInfo(x.Type())
Damien Neil5322bdb2019-04-09 15:57:05 -070071 if ei.funcs.isInit == nil {
72 continue
73 }
Damien Neil68b81c32019-08-22 11:41:32 -070074 v := x.Value()
75 if !v.IsValid() {
Damien Neil5322bdb2019-04-09 15:57:05 -070076 continue
77 }
78 if err := ei.funcs.isInit(v); err != nil {
79 return err
80 }
81 }
82 return nil
83}
84
85var (
86 needsInitCheckMu sync.Mutex
87 needsInitCheckMap sync.Map
88)
89
90// needsInitCheck reports whether a message needs to be checked for partial initialization.
91//
92// It returns true if the message transitively includes any required or extension fields.
93func needsInitCheck(md pref.MessageDescriptor) bool {
94 if v, ok := needsInitCheckMap.Load(md); ok {
95 if has, ok := v.(bool); ok {
96 return has
97 }
98 }
99 needsInitCheckMu.Lock()
100 defer needsInitCheckMu.Unlock()
101 return needsInitCheckLocked(md)
102}
103
104func needsInitCheckLocked(md pref.MessageDescriptor) (has bool) {
105 if v, ok := needsInitCheckMap.Load(md); ok {
106 // If has is true, we've previously determined that this message
107 // needs init checks.
108 //
109 // If has is false, we've previously determined that it can never
110 // be uninitialized.
111 //
112 // If has is not a bool, we've just encountered a cycle in the
113 // message graph. In this case, it is safe to return false: If
114 // the message does have required fields, we'll detect them later
115 // in the graph traversal.
116 has, ok := v.(bool)
117 return ok && has
118 }
119 needsInitCheckMap.Store(md, struct{}{}) // avoid cycles while descending into this message
120 defer func() {
121 needsInitCheckMap.Store(md, has)
122 }()
123 if md.RequiredNumbers().Len() > 0 {
124 return true
125 }
126 if md.ExtensionRanges().Len() > 0 {
127 return true
128 }
129 for i := 0; i < md.Fields().Len(); i++ {
130 fd := md.Fields().Get(i)
131 // Map keys are never messages, so just consider the map value.
132 if fd.IsMap() {
133 fd = fd.MapValue()
134 }
135 fmd := fd.Message()
136 if fmd != nil && needsInitCheckLocked(fmd) {
137 return true
138 }
139 }
140 return false
141}