1*1c12ee1eSDan Willemsen// Copyright 2020 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsen// Package protorange provides functionality to traverse a message value. 6*1c12ee1eSDan Willemsenpackage protorange 7*1c12ee1eSDan Willemsen 8*1c12ee1eSDan Willemsenimport ( 9*1c12ee1eSDan Willemsen "bytes" 10*1c12ee1eSDan Willemsen "errors" 11*1c12ee1eSDan Willemsen 12*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/genid" 13*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/order" 14*1c12ee1eSDan Willemsen "google.golang.org/protobuf/proto" 15*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protopath" 16*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoreflect" 17*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoregistry" 18*1c12ee1eSDan Willemsen) 19*1c12ee1eSDan Willemsen 20*1c12ee1eSDan Willemsenvar ( 21*1c12ee1eSDan Willemsen // Break breaks traversal of children in the current value. 22*1c12ee1eSDan Willemsen // It has no effect when traversing values that are not composite types 23*1c12ee1eSDan Willemsen // (e.g., messages, lists, and maps). 24*1c12ee1eSDan Willemsen Break = errors.New("break traversal of children in current value") 25*1c12ee1eSDan Willemsen 26*1c12ee1eSDan Willemsen // Terminate terminates the entire range operation. 27*1c12ee1eSDan Willemsen // All necessary Pop operations continue to be called. 28*1c12ee1eSDan Willemsen Terminate = errors.New("terminate range operation") 29*1c12ee1eSDan Willemsen) 30*1c12ee1eSDan Willemsen 31*1c12ee1eSDan Willemsen// Range performs a depth-first traversal over reachable values in a message. 32*1c12ee1eSDan Willemsen// 33*1c12ee1eSDan Willemsen// See Options.Range for details. 34*1c12ee1eSDan Willemsenfunc Range(m protoreflect.Message, f func(protopath.Values) error) error { 35*1c12ee1eSDan Willemsen return Options{}.Range(m, f, nil) 36*1c12ee1eSDan Willemsen} 37*1c12ee1eSDan Willemsen 38*1c12ee1eSDan Willemsen// Options configures traversal of a message value tree. 39*1c12ee1eSDan Willemsentype Options struct { 40*1c12ee1eSDan Willemsen // Stable specifies whether to visit message fields and map entries 41*1c12ee1eSDan Willemsen // in a stable ordering. If false, then the ordering is undefined and 42*1c12ee1eSDan Willemsen // may be non-deterministic. 43*1c12ee1eSDan Willemsen // 44*1c12ee1eSDan Willemsen // Message fields are visited in ascending order by field number. 45*1c12ee1eSDan Willemsen // Map entries are visited in ascending order, where 46*1c12ee1eSDan Willemsen // boolean keys are ordered such that false sorts before true, 47*1c12ee1eSDan Willemsen // numeric keys are ordered based on the numeric value, and 48*1c12ee1eSDan Willemsen // string keys are lexicographically ordered by Unicode codepoints. 49*1c12ee1eSDan Willemsen Stable bool 50*1c12ee1eSDan Willemsen 51*1c12ee1eSDan Willemsen // Resolver is used for looking up types when expanding google.protobuf.Any 52*1c12ee1eSDan Willemsen // messages. If nil, this defaults to using protoregistry.GlobalTypes. 53*1c12ee1eSDan Willemsen // To prevent expansion of Any messages, pass an empty protoregistry.Types: 54*1c12ee1eSDan Willemsen // 55*1c12ee1eSDan Willemsen // Options{Resolver: (*protoregistry.Types)(nil)} 56*1c12ee1eSDan Willemsen // 57*1c12ee1eSDan Willemsen Resolver interface { 58*1c12ee1eSDan Willemsen protoregistry.ExtensionTypeResolver 59*1c12ee1eSDan Willemsen protoregistry.MessageTypeResolver 60*1c12ee1eSDan Willemsen } 61*1c12ee1eSDan Willemsen} 62*1c12ee1eSDan Willemsen 63*1c12ee1eSDan Willemsen// Range performs a depth-first traversal over reachable values in a message. 64*1c12ee1eSDan Willemsen// The first push and the last pop are to push/pop a protopath.Root step. 65*1c12ee1eSDan Willemsen// If push or pop return any non-nil error (other than Break or Terminate), 66*1c12ee1eSDan Willemsen// it terminates the traversal and is returned by Range. 67*1c12ee1eSDan Willemsen// 68*1c12ee1eSDan Willemsen// The rules for traversing a message is as follows: 69*1c12ee1eSDan Willemsen// 70*1c12ee1eSDan Willemsen// • For messages, iterate over every populated known and extension field. 71*1c12ee1eSDan Willemsen// Each field is preceded by a push of a protopath.FieldAccess step, 72*1c12ee1eSDan Willemsen// followed by recursive application of the rules on the field value, 73*1c12ee1eSDan Willemsen// and succeeded by a pop of that step. 74*1c12ee1eSDan Willemsen// If the message has unknown fields, then push an protopath.UnknownAccess step 75*1c12ee1eSDan Willemsen// followed immediately by pop of that step. 76*1c12ee1eSDan Willemsen// 77*1c12ee1eSDan Willemsen// • As an exception to the above rule, if the current message is a 78*1c12ee1eSDan Willemsen// google.protobuf.Any message, expand the underlying message (if resolvable). 79*1c12ee1eSDan Willemsen// The expanded message is preceded by a push of a protopath.AnyExpand step, 80*1c12ee1eSDan Willemsen// followed by recursive application of the rules on the underlying message, 81*1c12ee1eSDan Willemsen// and succeeded by a pop of that step. Mutations to the expanded message 82*1c12ee1eSDan Willemsen// are written back to the Any message when popping back out. 83*1c12ee1eSDan Willemsen// 84*1c12ee1eSDan Willemsen// • For lists, iterate over every element. Each element is preceded by a push 85*1c12ee1eSDan Willemsen// of a protopath.ListIndex step, followed by recursive application of the rules 86*1c12ee1eSDan Willemsen// on the list element, and succeeded by a pop of that step. 87*1c12ee1eSDan Willemsen// 88*1c12ee1eSDan Willemsen// • For maps, iterate over every entry. Each entry is preceded by a push 89*1c12ee1eSDan Willemsen// of a protopath.MapIndex step, followed by recursive application of the rules 90*1c12ee1eSDan Willemsen// on the map entry value, and succeeded by a pop of that step. 91*1c12ee1eSDan Willemsen// 92*1c12ee1eSDan Willemsen// Mutations should only be made to the last value, otherwise the effects on 93*1c12ee1eSDan Willemsen// traversal will be undefined. If the mutation is made to the last value 94*1c12ee1eSDan Willemsen// during to a push, then the effects of the mutation will affect traversal. 95*1c12ee1eSDan Willemsen// For example, if the last value is currently a message, and the push function 96*1c12ee1eSDan Willemsen// populates a few fields in that message, then the newly modified fields 97*1c12ee1eSDan Willemsen// will be traversed. 98*1c12ee1eSDan Willemsen// 99*1c12ee1eSDan Willemsen// The protopath.Values provided to push functions is only valid until the 100*1c12ee1eSDan Willemsen// corresponding pop call and the values provided to a pop call is only valid 101*1c12ee1eSDan Willemsen// for the duration of the pop call itself. 102*1c12ee1eSDan Willemsenfunc (o Options) Range(m protoreflect.Message, push, pop func(protopath.Values) error) error { 103*1c12ee1eSDan Willemsen var err error 104*1c12ee1eSDan Willemsen p := new(protopath.Values) 105*1c12ee1eSDan Willemsen if o.Resolver == nil { 106*1c12ee1eSDan Willemsen o.Resolver = protoregistry.GlobalTypes 107*1c12ee1eSDan Willemsen } 108*1c12ee1eSDan Willemsen 109*1c12ee1eSDan Willemsen pushStep(p, protopath.Root(m.Descriptor()), protoreflect.ValueOfMessage(m)) 110*1c12ee1eSDan Willemsen if push != nil { 111*1c12ee1eSDan Willemsen err = amendError(err, push(*p)) 112*1c12ee1eSDan Willemsen } 113*1c12ee1eSDan Willemsen if err == nil { 114*1c12ee1eSDan Willemsen err = o.rangeMessage(p, m, push, pop) 115*1c12ee1eSDan Willemsen } 116*1c12ee1eSDan Willemsen if pop != nil { 117*1c12ee1eSDan Willemsen err = amendError(err, pop(*p)) 118*1c12ee1eSDan Willemsen } 119*1c12ee1eSDan Willemsen popStep(p) 120*1c12ee1eSDan Willemsen 121*1c12ee1eSDan Willemsen if err == Break || err == Terminate { 122*1c12ee1eSDan Willemsen err = nil 123*1c12ee1eSDan Willemsen } 124*1c12ee1eSDan Willemsen return err 125*1c12ee1eSDan Willemsen} 126*1c12ee1eSDan Willemsen 127*1c12ee1eSDan Willemsenfunc (o Options) rangeMessage(p *protopath.Values, m protoreflect.Message, push, pop func(protopath.Values) error) (err error) { 128*1c12ee1eSDan Willemsen if ok, err := o.rangeAnyMessage(p, m, push, pop); ok { 129*1c12ee1eSDan Willemsen return err 130*1c12ee1eSDan Willemsen } 131*1c12ee1eSDan Willemsen 132*1c12ee1eSDan Willemsen fieldOrder := order.AnyFieldOrder 133*1c12ee1eSDan Willemsen if o.Stable { 134*1c12ee1eSDan Willemsen fieldOrder = order.NumberFieldOrder 135*1c12ee1eSDan Willemsen } 136*1c12ee1eSDan Willemsen order.RangeFields(m, fieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { 137*1c12ee1eSDan Willemsen pushStep(p, protopath.FieldAccess(fd), v) 138*1c12ee1eSDan Willemsen if push != nil { 139*1c12ee1eSDan Willemsen err = amendError(err, push(*p)) 140*1c12ee1eSDan Willemsen } 141*1c12ee1eSDan Willemsen if err == nil { 142*1c12ee1eSDan Willemsen switch { 143*1c12ee1eSDan Willemsen case fd.IsMap(): 144*1c12ee1eSDan Willemsen err = o.rangeMap(p, fd, v.Map(), push, pop) 145*1c12ee1eSDan Willemsen case fd.IsList(): 146*1c12ee1eSDan Willemsen err = o.rangeList(p, fd, v.List(), push, pop) 147*1c12ee1eSDan Willemsen case fd.Message() != nil: 148*1c12ee1eSDan Willemsen err = o.rangeMessage(p, v.Message(), push, pop) 149*1c12ee1eSDan Willemsen } 150*1c12ee1eSDan Willemsen } 151*1c12ee1eSDan Willemsen if pop != nil { 152*1c12ee1eSDan Willemsen err = amendError(err, pop(*p)) 153*1c12ee1eSDan Willemsen } 154*1c12ee1eSDan Willemsen popStep(p) 155*1c12ee1eSDan Willemsen return err == nil 156*1c12ee1eSDan Willemsen }) 157*1c12ee1eSDan Willemsen 158*1c12ee1eSDan Willemsen if b := m.GetUnknown(); len(b) > 0 && err == nil { 159*1c12ee1eSDan Willemsen pushStep(p, protopath.UnknownAccess(), protoreflect.ValueOfBytes(b)) 160*1c12ee1eSDan Willemsen if push != nil { 161*1c12ee1eSDan Willemsen err = amendError(err, push(*p)) 162*1c12ee1eSDan Willemsen } 163*1c12ee1eSDan Willemsen if pop != nil { 164*1c12ee1eSDan Willemsen err = amendError(err, pop(*p)) 165*1c12ee1eSDan Willemsen } 166*1c12ee1eSDan Willemsen popStep(p) 167*1c12ee1eSDan Willemsen } 168*1c12ee1eSDan Willemsen 169*1c12ee1eSDan Willemsen if err == Break { 170*1c12ee1eSDan Willemsen err = nil 171*1c12ee1eSDan Willemsen } 172*1c12ee1eSDan Willemsen return err 173*1c12ee1eSDan Willemsen} 174*1c12ee1eSDan Willemsen 175*1c12ee1eSDan Willemsenfunc (o Options) rangeAnyMessage(p *protopath.Values, m protoreflect.Message, push, pop func(protopath.Values) error) (ok bool, err error) { 176*1c12ee1eSDan Willemsen md := m.Descriptor() 177*1c12ee1eSDan Willemsen if md.FullName() != "google.protobuf.Any" { 178*1c12ee1eSDan Willemsen return false, nil 179*1c12ee1eSDan Willemsen } 180*1c12ee1eSDan Willemsen 181*1c12ee1eSDan Willemsen fds := md.Fields() 182*1c12ee1eSDan Willemsen url := m.Get(fds.ByNumber(genid.Any_TypeUrl_field_number)).String() 183*1c12ee1eSDan Willemsen val := m.Get(fds.ByNumber(genid.Any_Value_field_number)).Bytes() 184*1c12ee1eSDan Willemsen mt, errFind := o.Resolver.FindMessageByURL(url) 185*1c12ee1eSDan Willemsen if errFind != nil { 186*1c12ee1eSDan Willemsen return false, nil 187*1c12ee1eSDan Willemsen } 188*1c12ee1eSDan Willemsen 189*1c12ee1eSDan Willemsen // Unmarshal the raw encoded message value into a structured message value. 190*1c12ee1eSDan Willemsen m2 := mt.New() 191*1c12ee1eSDan Willemsen errUnmarshal := proto.UnmarshalOptions{ 192*1c12ee1eSDan Willemsen Merge: true, 193*1c12ee1eSDan Willemsen AllowPartial: true, 194*1c12ee1eSDan Willemsen Resolver: o.Resolver, 195*1c12ee1eSDan Willemsen }.Unmarshal(val, m2.Interface()) 196*1c12ee1eSDan Willemsen if errUnmarshal != nil { 197*1c12ee1eSDan Willemsen // If the the underlying message cannot be unmarshaled, 198*1c12ee1eSDan Willemsen // then just treat this as an normal message type. 199*1c12ee1eSDan Willemsen return false, nil 200*1c12ee1eSDan Willemsen } 201*1c12ee1eSDan Willemsen 202*1c12ee1eSDan Willemsen // Marshal Any before ranging to detect possible mutations. 203*1c12ee1eSDan Willemsen b1, errMarshal := proto.MarshalOptions{ 204*1c12ee1eSDan Willemsen AllowPartial: true, 205*1c12ee1eSDan Willemsen Deterministic: true, 206*1c12ee1eSDan Willemsen }.Marshal(m2.Interface()) 207*1c12ee1eSDan Willemsen if errMarshal != nil { 208*1c12ee1eSDan Willemsen return true, errMarshal 209*1c12ee1eSDan Willemsen } 210*1c12ee1eSDan Willemsen 211*1c12ee1eSDan Willemsen pushStep(p, protopath.AnyExpand(m2.Descriptor()), protoreflect.ValueOfMessage(m2)) 212*1c12ee1eSDan Willemsen if push != nil { 213*1c12ee1eSDan Willemsen err = amendError(err, push(*p)) 214*1c12ee1eSDan Willemsen } 215*1c12ee1eSDan Willemsen if err == nil { 216*1c12ee1eSDan Willemsen err = o.rangeMessage(p, m2, push, pop) 217*1c12ee1eSDan Willemsen } 218*1c12ee1eSDan Willemsen if pop != nil { 219*1c12ee1eSDan Willemsen err = amendError(err, pop(*p)) 220*1c12ee1eSDan Willemsen } 221*1c12ee1eSDan Willemsen popStep(p) 222*1c12ee1eSDan Willemsen 223*1c12ee1eSDan Willemsen // Marshal Any after ranging to detect possible mutations. 224*1c12ee1eSDan Willemsen b2, errMarshal := proto.MarshalOptions{ 225*1c12ee1eSDan Willemsen AllowPartial: true, 226*1c12ee1eSDan Willemsen Deterministic: true, 227*1c12ee1eSDan Willemsen }.Marshal(m2.Interface()) 228*1c12ee1eSDan Willemsen if errMarshal != nil { 229*1c12ee1eSDan Willemsen return true, errMarshal 230*1c12ee1eSDan Willemsen } 231*1c12ee1eSDan Willemsen 232*1c12ee1eSDan Willemsen // Mutations detected, write the new sequence of bytes to the Any message. 233*1c12ee1eSDan Willemsen if !bytes.Equal(b1, b2) { 234*1c12ee1eSDan Willemsen m.Set(fds.ByNumber(genid.Any_Value_field_number), protoreflect.ValueOfBytes(b2)) 235*1c12ee1eSDan Willemsen } 236*1c12ee1eSDan Willemsen 237*1c12ee1eSDan Willemsen if err == Break { 238*1c12ee1eSDan Willemsen err = nil 239*1c12ee1eSDan Willemsen } 240*1c12ee1eSDan Willemsen return true, err 241*1c12ee1eSDan Willemsen} 242*1c12ee1eSDan Willemsen 243*1c12ee1eSDan Willemsenfunc (o Options) rangeList(p *protopath.Values, fd protoreflect.FieldDescriptor, ls protoreflect.List, push, pop func(protopath.Values) error) (err error) { 244*1c12ee1eSDan Willemsen for i := 0; i < ls.Len() && err == nil; i++ { 245*1c12ee1eSDan Willemsen v := ls.Get(i) 246*1c12ee1eSDan Willemsen pushStep(p, protopath.ListIndex(i), v) 247*1c12ee1eSDan Willemsen if push != nil { 248*1c12ee1eSDan Willemsen err = amendError(err, push(*p)) 249*1c12ee1eSDan Willemsen } 250*1c12ee1eSDan Willemsen if err == nil && fd.Message() != nil { 251*1c12ee1eSDan Willemsen err = o.rangeMessage(p, v.Message(), push, pop) 252*1c12ee1eSDan Willemsen } 253*1c12ee1eSDan Willemsen if pop != nil { 254*1c12ee1eSDan Willemsen err = amendError(err, pop(*p)) 255*1c12ee1eSDan Willemsen } 256*1c12ee1eSDan Willemsen popStep(p) 257*1c12ee1eSDan Willemsen } 258*1c12ee1eSDan Willemsen 259*1c12ee1eSDan Willemsen if err == Break { 260*1c12ee1eSDan Willemsen err = nil 261*1c12ee1eSDan Willemsen } 262*1c12ee1eSDan Willemsen return err 263*1c12ee1eSDan Willemsen} 264*1c12ee1eSDan Willemsen 265*1c12ee1eSDan Willemsenfunc (o Options) rangeMap(p *protopath.Values, fd protoreflect.FieldDescriptor, ms protoreflect.Map, push, pop func(protopath.Values) error) (err error) { 266*1c12ee1eSDan Willemsen keyOrder := order.AnyKeyOrder 267*1c12ee1eSDan Willemsen if o.Stable { 268*1c12ee1eSDan Willemsen keyOrder = order.GenericKeyOrder 269*1c12ee1eSDan Willemsen } 270*1c12ee1eSDan Willemsen order.RangeEntries(ms, keyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool { 271*1c12ee1eSDan Willemsen pushStep(p, protopath.MapIndex(k), v) 272*1c12ee1eSDan Willemsen if push != nil { 273*1c12ee1eSDan Willemsen err = amendError(err, push(*p)) 274*1c12ee1eSDan Willemsen } 275*1c12ee1eSDan Willemsen if err == nil && fd.MapValue().Message() != nil { 276*1c12ee1eSDan Willemsen err = o.rangeMessage(p, v.Message(), push, pop) 277*1c12ee1eSDan Willemsen } 278*1c12ee1eSDan Willemsen if pop != nil { 279*1c12ee1eSDan Willemsen err = amendError(err, pop(*p)) 280*1c12ee1eSDan Willemsen } 281*1c12ee1eSDan Willemsen popStep(p) 282*1c12ee1eSDan Willemsen return err == nil 283*1c12ee1eSDan Willemsen }) 284*1c12ee1eSDan Willemsen 285*1c12ee1eSDan Willemsen if err == Break { 286*1c12ee1eSDan Willemsen err = nil 287*1c12ee1eSDan Willemsen } 288*1c12ee1eSDan Willemsen return err 289*1c12ee1eSDan Willemsen} 290*1c12ee1eSDan Willemsen 291*1c12ee1eSDan Willemsenfunc pushStep(p *protopath.Values, s protopath.Step, v protoreflect.Value) { 292*1c12ee1eSDan Willemsen p.Path = append(p.Path, s) 293*1c12ee1eSDan Willemsen p.Values = append(p.Values, v) 294*1c12ee1eSDan Willemsen} 295*1c12ee1eSDan Willemsen 296*1c12ee1eSDan Willemsenfunc popStep(p *protopath.Values) { 297*1c12ee1eSDan Willemsen p.Path = p.Path[:len(p.Path)-1] 298*1c12ee1eSDan Willemsen p.Values = p.Values[:len(p.Values)-1] 299*1c12ee1eSDan Willemsen} 300*1c12ee1eSDan Willemsen 301*1c12ee1eSDan Willemsen// amendError amends the previous error with the current error if it is 302*1c12ee1eSDan Willemsen// considered more serious. The precedence order for errors is: 303*1c12ee1eSDan Willemsen// 304*1c12ee1eSDan Willemsen// nil < Break < Terminate < previous non-nil < current non-nil 305*1c12ee1eSDan Willemsenfunc amendError(prev, curr error) error { 306*1c12ee1eSDan Willemsen switch { 307*1c12ee1eSDan Willemsen case curr == nil: 308*1c12ee1eSDan Willemsen return prev 309*1c12ee1eSDan Willemsen case curr == Break && prev != nil: 310*1c12ee1eSDan Willemsen return prev 311*1c12ee1eSDan Willemsen case curr == Terminate && prev != nil && prev != Break: 312*1c12ee1eSDan Willemsen return prev 313*1c12ee1eSDan Willemsen default: 314*1c12ee1eSDan Willemsen return curr 315*1c12ee1eSDan Willemsen } 316*1c12ee1eSDan Willemsen} 317