@@ -97,11 +97,12 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
9797 cycleFound := false
9898 indirects := 0
9999 ve := v
100- for ve .Kind () == reflect .Ptr {
100+ for ve .Kind () == reflect .Pointer {
101101 if ve .IsNil () {
102102 nilFound = true
103103 break
104104 }
105+
105106 indirects ++
106107 addr := ve .Pointer ()
107108 pointerChain = append (pointerChain , addr )
@@ -114,11 +115,27 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
114115
115116 ve = ve .Elem ()
116117 if ve .Kind () == reflect .Interface {
117- if ve .IsNil () {
118+ if ve .IsNil () { // interface with nil value
118119 nilFound = true
119120 break
120121 }
121122 ve = ve .Elem ()
123+ if ve .Kind () == reflect .Pointer {
124+ if ve .IsNil () {
125+ nilFound = true
126+ break
127+ }
128+
129+ // case of interface containing a pointer that cycles to the same depth level.
130+ // If we have a cycle at the same level, we should break the loop now.
131+ addr = ve .Pointer ()
132+ if pd , ok := d .pointers [addr ]; ok && pd <= d .depth {
133+ cycleFound = true
134+ indirects --
135+ break
136+ }
137+ d .pointers [addr ] = d .depth
138+ }
122139 }
123140 }
124141
@@ -156,6 +173,64 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
156173 d .w .Write (closeParenBytes )
157174}
158175
176+ func (d * dumpState ) dumpMap (v reflect.Value ) {
177+ // Remove pointers at or below the current depth from map used to detect
178+ // circular refs.
179+ for k , depth := range d .pointers {
180+ if depth >= d .depth {
181+ delete (d .pointers , k )
182+ }
183+ }
184+
185+ // Keep list of all dereferenced pointers to show later.
186+ cycleFound := false
187+
188+ // nil maps should be indicated as different than empty maps
189+ if v .IsNil () {
190+ _ , _ = d .w .Write (nilAngleBytes )
191+ return
192+ }
193+
194+ // maps like pointers may present circular references
195+ addr := v .Pointer ()
196+ if pd , ok := d .pointers [addr ]; ok && pd <= d .depth {
197+ cycleFound = true
198+ }
199+ d .pointers [addr ] = d .depth
200+
201+ _ , _ = d .w .Write (openBraceNewlineBytes )
202+ d .depth ++
203+
204+ switch {
205+ case d .cs .MaxDepth != 0 && d .depth > d .cs .MaxDepth :
206+ d .indent ()
207+ _ , _ = d .w .Write (maxNewlineBytes )
208+ case cycleFound :
209+ _ , _ = d .w .Write (circularBytes )
210+ default :
211+ numEntries := v .Len ()
212+ keys := v .MapKeys ()
213+ if d .cs .SortKeys {
214+ sortValues (keys , d .cs )
215+ }
216+ for i , key := range keys {
217+ d .dump (d .unpackValue (key ))
218+ _ , _ = d .w .Write (colonSpaceBytes )
219+ d .ignoreNextIndent = true
220+ d .dump (d .unpackValue (v .MapIndex (key )))
221+ if i < (numEntries - 1 ) {
222+ _ , _ = d .w .Write (commaNewlineBytes )
223+ } else {
224+ _ , _ = d .w .Write (newlineBytes )
225+ }
226+ }
227+ }
228+
229+ d .depth --
230+ d .indent ()
231+ _ , _ = d .w .Write (closeBraceBytes )
232+ }
233+
159234// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
160235// reflection) arrays and slices are dumped in hexdump -C fashion.
161236func (d * dumpState ) dumpSlice (v reflect.Value ) {
@@ -257,7 +332,7 @@ func (d *dumpState) dump(v reflect.Value) {
257332 }
258333
259334 // Handle pointers specially.
260- if kind == reflect .Ptr {
335+ if kind == reflect .Pointer {
261336 d .indent ()
262337 d .dumpPtr (v )
263338 return
@@ -367,43 +442,12 @@ func (d *dumpState) dump(v reflect.Value) {
367442 d .w .Write (nilAngleBytes )
368443 }
369444
370- case reflect .Ptr :
445+ case reflect .Pointer :
371446 // Do nothing. We should never get here since pointers have already
372447 // been handled above.
373448
374449 case reflect .Map :
375- // nil maps should be indicated as different than empty maps
376- if v .IsNil () {
377- d .w .Write (nilAngleBytes )
378- break
379- }
380-
381- d .w .Write (openBraceNewlineBytes )
382- d .depth ++
383- if (d .cs .MaxDepth != 0 ) && (d .depth > d .cs .MaxDepth ) {
384- d .indent ()
385- d .w .Write (maxNewlineBytes )
386- } else {
387- numEntries := v .Len ()
388- keys := v .MapKeys ()
389- if d .cs .SortKeys {
390- sortValues (keys , d .cs )
391- }
392- for i , key := range keys {
393- d .dump (d .unpackValue (key ))
394- d .w .Write (colonSpaceBytes )
395- d .ignoreNextIndent = true
396- d .dump (d .unpackValue (v .MapIndex (key )))
397- if i < (numEntries - 1 ) {
398- d .w .Write (commaNewlineBytes )
399- } else {
400- d .w .Write (newlineBytes )
401- }
402- }
403- }
404- d .depth --
405- d .indent ()
406- d .w .Write (closeBraceBytes )
450+ d .dumpMap (v )
407451
408452 case reflect .Struct :
409453 d .w .Write (openBraceNewlineBytes )
0 commit comments