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