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  }