// 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" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/runtime/protoiface" ) func (mi *MessageInfo) checkInitialized(in protoiface.CheckInitializedInput) (protoiface.CheckInitializedOutput, error) { var p pointer if ms, ok := in.Message.(*messageState); ok { p = ms.pointer() } else { p = in.Message.(*messageReflectWrapper).pointer() } return protoiface.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 protoreflect.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 protoreflect.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 }