github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/throw/stackwrap.go (about) 1 // Copyright 2020 Insolar Network Ltd. 2 // All rights reserved. 3 // This material is licensed under the Insolar License version 1.0, 4 // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md. 5 6 package throw 7 8 import "reflect" 9 10 // WithStackExt wraps the error with stack of caller. Nil value will return nil. 11 func WithStack(err error) error { 12 return WithStackExt(err, 1) 13 } 14 15 func WithStackAndDetails(predecessor error, details interface{}) error { 16 return WithStackExt(WithDetails(predecessor, details), 1) 17 } 18 19 // WithStackTop wraps the error with stack's topmost entry only. Nil value will return nil. 20 // Use this method to augment an error specific to a code location. 21 func WithStackTop(err error) error { 22 return WithStackTopExt(err, 1) 23 } 24 25 // WithStackExt wraps the error with stack with the given number of frames skipped. Nil value will return nil. 26 func WithStackExt(err error, skipFrames int) error { 27 if err == nil { 28 return nil 29 } 30 if skipFrames < 0 { 31 skipFrames = 0 32 } 33 return withStack(err, CaptureStack(skipFrames+1)) 34 } 35 36 // WithStackTopExt wraps the error with stack's topmost entry after skipping the given number of frames. Nil value will return nil. 37 func WithStackTopExt(err error, skipFrames int) error { 38 if err == nil { 39 return nil 40 } 41 if skipFrames < 0 { 42 skipFrames = 0 43 } 44 return withStack(err, CaptureStackTop(skipFrames+1)) 45 } 46 47 func reuseSupersetTrace(current, wrapped StackTrace) (StackTrace, DeepestStackMode) { 48 switch { 49 case current == nil: 50 return wrapped, InheritedTrace 51 case wrapped == nil: 52 return nil, 0 53 } 54 switch CompareStackTraceExt(current, wrapped, SameMethod) { 55 case SubsetStack, StackTop: 56 return wrapped, InheritedTrace 57 case FullStack: 58 return nil, SupersededTrace 59 default: 60 return nil, 0 61 } 62 } 63 64 func withStack(err error, st StackTrace) stackWrap { 65 if sth := OutermostStack(err); sth != nil { 66 stDeep, _ := sth.DeepestStackTrace() 67 stDeepest, stDeepMod := reuseSupersetTrace(st, stDeep) 68 return stackWrap{st, stDeepest, stDeepMod, err} 69 } 70 return stackWrap{st: st, err: err} 71 } 72 73 func WithDetails(predecessor error, details ...interface{}) error { 74 switch { 75 case len(details) == 0: 76 return predecessor 77 case predecessor == nil: 78 return WithDetails(NewDescription(details[0]), details[1:]...) 79 default: 80 for _, d := range details { 81 if d == nil { 82 continue 83 } 84 predecessor = withDetails(predecessor, d) 85 } 86 return predecessor 87 } 88 } 89 90 func withDetails(predecessor error, details interface{}) error { 91 var d fmtWrap 92 switch vv := details.(type) { 93 case fmtWrap: 94 d = vv 95 case panicWrap: 96 d = vv.fmtWrap 97 case msgWrap: 98 d.msg = vv.msg 99 case stackWrap: 100 if vv.err == nil { 101 return predecessor 102 } 103 return withDetails(predecessor, vv.err) 104 case nil: 105 // nil is handled by caller 106 panic("illegal value") 107 default: 108 d = wrapInternal(details) 109 } 110 111 return detailsWrap{err: predecessor, details: d, isComparable: reflect.TypeOf(details).Comparable()} 112 }