github.com/KarpelesLab/contexter@v1.0.2/contexter.go (about) 1 package contexter 2 3 import ( 4 "context" 5 "reflect" 6 "regexp" 7 "runtime/debug" 8 "strconv" 9 "unsafe" 10 ) 11 12 var ( 13 matchHex = regexp.MustCompile("[^+]0x([a-fA-F0-9]{2,16})\\??, 0x([a-fA-F0-9]{2,16})\\??") 14 intfType = reflect.TypeOf((*context.Context)(nil)).Elem() 15 intfTypeVal = intfValue(intfType) 16 ) 17 18 type itab struct { 19 inter uintptr 20 typ uintptr 21 } 22 23 type interfaceHeader struct { 24 tab *itab 25 data unsafe.Pointer 26 } 27 28 func intfValue(v interface{}) uintptr { 29 x := (*interfaceHeader)(unsafe.Pointer(&v)) 30 return uintptr(x.data) 31 } 32 33 func testPtr(p1, p2 uintptr) context.Context { 34 defer func() { 35 recover() 36 }() 37 38 // we expect itab in p1 39 tab := (*itab)(unsafe.Pointer(p1)) 40 41 if tab == nil || tab.inter != intfTypeVal { 42 // not a context.Context interface 43 return nil 44 } 45 46 var res context.Context 47 ti := (*interfaceHeader)(unsafe.Pointer(&res)) 48 ti.data = unsafe.Pointer(p2) 49 ti.tab = tab 50 51 return res 52 } 53 54 func testTab(tab, expect uintptr) bool { 55 defer func() { 56 recover() 57 }() 58 59 t := (*itab)(unsafe.Pointer(tab)) 60 return t.inter == expect 61 } 62 63 // Context will return the first instance of context.Context found in the 64 // calling stack 65 func Context() context.Context { 66 // argp := unsafe.Pointer(frame.argp) 67 // The "instruction" of argument printing is encoded in _FUNCDATA_ArgInfo. 68 // See cmd/compile/internal/ssagen.emitArgInfo for the description of the 69 // encoding 70 // _FUNCDATA_ArgInfo = 5 71 // ... 72 // Unfortunately(?) go provides no way to get this kind of data from the 73 // stack except by calling runtime.Stack(), in which case pointers are 74 // encoded in hex as string. This is not optimal, but at least it works. 75 76 v := debug.Stack() 77 res := matchHex.FindAllSubmatch(v, -1) 78 for _, h := range res { 79 i1, _ := strconv.ParseUint(string(h[1]), 16, 64) 80 i2, _ := strconv.ParseUint(string(h[2]), 16, 64) 81 82 if i1 == 0 || i2 == 0 { 83 continue 84 } 85 86 res := testPtr(uintptr(i1), uintptr(i2)) 87 if res != nil { 88 return res 89 } 90 } 91 92 return nil 93 } 94 95 func Find(i interface{}) bool { 96 // try to find object of type intf 97 v := reflect.ValueOf(i).Elem() 98 searchType := intfValue(v.Type()) 99 100 // when an interface is called, it passes two pointers 101 res := matchHex.FindAllSubmatch(debug.Stack(), -1) 102 for _, h := range res { 103 i1, _ := strconv.ParseUint(string(h[1]), 16, 64) 104 i2, _ := strconv.ParseUint(string(h[2]), 16, 64) 105 106 if i1 == 0 || i2 == 0 { 107 continue 108 } 109 110 if !testTab(uintptr(i1), searchType) { 111 continue 112 } 113 // found our value 114 ti := (*interfaceHeader)(unsafe.Pointer(v.UnsafeAddr())) 115 ti.tab = (*itab)(unsafe.Pointer(uintptr(i1))) 116 ti.data = unsafe.Pointer(uintptr(i2)) 117 return true 118 } 119 return false 120 }