github.com/kortschak/utter@v1.5.0/walk.go (about) 1 /* 2 * Copyright (c) 2013 Dave Collins <dave@davec.name> 3 * Copyright (c) 2015 Dan Kortschak <dan.kortschak@adelaide.edu.au> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 package utter 19 20 import "reflect" 21 22 // walkPtr handles walking of pointers by indirecting them as necessary. 23 func (d *dumpState) walkPtr(v reflect.Value) { 24 // Remove pointers at or below the current depth from map used to detect 25 // circular refs. 26 for k, depth := range d.pointers { 27 if depth >= d.depth { 28 delete(d.pointers, k) 29 } 30 } 31 32 var nilFound, cycleFound bool 33 for v.Kind() == reflect.Ptr { 34 if v.IsNil() { 35 nilFound = true 36 break 37 } 38 addr := v.Pointer() 39 if pd, ok := d.pointers[addr]; ok && pd < d.depth { 40 cycleFound = true 41 break 42 } 43 d.pointers[addr] = d.depth 44 d.nodes[addrType{addr, v.Type()}] = struct{}{} 45 46 v = v.Elem() 47 if v.Kind() == reflect.Interface { 48 if v.IsNil() { 49 nilFound = true 50 break 51 } 52 v = v.Elem() 53 } 54 d.nodes[addrType{addr, v.Type()}] = struct{}{} 55 } 56 57 if !nilFound && !cycleFound { 58 d.walk(v, false, false, false, 0) 59 } 60 } 61 62 // walkSlice handles walking of arrays and slices. 63 func (d *dumpState) walkSlice(v reflect.Value) { 64 d.depth++ 65 // Recursively call walk for each item. 66 for i := 0; i < v.Len(); i++ { 67 d.walk(d.unpackValue(v.Index(i))) 68 } 69 d.depth-- 70 } 71 72 // walk is the main workhorse for walking a value. It uses the passed reflect 73 // value to figure out what kind of object we are dealing with and follows it 74 // appropriately. It is a recursive function, however circular data structures 75 // are detected and escaped from. 76 func (d *dumpState) walk(v reflect.Value, _, _, _ bool, _ uintptr) { 77 // Handle invalid reflect values immediately. 78 kind := v.Kind() 79 if kind == reflect.Invalid { 80 return 81 } 82 83 // Handle pointers specially. 84 if kind == reflect.Ptr { 85 d.walkPtr(v) 86 return 87 } 88 89 switch kind { 90 case reflect.Slice: 91 if v.IsNil() { 92 break 93 } 94 fallthrough 95 96 case reflect.Array: 97 d.walkSlice(v) 98 99 case reflect.Map: 100 if v.IsNil() { 101 break 102 } 103 d.depth++ 104 keys := v.MapKeys() 105 for _, key := range keys { 106 d.walk(d.unpackValue(key)) 107 d.walk(d.unpackValue(v.MapIndex(key))) 108 } 109 d.depth-- 110 111 case reflect.Struct: 112 d.depth++ 113 vt := v.Type() 114 for i := 0; i < v.NumField(); i++ { 115 vtf := vt.Field(i) 116 if d.cs.IgnoreUnexported && vtf.PkgPath != "" { 117 continue 118 } 119 d.walk(d.unpackValue(v.Field(i))) 120 } 121 d.depth-- 122 } 123 }