Update reflectwalk and copystructure

Bug fix and improvements
This commit is contained in:
James Bardin 2016-10-03 14:30:48 -04:00
parent 36fd208fa9
commit a86aa0f22d
3 changed files with 82 additions and 10 deletions

View File

@ -82,6 +82,14 @@ func (c Config) Copy(v interface{}) (interface{}, error) {
return result, nil
}
// Return the key used to index interfaces types we've seen. Store the number
// of pointers in the upper 32bits, and the depth in the lower 32bits. This is
// easy to calculate, easy to match a key with our current depth, and we don't
// need to deal with initializing and cleaning up nested maps or slices.
func ifaceKey(pointers, depth int) uint64 {
return uint64(pointers)<<32 | uint64(depth)
}
type walker struct {
Result interface{}
@ -89,7 +97,16 @@ type walker struct {
ignoreDepth int
vals []reflect.Value
cs []reflect.Value
ps []int
// This stores the number of pointers we've walked over, indexed by depth.
ps []int
// If an interface is indirected by a pointer, we need to know the type of
// interface to create when creating the new value. Store the interface
// types here, indexed by both the walk depth and the number of pointers
// already seen at that depth. Use ifaceKey to calculate the proper uint64
// value.
ifaceTypes map[uint64]reflect.Type
// any locks we've taken, indexed by depth
locks []sync.Locker
@ -119,7 +136,16 @@ func (w *walker) Exit(l reflectwalk.Location) error {
defer locker.Unlock()
}
// clear out pointers and interfaces as we exit the stack
w.ps[w.depth] = 0
for k := range w.ifaceTypes {
mask := uint64(^uint32(0))
if k&mask == uint64(w.depth) {
delete(w.ifaceTypes, k)
}
}
w.depth--
if w.ignoreDepth > w.depth {
w.ignoreDepth = 0
@ -222,6 +248,18 @@ func (w *walker) PointerExit(v bool) error {
return nil
}
func (w *walker) Interface(v reflect.Value) error {
if !v.IsValid() {
return nil
}
if w.ifaceTypes == nil {
w.ifaceTypes = make(map[uint64]reflect.Type)
}
w.ifaceTypes[ifaceKey(w.ps[w.depth], w.depth)] = v.Type()
return nil
}
func (w *walker) Primitive(v reflect.Value) error {
if w.ignoring() {
return nil
@ -312,8 +350,7 @@ func (w *walker) StructField(f reflect.StructField, v reflect.Value) error {
// If PkgPath is non-empty, this is a private (unexported) field.
// We do not set this unexported since the Go runtime doesn't allow us.
if f.PkgPath != "" {
w.ignore()
return nil
return reflectwalk.SkipEntry
}
// Push the field onto the stack, we'll handle it when we exit
@ -369,6 +406,12 @@ func (w *walker) replacePointerMaybe() {
v := w.valPop()
for i := 1; i < w.ps[w.depth]; i++ {
if iType, ok := w.ifaceTypes[ifaceKey(w.ps[w.depth]-i, w.depth)]; ok {
iface := reflect.New(iType).Elem()
iface.Set(v)
v = iface
}
p := reflect.New(v.Type())
p.Elem().Set(v)
v = p

View File

@ -4,7 +4,10 @@
// those elements.
package reflectwalk
import "reflect"
import (
"errors"
"reflect"
)
// PrimitiveWalker implementations are able to handle primitive values
// within complex structures. Primitive values are numbers, strings,
@ -16,6 +19,12 @@ type PrimitiveWalker interface {
Primitive(reflect.Value) error
}
// InterfaceWalker implementations are able to handle interface values as they
// are encountered during the walk.
type InterfaceWalker interface {
Interface(reflect.Value) error
}
// MapWalker implementations are able to handle individual elements
// found within a map structure.
type MapWalker interface {
@ -53,6 +62,13 @@ type PointerWalker interface {
PointerExit(bool) error
}
// SkipEntry can be returned from walk functions to skip walking
// the value of this field. This is only valid in the following functions:
//
// - StructField: skips walking the struct value
//
var SkipEntry = errors.New("skip this entry")
// Walk takes an arbitrary value and an interface and traverses the
// value, calling callbacks on the interface if they are supported.
// The interface should implement one or more of the walker interfaces
@ -96,8 +112,15 @@ func walk(v reflect.Value, w interface{}) (err error) {
for {
if pointerV.Kind() == reflect.Interface {
if iw, ok := w.(InterfaceWalker); ok {
if err = iw.Interface(pointerV); err != nil {
return
}
}
pointerV = pointerV.Elem()
}
if pointerV.Kind() == reflect.Ptr {
pointer = true
v = reflect.Indirect(pointerV)
@ -282,6 +305,12 @@ func walkStruct(v reflect.Value, w interface{}) (err error) {
if sw, ok := w.(StructWalker); ok {
err = sw.StructField(sf, f)
// SkipEntry just pretends this field doesn't even exist
if err == SkipEntry {
continue
}
if err != nil {
return
}

12
vendor/vendor.json vendored
View File

@ -1464,10 +1464,10 @@
"revision": "8631ce90f28644f54aeedcb3e389a85174e067d1"
},
{
"checksumSHA1": "gsIaLhXE/YZahSg33edGHvzVQ1o=",
"checksumSHA1": "BsIMq23KDyxdhQC7g2dDz/9oHbA=",
"path": "github.com/mitchellh/copystructure",
"revision": "c740739b7b69de009385f52933769c1c037705df",
"revisionTime": "2016-10-03T06:35:14Z"
"revision": "c4815f984fb5c5486f6db1c9a5660e7605fd4c20",
"revisionTime": "2016-10-03T18:23:19Z"
},
{
"path": "github.com/mitchellh/go-homedir",
@ -1499,10 +1499,10 @@
"revision": "6e6954073784f7ee67b28f2d22749d6479151ed7"
},
{
"checksumSHA1": "u+sHUhd/XXvjiVPAJ/h8yvLbSj4=",
"checksumSHA1": "vBpuqNfSTZcAR/0tP8tNYacySGs=",
"path": "github.com/mitchellh/reflectwalk",
"revision": "4a18c295388e8482403483c88469392f9e6556d7",
"revisionTime": "2016-10-02T04:32:56Z"
"revision": "92573fe8d000a145bfebc03a16bc22b34945867f",
"revisionTime": "2016-10-03T17:45:16Z"
},
{
"checksumSHA1": "/iig5lYSPCL3C8J7e4nTAevYNDE=",