mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			253 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2010 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // This file contains printing support for ASTs.
 | |
| 
 | |
| package ast
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"k8s.io/kubernetes/third_party/golang/go/token"
 | |
| )
 | |
| 
 | |
| // A FieldFilter may be provided to Fprint to control the output.
 | |
| type FieldFilter func(name string, value reflect.Value) bool
 | |
| 
 | |
| // NotNilFilter returns true for field values that are not nil;
 | |
| // it returns false otherwise.
 | |
| func NotNilFilter(_ string, v reflect.Value) bool {
 | |
| 	switch v.Kind() {
 | |
| 	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
 | |
| 		return !v.IsNil()
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Fprint prints the (sub-)tree starting at AST node x to w.
 | |
| // If fset != nil, position information is interpreted relative
 | |
| // to that file set. Otherwise positions are printed as integer
 | |
| // values (file set specific offsets).
 | |
| //
 | |
| // A non-nil FieldFilter f may be provided to control the output:
 | |
| // struct fields for which f(fieldname, fieldvalue) is true are
 | |
| // printed; all others are filtered from the output. Unexported
 | |
| // struct fields are never printed.
 | |
| //
 | |
| func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) {
 | |
| 	// setup printer
 | |
| 	p := printer{
 | |
| 		output: w,
 | |
| 		fset:   fset,
 | |
| 		filter: f,
 | |
| 		ptrmap: make(map[interface{}]int),
 | |
| 		last:   '\n', // force printing of line number on first line
 | |
| 	}
 | |
| 
 | |
| 	// install error handler
 | |
| 	defer func() {
 | |
| 		if e := recover(); e != nil {
 | |
| 			err = e.(localError).err // re-panics if it's not a localError
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	// print x
 | |
| 	if x == nil {
 | |
| 		p.printf("nil\n")
 | |
| 		return
 | |
| 	}
 | |
| 	p.print(reflect.ValueOf(x))
 | |
| 	p.printf("\n")
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Print prints x to standard output, skipping nil fields.
 | |
| // Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter).
 | |
| func Print(fset *token.FileSet, x interface{}) error {
 | |
| 	return Fprint(os.Stdout, fset, x, NotNilFilter)
 | |
| }
 | |
| 
 | |
| type printer struct {
 | |
| 	output io.Writer
 | |
| 	fset   *token.FileSet
 | |
| 	filter FieldFilter
 | |
| 	ptrmap map[interface{}]int // *T -> line number
 | |
| 	indent int                 // current indentation level
 | |
| 	last   byte                // the last byte processed by Write
 | |
| 	line   int                 // current line number
 | |
| }
 | |
| 
 | |
| var indent = []byte(".  ")
 | |
| 
 | |
| func (p *printer) Write(data []byte) (n int, err error) {
 | |
| 	var m int
 | |
| 	for i, b := range data {
 | |
| 		// invariant: data[0:n] has been written
 | |
| 		if b == '\n' {
 | |
| 			m, err = p.output.Write(data[n : i+1])
 | |
| 			n += m
 | |
| 			if err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			p.line++
 | |
| 		} else if p.last == '\n' {
 | |
| 			_, err = fmt.Fprintf(p.output, "%6d  ", p.line)
 | |
| 			if err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			for j := p.indent; j > 0; j-- {
 | |
| 				_, err = p.output.Write(indent)
 | |
| 				if err != nil {
 | |
| 					return
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		p.last = b
 | |
| 	}
 | |
| 	if len(data) > n {
 | |
| 		m, err = p.output.Write(data[n:])
 | |
| 		n += m
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // localError wraps locally caught errors so we can distinguish
 | |
| // them from genuine panics which we don't want to return as errors.
 | |
| type localError struct {
 | |
| 	err error
 | |
| }
 | |
| 
 | |
| // printf is a convenience wrapper that takes care of print errors.
 | |
| func (p *printer) printf(format string, args ...interface{}) {
 | |
| 	if _, err := fmt.Fprintf(p, format, args...); err != nil {
 | |
| 		panic(localError{err})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Implementation note: Print is written for AST nodes but could be
 | |
| // used to print arbitrary data structures; such a version should
 | |
| // probably be in a different package.
 | |
| //
 | |
| // Note: This code detects (some) cycles created via pointers but
 | |
| // not cycles that are created via slices or maps containing the
 | |
| // same slice or map. Code for general data structures probably
 | |
| // should catch those as well.
 | |
| 
 | |
| func (p *printer) print(x reflect.Value) {
 | |
| 	if !NotNilFilter("", x) {
 | |
| 		p.printf("nil")
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	switch x.Kind() {
 | |
| 	case reflect.Interface:
 | |
| 		p.print(x.Elem())
 | |
| 
 | |
| 	case reflect.Map:
 | |
| 		p.printf("%s (len = %d) {", x.Type(), x.Len())
 | |
| 		if x.Len() > 0 {
 | |
| 			p.indent++
 | |
| 			p.printf("\n")
 | |
| 			for _, key := range x.MapKeys() {
 | |
| 				p.print(key)
 | |
| 				p.printf(": ")
 | |
| 				p.print(x.MapIndex(key))
 | |
| 				p.printf("\n")
 | |
| 			}
 | |
| 			p.indent--
 | |
| 		}
 | |
| 		p.printf("}")
 | |
| 
 | |
| 	case reflect.Ptr:
 | |
| 		p.printf("*")
 | |
| 		// type-checked ASTs may contain cycles - use ptrmap
 | |
| 		// to keep track of objects that have been printed
 | |
| 		// already and print the respective line number instead
 | |
| 		ptr := x.Interface()
 | |
| 		if line, exists := p.ptrmap[ptr]; exists {
 | |
| 			p.printf("(obj @ %d)", line)
 | |
| 		} else {
 | |
| 			p.ptrmap[ptr] = p.line
 | |
| 			p.print(x.Elem())
 | |
| 		}
 | |
| 
 | |
| 	case reflect.Array:
 | |
| 		p.printf("%s {", x.Type())
 | |
| 		if x.Len() > 0 {
 | |
| 			p.indent++
 | |
| 			p.printf("\n")
 | |
| 			for i, n := 0, x.Len(); i < n; i++ {
 | |
| 				p.printf("%d: ", i)
 | |
| 				p.print(x.Index(i))
 | |
| 				p.printf("\n")
 | |
| 			}
 | |
| 			p.indent--
 | |
| 		}
 | |
| 		p.printf("}")
 | |
| 
 | |
| 	case reflect.Slice:
 | |
| 		if s, ok := x.Interface().([]byte); ok {
 | |
| 			p.printf("%#q", s)
 | |
| 			return
 | |
| 		}
 | |
| 		p.printf("%s (len = %d) {", x.Type(), x.Len())
 | |
| 		if x.Len() > 0 {
 | |
| 			p.indent++
 | |
| 			p.printf("\n")
 | |
| 			for i, n := 0, x.Len(); i < n; i++ {
 | |
| 				p.printf("%d: ", i)
 | |
| 				p.print(x.Index(i))
 | |
| 				p.printf("\n")
 | |
| 			}
 | |
| 			p.indent--
 | |
| 		}
 | |
| 		p.printf("}")
 | |
| 
 | |
| 	case reflect.Struct:
 | |
| 		t := x.Type()
 | |
| 		p.printf("%s {", t)
 | |
| 		p.indent++
 | |
| 		first := true
 | |
| 		for i, n := 0, t.NumField(); i < n; i++ {
 | |
| 			// exclude non-exported fields because their
 | |
| 			// values cannot be accessed via reflection
 | |
| 			if name := t.Field(i).Name; IsExported(name) {
 | |
| 				value := x.Field(i)
 | |
| 				if p.filter == nil || p.filter(name, value) {
 | |
| 					if first {
 | |
| 						p.printf("\n")
 | |
| 						first = false
 | |
| 					}
 | |
| 					p.printf("%s: ", name)
 | |
| 					p.print(value)
 | |
| 					p.printf("\n")
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		p.indent--
 | |
| 		p.printf("}")
 | |
| 
 | |
| 	default:
 | |
| 		v := x.Interface()
 | |
| 		switch v := v.(type) {
 | |
| 		case string:
 | |
| 			// print strings in quotes
 | |
| 			p.printf("%q", v)
 | |
| 			return
 | |
| 		case token.Pos:
 | |
| 			// position values can be printed nicely if we have a file set
 | |
| 			if p.fset != nil {
 | |
| 				p.printf("%s", p.fset.Position(v))
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 		// default
 | |
| 		p.printf("%v", v)
 | |
| 	}
 | |
| }
 |