| // 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 ( |
| "sync" |
| |
| "google.golang.org/protobuf/internal/errors" |
| pref "google.golang.org/protobuf/reflect/protoreflect" |
| piface "google.golang.org/protobuf/runtime/protoiface" |
| ) |
| |
| func (mi *MessageInfo) checkInitialized(in piface.CheckInitializedInput) (piface.CheckInitializedOutput, error) { |
| var p pointer |
| if ms, ok := in.Message.(*messageState); ok { |
| p = ms.pointer() |
| } else { |
| p = in.Message.(*messageReflectWrapper).pointer() |
| } |
| return piface.CheckInitializedOutput{}, mi.checkInitializedPointer(p) |
| } |
| |
| func (mi *MessageInfo) checkInitializedPointer(p pointer) error { |
| mi.init() |
| if !mi.needsInitCheck { |
| return nil |
| } |
| if p.IsNil() { |
| for _, f := range mi.orderedCoderFields { |
| if f.isRequired { |
| return errors.RequiredNotSet(string(mi.Desc.Fields().ByNumber(f.num).FullName())) |
| } |
| } |
| return nil |
| } |
| if mi.extensionOffset.IsValid() { |
| e := p.Apply(mi.extensionOffset).Extensions() |
| if err := mi.isInitExtensions(e); err != nil { |
| return err |
| } |
| } |
| for _, f := range mi.orderedCoderFields { |
| if !f.isRequired && f.funcs.isInit == nil { |
| continue |
| } |
| fptr := p.Apply(f.offset) |
| if f.isPointer && fptr.Elem().IsNil() { |
| if f.isRequired { |
| return errors.RequiredNotSet(string(mi.Desc.Fields().ByNumber(f.num).FullName())) |
| } |
| continue |
| } |
| if f.funcs.isInit == nil { |
| continue |
| } |
| if err := f.funcs.isInit(fptr, f); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func (mi *MessageInfo) isInitExtensions(ext *map[int32]ExtensionField) error { |
| if ext == nil { |
| return nil |
| } |
| for _, x := range *ext { |
| ei := getExtensionFieldInfo(x.Type()) |
| if ei.funcs.isInit == nil { |
| continue |
| } |
| v := x.Value() |
| if !v.IsValid() { |
| continue |
| } |
| if err := ei.funcs.isInit(v); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| var ( |
| needsInitCheckMu sync.Mutex |
| needsInitCheckMap sync.Map |
| ) |
| |
| // needsInitCheck reports whether a message needs to be checked for partial initialization. |
| // |
| // It returns true if the message transitively includes any required or extension fields. |
| func needsInitCheck(md pref.MessageDescriptor) bool { |
| if v, ok := needsInitCheckMap.Load(md); ok { |
| if has, ok := v.(bool); ok { |
| return has |
| } |
| } |
| needsInitCheckMu.Lock() |
| defer needsInitCheckMu.Unlock() |
| return needsInitCheckLocked(md) |
| } |
| |
| func needsInitCheckLocked(md pref.MessageDescriptor) (has bool) { |
| if v, ok := needsInitCheckMap.Load(md); ok { |
| // If has is true, we've previously determined that this message |
| // needs init checks. |
| // |
| // If has is false, we've previously determined that it can never |
| // be uninitialized. |
| // |
| // If has is not a bool, we've just encountered a cycle in the |
| // message graph. In this case, it is safe to return false: If |
| // the message does have required fields, we'll detect them later |
| // in the graph traversal. |
| has, ok := v.(bool) |
| return ok && has |
| } |
| needsInitCheckMap.Store(md, struct{}{}) // avoid cycles while descending into this message |
| defer func() { |
| needsInitCheckMap.Store(md, has) |
| }() |
| if md.RequiredNumbers().Len() > 0 { |
| return true |
| } |
| if md.ExtensionRanges().Len() > 0 { |
| return true |
| } |
| for i := 0; i < md.Fields().Len(); i++ { |
| fd := md.Fields().Get(i) |
| // Map keys are never messages, so just consider the map value. |
| if fd.IsMap() { |
| fd = fd.MapValue() |
| } |
| fmd := fd.Message() |
| if fmd != nil && needsInitCheckLocked(fmd) { |
| return true |
| } |
| } |
| return false |
| } |