• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017, 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.md file.
4
5// Package value provides functionality for reflect.Value types.
6package value
7
8import (
9	"fmt"
10	"reflect"
11	"strings"
12	"unicode"
13	"unicode/utf8"
14)
15
16// formatFakePointers controls whether to substitute pointer addresses with nil.
17// This is used for deterministic testing.
18var formatFakePointers = false
19
20var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
21
22// Format formats the value v as a string.
23//
24// This is similar to fmt.Sprintf("%+v", v) except this:
25//	* Prints the type unless it can be elided
26//	* Avoids printing struct fields that are zero
27//	* Prints a nil-slice as being nil, not empty
28//	* Prints map entries in deterministic order
29func Format(v reflect.Value, useStringer bool) string {
30	return formatAny(v, formatConfig{useStringer, true, true, !formatFakePointers}, nil)
31}
32
33type formatConfig struct {
34	useStringer    bool // Should the String method be used if available?
35	printType      bool // Should we print the type before the value?
36	followPointers bool // Should we recursively follow pointers?
37	realPointers   bool // Should we print the real address of pointers?
38}
39
40func formatAny(v reflect.Value, conf formatConfig, visited map[uintptr]bool) string {
41	// TODO: Should this be a multi-line printout in certain situations?
42
43	if !v.IsValid() {
44		return "<non-existent>"
45	}
46	if conf.useStringer && v.Type().Implements(stringerIface) && v.CanInterface() {
47		if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() {
48			return "<nil>"
49		}
50		return fmt.Sprintf("%q", v.Interface().(fmt.Stringer).String())
51	}
52
53	switch v.Kind() {
54	case reflect.Bool:
55		return formatPrimitive(v.Type(), v.Bool(), conf)
56	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
57		return formatPrimitive(v.Type(), v.Int(), conf)
58	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
59		if v.Type().PkgPath() == "" || v.Kind() == reflect.Uintptr {
60			// Unnamed uints are usually bytes or words, so use hexadecimal.
61			return formatPrimitive(v.Type(), formatHex(v.Uint()), conf)
62		}
63		return formatPrimitive(v.Type(), v.Uint(), conf)
64	case reflect.Float32, reflect.Float64:
65		return formatPrimitive(v.Type(), v.Float(), conf)
66	case reflect.Complex64, reflect.Complex128:
67		return formatPrimitive(v.Type(), v.Complex(), conf)
68	case reflect.String:
69		return formatPrimitive(v.Type(), fmt.Sprintf("%q", v), conf)
70	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
71		return formatPointer(v, conf)
72	case reflect.Ptr:
73		if v.IsNil() {
74			if conf.printType {
75				return fmt.Sprintf("(%v)(nil)", v.Type())
76			}
77			return "<nil>"
78		}
79		if visited[v.Pointer()] || !conf.followPointers {
80			return formatPointer(v, conf)
81		}
82		visited = insertPointer(visited, v.Pointer())
83		return "&" + formatAny(v.Elem(), conf, visited)
84	case reflect.Interface:
85		if v.IsNil() {
86			if conf.printType {
87				return fmt.Sprintf("%v(nil)", v.Type())
88			}
89			return "<nil>"
90		}
91		return formatAny(v.Elem(), conf, visited)
92	case reflect.Slice:
93		if v.IsNil() {
94			if conf.printType {
95				return fmt.Sprintf("%v(nil)", v.Type())
96			}
97			return "<nil>"
98		}
99		if visited[v.Pointer()] {
100			return formatPointer(v, conf)
101		}
102		visited = insertPointer(visited, v.Pointer())
103		fallthrough
104	case reflect.Array:
105		var ss []string
106		subConf := conf
107		subConf.printType = v.Type().Elem().Kind() == reflect.Interface
108		for i := 0; i < v.Len(); i++ {
109			s := formatAny(v.Index(i), subConf, visited)
110			ss = append(ss, s)
111		}
112		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
113		if conf.printType {
114			return v.Type().String() + s
115		}
116		return s
117	case reflect.Map:
118		if v.IsNil() {
119			if conf.printType {
120				return fmt.Sprintf("%v(nil)", v.Type())
121			}
122			return "<nil>"
123		}
124		if visited[v.Pointer()] {
125			return formatPointer(v, conf)
126		}
127		visited = insertPointer(visited, v.Pointer())
128
129		var ss []string
130		subConf := conf
131		subConf.printType = v.Type().Elem().Kind() == reflect.Interface
132		for _, k := range SortKeys(v.MapKeys()) {
133			sk := formatAny(k, formatConfig{realPointers: conf.realPointers}, visited)
134			sv := formatAny(v.MapIndex(k), subConf, visited)
135			ss = append(ss, fmt.Sprintf("%s: %s", sk, sv))
136		}
137		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
138		if conf.printType {
139			return v.Type().String() + s
140		}
141		return s
142	case reflect.Struct:
143		var ss []string
144		subConf := conf
145		subConf.printType = true
146		for i := 0; i < v.NumField(); i++ {
147			vv := v.Field(i)
148			if isZero(vv) {
149				continue // Elide zero value fields
150			}
151			name := v.Type().Field(i).Name
152			subConf.useStringer = conf.useStringer && isExported(name)
153			s := formatAny(vv, subConf, visited)
154			ss = append(ss, fmt.Sprintf("%s: %s", name, s))
155		}
156		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
157		if conf.printType {
158			return v.Type().String() + s
159		}
160		return s
161	default:
162		panic(fmt.Sprintf("%v kind not handled", v.Kind()))
163	}
164}
165
166func formatPrimitive(t reflect.Type, v interface{}, conf formatConfig) string {
167	if conf.printType && t.PkgPath() != "" {
168		return fmt.Sprintf("%v(%v)", t, v)
169	}
170	return fmt.Sprintf("%v", v)
171}
172
173func formatPointer(v reflect.Value, conf formatConfig) string {
174	p := v.Pointer()
175	if !conf.realPointers {
176		p = 0 // For deterministic printing purposes
177	}
178	s := formatHex(uint64(p))
179	if conf.printType {
180		return fmt.Sprintf("(%v)(%s)", v.Type(), s)
181	}
182	return s
183}
184
185func formatHex(u uint64) string {
186	var f string
187	switch {
188	case u <= 0xff:
189		f = "0x%02x"
190	case u <= 0xffff:
191		f = "0x%04x"
192	case u <= 0xffffff:
193		f = "0x%06x"
194	case u <= 0xffffffff:
195		f = "0x%08x"
196	case u <= 0xffffffffff:
197		f = "0x%010x"
198	case u <= 0xffffffffffff:
199		f = "0x%012x"
200	case u <= 0xffffffffffffff:
201		f = "0x%014x"
202	case u <= 0xffffffffffffffff:
203		f = "0x%016x"
204	}
205	return fmt.Sprintf(f, u)
206}
207
208// insertPointer insert p into m, allocating m if necessary.
209func insertPointer(m map[uintptr]bool, p uintptr) map[uintptr]bool {
210	if m == nil {
211		m = make(map[uintptr]bool)
212	}
213	m[p] = true
214	return m
215}
216
217// isZero reports whether v is the zero value.
218// This does not rely on Interface and so can be used on unexported fields.
219func isZero(v reflect.Value) bool {
220	switch v.Kind() {
221	case reflect.Bool:
222		return v.Bool() == false
223	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
224		return v.Int() == 0
225	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
226		return v.Uint() == 0
227	case reflect.Float32, reflect.Float64:
228		return v.Float() == 0
229	case reflect.Complex64, reflect.Complex128:
230		return v.Complex() == 0
231	case reflect.String:
232		return v.String() == ""
233	case reflect.UnsafePointer:
234		return v.Pointer() == 0
235	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
236		return v.IsNil()
237	case reflect.Array:
238		for i := 0; i < v.Len(); i++ {
239			if !isZero(v.Index(i)) {
240				return false
241			}
242		}
243		return true
244	case reflect.Struct:
245		for i := 0; i < v.NumField(); i++ {
246			if !isZero(v.Field(i)) {
247				return false
248			}
249		}
250		return true
251	}
252	return false
253}
254
255// isExported reports whether the identifier is exported.
256func isExported(id string) bool {
257	r, _ := utf8.DecodeRuneInString(id)
258	return unicode.IsUpper(r)
259}
260