func.go 2.7 KB

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