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 file. 4 5// Package function provides functionality for identifying function types. 6package function 7 8import ( 9 "reflect" 10 "regexp" 11 "runtime" 12 "strings" 13) 14 15type funcType int 16 17const ( 18 _ funcType = iota 19 20 tbFunc // func(T) bool 21 ttbFunc // func(T, T) bool 22 trbFunc // func(T, R) bool 23 tibFunc // func(T, I) bool 24 trFunc // func(T) R 25 26 Equal = ttbFunc // func(T, T) bool 27 EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool 28 Transformer = trFunc // func(T) R 29 ValueFilter = ttbFunc // func(T, T) bool 30 Less = ttbFunc // func(T, T) bool 31 ValuePredicate = tbFunc // func(T) bool 32 KeyValuePredicate = trbFunc // func(T, R) bool 33) 34 35var boolType = reflect.TypeOf(true) 36 37// IsType reports whether the reflect.Type is of the specified function type. 38func IsType(t reflect.Type, ft funcType) bool { 39 if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { 40 return false 41 } 42 ni, no := t.NumIn(), t.NumOut() 43 switch ft { 44 case tbFunc: // func(T) bool 45 if ni == 1 && no == 1 && t.Out(0) == boolType { 46 return true 47 } 48 case ttbFunc: // func(T, T) bool 49 if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { 50 return true 51 } 52 case trbFunc: // func(T, R) bool 53 if ni == 2 && no == 1 && t.Out(0) == boolType { 54 return true 55 } 56 case tibFunc: // func(T, I) bool 57 if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { 58 return true 59 } 60 case trFunc: // func(T) R 61 if ni == 1 && no == 1 { 62 return true 63 } 64 } 65 return false 66} 67 68var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`) 69 70// NameOf returns the name of the function value. 71func NameOf(v reflect.Value) string { 72 fnc := runtime.FuncForPC(v.Pointer()) 73 if fnc == nil { 74 return "<unknown>" 75 } 76 fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm" 77 78 // Method closures have a "-fm" suffix. 79 fullName = strings.TrimSuffix(fullName, "-fm") 80 81 var name string 82 for len(fullName) > 0 { 83 inParen := strings.HasSuffix(fullName, ")") 84 fullName = strings.TrimSuffix(fullName, ")") 85 86 s := lastIdentRx.FindString(fullName) 87 if s == "" { 88 break 89 } 90 name = s + "." + name 91 fullName = strings.TrimSuffix(fullName, s) 92 93 if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 { 94 fullName = fullName[:i] 95 } 96 fullName = strings.TrimSuffix(fullName, ".") 97 } 98 return strings.TrimSuffix(name, ".") 99} 100