github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/gc/dump.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file implements textual dumping of arbitrary data structures
     6  // for debugging purposes. The code is customized for Node graphs
     7  // and may be used for an alternative view of the node structure.
     8  
     9  package gc
    10  
    11  import (
    12  	"github.com/gagliardetto/golang-go/cmd/compile/internal/types"
    13  	"github.com/gagliardetto/golang-go/cmd/internal/src"
    14  	"fmt"
    15  	"io"
    16  	"os"
    17  	"reflect"
    18  	"regexp"
    19  )
    20  
    21  // dump is like fdump but prints to stderr.
    22  func dump(root interface{}, filter string, depth int) {
    23  	fdump(os.Stderr, root, filter, depth)
    24  }
    25  
    26  // fdump prints the structure of a rooted data structure
    27  // to w by depth-first traversal of the data structure.
    28  //
    29  // The filter parameter is a regular expression. If it is
    30  // non-empty, only struct fields whose names match filter
    31  // are printed.
    32  //
    33  // The depth parameter controls how deep traversal recurses
    34  // before it returns (higher value means greater depth).
    35  // If an empty field filter is given, a good depth default value
    36  // is 4. A negative depth means no depth limit, which may be fine
    37  // for small data structures or if there is a non-empty filter.
    38  //
    39  // In the output, Node structs are identified by their Op name
    40  // rather than their type; struct fields with zero values or
    41  // non-matching field names are omitted, and "…" means recursion
    42  // depth has been reached or struct fields have been omitted.
    43  func fdump(w io.Writer, root interface{}, filter string, depth int) {
    44  	if root == nil {
    45  		fmt.Fprintln(w, "nil")
    46  		return
    47  	}
    48  
    49  	if filter == "" {
    50  		filter = ".*" // default
    51  	}
    52  
    53  	p := dumper{
    54  		output:  w,
    55  		fieldrx: regexp.MustCompile(filter),
    56  		ptrmap:  make(map[uintptr]int),
    57  		last:    '\n', // force printing of line number on first line
    58  	}
    59  
    60  	p.dump(reflect.ValueOf(root), depth)
    61  	p.printf("\n")
    62  }
    63  
    64  type dumper struct {
    65  	output  io.Writer
    66  	fieldrx *regexp.Regexp  // field name filter
    67  	ptrmap  map[uintptr]int // ptr -> dump line number
    68  	lastadr string          // last address string printed (for shortening)
    69  
    70  	// output
    71  	indent int  // current indentation level
    72  	last   byte // last byte processed by Write
    73  	line   int  // current line number
    74  }
    75  
    76  var indentBytes = []byte(".  ")
    77  
    78  func (p *dumper) Write(data []byte) (n int, err error) {
    79  	var m int
    80  	for i, b := range data {
    81  		// invariant: data[0:n] has been written
    82  		if b == '\n' {
    83  			m, err = p.output.Write(data[n : i+1])
    84  			n += m
    85  			if err != nil {
    86  				return
    87  			}
    88  		} else if p.last == '\n' {
    89  			p.line++
    90  			_, err = fmt.Fprintf(p.output, "%6d  ", p.line)
    91  			if err != nil {
    92  				return
    93  			}
    94  			for j := p.indent; j > 0; j-- {
    95  				_, err = p.output.Write(indentBytes)
    96  				if err != nil {
    97  					return
    98  				}
    99  			}
   100  		}
   101  		p.last = b
   102  	}
   103  	if len(data) > n {
   104  		m, err = p.output.Write(data[n:])
   105  		n += m
   106  	}
   107  	return
   108  }
   109  
   110  // printf is a convenience wrapper.
   111  func (p *dumper) printf(format string, args ...interface{}) {
   112  	if _, err := fmt.Fprintf(p, format, args...); err != nil {
   113  		panic(err)
   114  	}
   115  }
   116  
   117  // addr returns the (hexadecimal) address string of the object
   118  // represented by x (or "?" if x is not addressable), with the
   119  // common prefix between this and the prior address replaced by
   120  // "0x…" to make it easier to visually match addresses.
   121  func (p *dumper) addr(x reflect.Value) string {
   122  	if !x.CanAddr() {
   123  		return "?"
   124  	}
   125  	adr := fmt.Sprintf("%p", x.Addr().Interface())
   126  	s := adr
   127  	if i := commonPrefixLen(p.lastadr, adr); i > 0 {
   128  		s = "0x…" + adr[i:]
   129  	}
   130  	p.lastadr = adr
   131  	return s
   132  }
   133  
   134  // dump prints the contents of x.
   135  func (p *dumper) dump(x reflect.Value, depth int) {
   136  	if depth == 0 {
   137  		p.printf("…")
   138  		return
   139  	}
   140  
   141  	// special cases
   142  	switch v := x.Interface().(type) {
   143  	case Nodes:
   144  		// unpack Nodes since reflect cannot look inside
   145  		// due to the unexported field in its struct
   146  		x = reflect.ValueOf(v.Slice())
   147  
   148  	case src.XPos:
   149  		p.printf("%s", linestr(v))
   150  		return
   151  
   152  	case *types.Node:
   153  		x = reflect.ValueOf(asNode(v))
   154  	}
   155  
   156  	switch x.Kind() {
   157  	case reflect.String:
   158  		p.printf("%q", x.Interface()) // print strings in quotes
   159  
   160  	case reflect.Interface:
   161  		if x.IsNil() {
   162  			p.printf("nil")
   163  			return
   164  		}
   165  		p.dump(x.Elem(), depth-1)
   166  
   167  	case reflect.Ptr:
   168  		if x.IsNil() {
   169  			p.printf("nil")
   170  			return
   171  		}
   172  
   173  		p.printf("*")
   174  		ptr := x.Pointer()
   175  		if line, exists := p.ptrmap[ptr]; exists {
   176  			p.printf("(@%d)", line)
   177  			return
   178  		}
   179  		p.ptrmap[ptr] = p.line
   180  		p.dump(x.Elem(), depth) // don't count pointer indirection towards depth
   181  
   182  	case reflect.Slice:
   183  		if x.IsNil() {
   184  			p.printf("nil")
   185  			return
   186  		}
   187  		p.printf("%s (%d entries) {", x.Type(), x.Len())
   188  		if x.Len() > 0 {
   189  			p.indent++
   190  			p.printf("\n")
   191  			for i, n := 0, x.Len(); i < n; i++ {
   192  				p.printf("%d: ", i)
   193  				p.dump(x.Index(i), depth-1)
   194  				p.printf("\n")
   195  			}
   196  			p.indent--
   197  		}
   198  		p.printf("}")
   199  
   200  	case reflect.Struct:
   201  		typ := x.Type()
   202  
   203  		isNode := false
   204  		if n, ok := x.Interface().(Node); ok {
   205  			isNode = true
   206  			p.printf("%s %s {", n.Op.String(), p.addr(x))
   207  		} else {
   208  			p.printf("%s {", typ)
   209  		}
   210  		p.indent++
   211  
   212  		first := true
   213  		omitted := false
   214  		for i, n := 0, typ.NumField(); i < n; i++ {
   215  			// Exclude non-exported fields because their
   216  			// values cannot be accessed via reflection.
   217  			if name := typ.Field(i).Name; types.IsExported(name) {
   218  				if !p.fieldrx.MatchString(name) {
   219  					omitted = true
   220  					continue // field name not selected by filter
   221  				}
   222  
   223  				// special cases
   224  				if isNode && name == "Op" {
   225  					omitted = true
   226  					continue // Op field already printed for Nodes
   227  				}
   228  				x := x.Field(i)
   229  				if isZeroVal(x) {
   230  					omitted = true
   231  					continue // exclude zero-valued fields
   232  				}
   233  				if n, ok := x.Interface().(Nodes); ok && n.Len() == 0 {
   234  					omitted = true
   235  					continue // exclude empty Nodes slices
   236  				}
   237  
   238  				if first {
   239  					p.printf("\n")
   240  					first = false
   241  				}
   242  				p.printf("%s: ", name)
   243  				p.dump(x, depth-1)
   244  				p.printf("\n")
   245  			}
   246  		}
   247  		if omitted {
   248  			p.printf("…\n")
   249  		}
   250  
   251  		p.indent--
   252  		p.printf("}")
   253  
   254  	default:
   255  		p.printf("%v", x.Interface())
   256  	}
   257  }
   258  
   259  func isZeroVal(x reflect.Value) bool {
   260  	switch x.Kind() {
   261  	case reflect.Bool:
   262  		return !x.Bool()
   263  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   264  		return x.Int() == 0
   265  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   266  		return x.Uint() == 0
   267  	case reflect.String:
   268  		return x.String() == ""
   269  	case reflect.Interface, reflect.Ptr, reflect.Slice:
   270  		return x.IsNil()
   271  	}
   272  	return false
   273  }
   274  
   275  func commonPrefixLen(a, b string) (i int) {
   276  	for i < len(a) && i < len(b) && a[i] == b[i] {
   277  		i++
   278  	}
   279  	return
   280  }