OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package render | 5 package render |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "fmt" | 9 "fmt" |
10 "reflect" | 10 "reflect" |
11 "sort" | 11 "sort" |
12 "strconv" | 12 "strconv" |
13 ) | 13 ) |
14 | 14 |
15 var implicitTypeMap = map[reflect.Kind]string{ | 15 var builtinTypeMap = map[reflect.Kind]string{ |
16 reflect.Bool: "bool", | 16 reflect.Bool: "bool", |
17 » reflect.String: "string", | 17 » reflect.Complex128: "complex128", |
18 » reflect.Int: "int", | 18 » reflect.Complex64: "complex64", |
19 » reflect.Int8: "int8", | 19 » reflect.Float32: "float32", |
| 20 » reflect.Float64: "float64", |
20 reflect.Int16: "int16", | 21 reflect.Int16: "int16", |
21 reflect.Int32: "int32", | 22 reflect.Int32: "int32", |
22 reflect.Int64: "int64", | 23 reflect.Int64: "int64", |
23 » reflect.Uint: "uint", | 24 » reflect.Int8: "int8", |
24 » reflect.Uint8: "uint8", | 25 » reflect.Int: "int", |
| 26 » reflect.String: "string", |
25 reflect.Uint16: "uint16", | 27 reflect.Uint16: "uint16", |
26 reflect.Uint32: "uint32", | 28 reflect.Uint32: "uint32", |
27 reflect.Uint64: "uint64", | 29 reflect.Uint64: "uint64", |
28 » reflect.Float32: "float32", | 30 » reflect.Uint8: "uint8", |
29 » reflect.Float64: "float64", | 31 » reflect.Uint: "uint", |
30 » reflect.Complex64: "complex64", | 32 » reflect.Uintptr: "uintptr", |
31 » reflect.Complex128: "complex128", | |
32 } | 33 } |
33 | 34 |
| 35 var builtinTypeSet = map[string]struct{}{} |
| 36 |
| 37 func init() { |
| 38 for _, v := range builtinTypeMap { |
| 39 builtinTypeSet[v] = struct{}{} |
| 40 } |
| 41 } |
| 42 |
| 43 var typeOfString = reflect.TypeOf("") |
| 44 var typeOfInt = reflect.TypeOf(int(1)) |
| 45 var typeOfUint = reflect.TypeOf(uint(1)) |
| 46 var typeOfFloat = reflect.TypeOf(10.1) |
| 47 |
34 // Render converts a structure to a string representation. Unline the "%#v" | 48 // Render converts a structure to a string representation. Unline the "%#v" |
35 // format string, this resolves pointer types' contents in structs, maps, and | 49 // format string, this resolves pointer types' contents in structs, maps, and |
36 // slices/arrays and prints their field values. | 50 // slices/arrays and prints their field values. |
37 func Render(v interface{}) string { | 51 func Render(v interface{}) string { |
38 buf := bytes.Buffer{} | 52 buf := bytes.Buffer{} |
39 s := (*traverseState)(nil) | 53 s := (*traverseState)(nil) |
40 » s.render(&buf, 0, reflect.ValueOf(v)) | 54 » s.render(&buf, 0, reflect.ValueOf(v), false) |
41 return buf.String() | 55 return buf.String() |
42 } | 56 } |
43 | 57 |
44 // renderPointer is called to render a pointer value. | 58 // renderPointer is called to render a pointer value. |
45 // | 59 // |
46 // This is overridable so that the test suite can have deterministic pointer | 60 // This is overridable so that the test suite can have deterministic pointer |
47 // values in its expectations. | 61 // values in its expectations. |
48 var renderPointer = func(buf *bytes.Buffer, p uintptr) { | 62 var renderPointer = func(buf *bytes.Buffer, p uintptr) { |
49 fmt.Fprintf(buf, "0x%016x", p) | 63 fmt.Fprintf(buf, "0x%016x", p) |
50 } | 64 } |
(...skipping 14 matching lines...) Expand all Loading... |
65 } | 79 } |
66 } | 80 } |
67 | 81 |
68 fs := &traverseState{ | 82 fs := &traverseState{ |
69 parent: s, | 83 parent: s, |
70 ptr: ptr, | 84 ptr: ptr, |
71 } | 85 } |
72 return fs | 86 return fs |
73 } | 87 } |
74 | 88 |
75 func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value) { | 89 func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value, imp
licit bool) { |
76 if v.Kind() == reflect.Invalid { | 90 if v.Kind() == reflect.Invalid { |
77 buf.WriteString("nil") | 91 buf.WriteString("nil") |
78 return | 92 return |
79 } | 93 } |
80 vt := v.Type() | 94 vt := v.Type() |
81 | 95 |
82 // If the type being rendered is a potentially recursive type (a type th
at | 96 // If the type being rendered is a potentially recursive type (a type th
at |
83 // can contain itself as a member), we need to avoid recursion. | 97 // can contain itself as a member), we need to avoid recursion. |
84 // | 98 // |
85 // If we've already seen this type before, mark that this is the case an
d | 99 // If we've already seen this type before, mark that this is the case an
d |
(...skipping 14 matching lines...) Expand all Loading... |
100 pe = v.Pointer() | 114 pe = v.Pointer() |
101 } | 115 } |
102 | 116 |
103 case reflect.Slice, reflect.Map: | 117 case reflect.Slice, reflect.Map: |
104 pe = v.Pointer() | 118 pe = v.Pointer() |
105 } | 119 } |
106 if pe != 0 { | 120 if pe != 0 { |
107 s = s.forkFor(pe) | 121 s = s.forkFor(pe) |
108 if s == nil { | 122 if s == nil { |
109 buf.WriteString("<REC(") | 123 buf.WriteString("<REC(") |
110 » » » writeType(buf, ptrs, vt) | 124 » » » if !implicit { |
| 125 » » » » writeType(buf, ptrs, vt) |
| 126 » » » } |
111 buf.WriteString(")>") | 127 buf.WriteString(")>") |
112 return | 128 return |
113 } | 129 } |
114 } | 130 } |
115 | 131 |
| 132 isAnon := func(t reflect.Type) bool { |
| 133 if t.Name() != "" { |
| 134 if _, ok := builtinTypeSet[t.Name()]; !ok { |
| 135 return false |
| 136 } |
| 137 } |
| 138 return t.Kind() != reflect.Interface |
| 139 } |
| 140 |
116 switch vk { | 141 switch vk { |
117 case reflect.Struct: | 142 case reflect.Struct: |
118 » » writeType(buf, ptrs, vt) | 143 » » if !implicit { |
| 144 » » » writeType(buf, ptrs, vt) |
| 145 » » } |
| 146 » » structAnon := vt.Name() == "" |
119 buf.WriteRune('{') | 147 buf.WriteRune('{') |
120 for i := 0; i < vt.NumField(); i++ { | 148 for i := 0; i < vt.NumField(); i++ { |
121 if i > 0 { | 149 if i > 0 { |
122 buf.WriteString(", ") | 150 buf.WriteString(", ") |
123 } | 151 } |
124 » » » buf.WriteString(vt.Field(i).Name) | 152 » » » anon := structAnon && isAnon(vt.Field(i).Type) |
125 » » » buf.WriteRune(':') | |
126 | 153 |
127 » » » s.render(buf, 0, v.Field(i)) | 154 » » » if !anon { |
| 155 » » » » buf.WriteString(vt.Field(i).Name) |
| 156 » » » » buf.WriteRune(':') |
| 157 » » » } |
| 158 |
| 159 » » » s.render(buf, 0, v.Field(i), anon) |
128 } | 160 } |
129 buf.WriteRune('}') | 161 buf.WriteRune('}') |
130 | 162 |
131 case reflect.Slice: | 163 case reflect.Slice: |
132 if v.IsNil() { | 164 if v.IsNil() { |
133 » » » writeType(buf, ptrs, vt) | 165 » » » if !implicit { |
134 » » » buf.WriteString("(nil)") | 166 » » » » writeType(buf, ptrs, vt) |
| 167 » » » » buf.WriteString("(nil)") |
| 168 » » » } else { |
| 169 » » » » buf.WriteString("nil") |
| 170 » » » } |
135 return | 171 return |
136 } | 172 } |
137 fallthrough | 173 fallthrough |
138 | 174 |
139 case reflect.Array: | 175 case reflect.Array: |
140 » » writeType(buf, ptrs, vt) | 176 » » if !implicit { |
| 177 » » » writeType(buf, ptrs, vt) |
| 178 » » } |
| 179 » » anon := vt.Name() == "" && isAnon(vt.Elem()) |
141 buf.WriteString("{") | 180 buf.WriteString("{") |
142 for i := 0; i < v.Len(); i++ { | 181 for i := 0; i < v.Len(); i++ { |
143 if i > 0 { | 182 if i > 0 { |
144 buf.WriteString(", ") | 183 buf.WriteString(", ") |
145 } | 184 } |
146 | 185 |
147 » » » s.render(buf, 0, v.Index(i)) | 186 » » » s.render(buf, 0, v.Index(i), anon) |
148 } | 187 } |
149 buf.WriteRune('}') | 188 buf.WriteRune('}') |
150 | 189 |
151 case reflect.Map: | 190 case reflect.Map: |
152 » » writeType(buf, ptrs, vt) | 191 » » if !implicit { |
| 192 » » » writeType(buf, ptrs, vt) |
| 193 » » } |
153 if v.IsNil() { | 194 if v.IsNil() { |
154 buf.WriteString("(nil)") | 195 buf.WriteString("(nil)") |
155 } else { | 196 } else { |
156 buf.WriteString("{") | 197 buf.WriteString("{") |
157 | 198 |
158 mkeys := v.MapKeys() | 199 mkeys := v.MapKeys() |
159 tryAndSortMapKeys(vt, mkeys) | 200 tryAndSortMapKeys(vt, mkeys) |
160 | 201 |
| 202 kt := vt.Key() |
| 203 keyAnon := typeOfString.ConvertibleTo(kt) || typeOfInt.C
onvertibleTo(kt) || typeOfUint.ConvertibleTo(kt) || typeOfFloat.ConvertibleTo(kt
) |
| 204 valAnon := vt.Name() == "" && isAnon(vt.Elem()) |
161 for i, mk := range mkeys { | 205 for i, mk := range mkeys { |
162 if i > 0 { | 206 if i > 0 { |
163 buf.WriteString(", ") | 207 buf.WriteString(", ") |
164 } | 208 } |
165 | 209 |
166 » » » » s.render(buf, 0, mk) | 210 » » » » s.render(buf, 0, mk, keyAnon) |
167 buf.WriteString(":") | 211 buf.WriteString(":") |
168 » » » » s.render(buf, 0, v.MapIndex(mk)) | 212 » » » » s.render(buf, 0, v.MapIndex(mk), valAnon) |
169 } | 213 } |
170 buf.WriteRune('}') | 214 buf.WriteRune('}') |
171 } | 215 } |
172 | 216 |
173 case reflect.Ptr: | 217 case reflect.Ptr: |
174 ptrs++ | 218 ptrs++ |
175 fallthrough | 219 fallthrough |
176 case reflect.Interface: | 220 case reflect.Interface: |
177 if v.IsNil() { | 221 if v.IsNil() { |
178 writeType(buf, ptrs, v.Type()) | 222 writeType(buf, ptrs, v.Type()) |
179 » » » buf.WriteRune('(') | 223 » » » buf.WriteString("(nil)") |
180 » » » fmt.Fprint(buf, "nil") | |
181 » » » buf.WriteRune(')') | |
182 } else { | 224 } else { |
183 » » » s.render(buf, ptrs, v.Elem()) | 225 » » » s.render(buf, ptrs, v.Elem(), false) |
184 } | 226 } |
185 | 227 |
186 case reflect.Chan, reflect.Func, reflect.UnsafePointer: | 228 case reflect.Chan, reflect.Func, reflect.UnsafePointer: |
187 writeType(buf, ptrs, vt) | 229 writeType(buf, ptrs, vt) |
188 buf.WriteRune('(') | 230 buf.WriteRune('(') |
189 renderPointer(buf, v.Pointer()) | 231 renderPointer(buf, v.Pointer()) |
190 buf.WriteRune(')') | 232 buf.WriteRune(')') |
191 | 233 |
192 default: | 234 default: |
193 tstr := vt.String() | 235 tstr := vt.String() |
194 » » implicit := ptrs == 0 && implicitTypeMap[vk] == tstr | 236 » » implicit = implicit || (ptrs == 0 && builtinTypeMap[vk] == tstr) |
195 if !implicit { | 237 if !implicit { |
196 writeType(buf, ptrs, vt) | 238 writeType(buf, ptrs, vt) |
197 buf.WriteRune('(') | 239 buf.WriteRune('(') |
198 } | 240 } |
199 | 241 |
200 switch vk { | 242 switch vk { |
201 case reflect.String: | 243 case reflect.String: |
202 fmt.Fprintf(buf, "%q", v.String()) | 244 fmt.Fprintf(buf, "%q", v.String()) |
203 case reflect.Bool: | 245 case reflect.Bool: |
204 fmt.Fprintf(buf, "%v", v.Bool()) | 246 fmt.Fprintf(buf, "%v", v.Bool()) |
205 | 247 |
206 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, re
flect.Int64: | 248 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, re
flect.Int64: |
207 fmt.Fprintf(buf, "%d", v.Int()) | 249 fmt.Fprintf(buf, "%d", v.Int()) |
208 | 250 |
209 » » case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32
, reflect.Uint64: | 251 » » case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32
, reflect.Uint64, reflect.Uintptr: |
210 fmt.Fprintf(buf, "%d", v.Uint()) | 252 fmt.Fprintf(buf, "%d", v.Uint()) |
211 | 253 |
212 case reflect.Float32, reflect.Float64: | 254 case reflect.Float32, reflect.Float64: |
213 fmt.Fprintf(buf, "%g", v.Float()) | 255 fmt.Fprintf(buf, "%g", v.Float()) |
214 | 256 |
215 case reflect.Complex64, reflect.Complex128: | 257 case reflect.Complex64, reflect.Complex128: |
216 fmt.Fprintf(buf, "%g", v.Complex()) | 258 fmt.Fprintf(buf, "%g", v.Complex()) |
217 } | 259 } |
218 | 260 |
219 if !implicit { | 261 if !implicit { |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
318 // Try our stock sortable values. | 360 // Try our stock sortable values. |
319 switch mt.Key().Kind() { | 361 switch mt.Key().Kind() { |
320 case reflect.String, reflect.Int: | 362 case reflect.String, reflect.Int: |
321 vs := &sortableValueSlice{ | 363 vs := &sortableValueSlice{ |
322 kind: mt.Key().Kind(), | 364 kind: mt.Key().Kind(), |
323 elements: k, | 365 elements: k, |
324 } | 366 } |
325 sort.Sort(vs) | 367 sort.Sort(vs) |
326 } | 368 } |
327 } | 369 } |
OLD | NEW |