reflect.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. package neffos
  2. import (
  3. "reflect"
  4. "strings"
  5. )
  6. func indirectType(typ reflect.Type) reflect.Type {
  7. if typ.Kind() == reflect.Ptr {
  8. typ = typ.Elem()
  9. }
  10. return typ
  11. }
  12. func isZero(v reflect.Value) bool {
  13. switch v.Kind() {
  14. case reflect.Struct:
  15. zero := true
  16. for i := 0; i < v.NumField(); i++ {
  17. zero = zero && isZero(v.Field(i))
  18. }
  19. if typ := v.Type(); typ != nil && v.IsValid() {
  20. f, ok := typ.MethodByName("IsZero")
  21. // if not found
  22. // if has input arguments (1 is for the value receiver, so > 1 for the actual input args)
  23. // if output argument is not boolean
  24. // then skip this IsZero user-defined function.
  25. if !ok || f.Type.NumIn() > 1 || f.Type.NumOut() != 1 && f.Type.Out(0).Kind() != reflect.Bool {
  26. return zero
  27. }
  28. method := v.Method(f.Index)
  29. // no needed check but:
  30. if method.IsValid() && !method.IsNil() {
  31. // it shouldn't panic here.
  32. zero = method.Call(nil)[0].Interface().(bool)
  33. }
  34. }
  35. return zero
  36. case reflect.Func, reflect.Map, reflect.Slice:
  37. return v.IsNil()
  38. case reflect.Array:
  39. zero := true
  40. for i := 0; i < v.Len(); i++ {
  41. zero = zero && isZero(v.Index(i))
  42. }
  43. return zero
  44. }
  45. // if not any special type then use the reflect's .Zero
  46. // usually for fields, but remember if it's boolean and it's false
  47. // then it's zero, even if set-ed.
  48. if !v.CanInterface() {
  49. // if can't interface, i.e return value from unexported field or method then return false
  50. return false
  51. }
  52. zero := reflect.Zero(v.Type())
  53. return v.Interface() == zero.Interface()
  54. }
  55. // does not support child elements on purpose.
  56. func visitFields(typ reflect.Type, visitor func(f reflect.StructField) bool) int {
  57. typ = indirectType(typ)
  58. for n, i := typ.NumField(), 0; i < n; i++ {
  59. f := typ.Field(i)
  60. found := visitor(f)
  61. if found {
  62. return i
  63. }
  64. }
  65. return -1
  66. }
  67. func getNonZeroFields(v reflect.Value) (fields map[int]reflect.Value) {
  68. v = reflect.Indirect(v)
  69. visitFields(v.Type(), func(f reflect.StructField) bool {
  70. fieldIndex := f.Index[0]
  71. fieldValue := v.Field(fieldIndex)
  72. if !isZero(fieldValue) {
  73. if fields == nil {
  74. fields = make(map[int]reflect.Value)
  75. }
  76. fields[fieldIndex] = fieldValue
  77. }
  78. return false
  79. })
  80. return
  81. }
  82. func getFieldIndex(forType reflect.Type, fieldType reflect.Type) int {
  83. return visitFields(forType, func(f reflect.StructField) bool {
  84. return f.Type == fieldType
  85. })
  86. }
  87. func resolveStructNamespace(v reflect.Value) (string, bool) {
  88. // By Namespace() string method.
  89. typ := v.Type()
  90. method, ok := typ.MethodByName("Namespace")
  91. if ok {
  92. if getNamespace, ok := v.Method(method.Index).Interface().(func() string); ok {
  93. namespace := getNamespace()
  94. Debugf("Set namespace [\"%s\"] from method [%s.%s]", func() dargs {
  95. return dargs{namespace, nameOf(typ), method.Name}
  96. })
  97. return namespace, true
  98. }
  99. }
  100. // By field Namespace string with filled value.
  101. typ = indirectType(typ)
  102. v = reflect.Indirect(v)
  103. if f, ok := typ.FieldByNameFunc(func(s string) bool { return s == "Namespace" }); ok {
  104. if f.Type.Kind() == reflect.String {
  105. namespace := v.Field(f.Index[0]).String()
  106. Debugf("Set namespace [\"%s\"] from field [%s.%s]", func() dargs {
  107. return dargs{namespace, nameOf(typ), f.Name}
  108. })
  109. return namespace, true
  110. }
  111. }
  112. return "", false
  113. }
  114. var (
  115. nsConnType = reflect.TypeOf((*NSConn)(nil))
  116. msgType = reflect.TypeOf(Message{})
  117. errType = reflect.TypeOf((*error)(nil)).Elem()
  118. )
  119. func makeMessageHandlerFuncType(forType reflect.Type, nsConnFieldIndex int) reflect.Type {
  120. // Create the dynamic type which methods will be compared to.
  121. // remember, the receiver Ptr is also part of the input arguments,
  122. // that's why we don't use a static type assertion.
  123. expectedIn := []reflect.Type{
  124. forType,
  125. nsConnType,
  126. msgType,
  127. }
  128. if nsConnFieldIndex >= 0 {
  129. // Except when the Ptr is a dynamic one (has a field of NSConn) then the event callback does not require
  130. // that on its input arguments.
  131. expectedIn = append(expectedIn[0:1], expectedIn[2:]...)
  132. }
  133. return reflect.FuncOf(expectedIn, []reflect.Type{errType}, false)
  134. }
  135. func isArgOf(fnType reflect.Type, argType reflect.Type) bool {
  136. if fnType.Kind() != reflect.Func {
  137. panic("isArgOf used on a non-method type")
  138. }
  139. for i, n := 0, fnType.NumIn(); i < n; i++ {
  140. if fnType.In(i) == argType {
  141. return true
  142. }
  143. }
  144. return false
  145. }
  146. func makeEventFromMethod(v reflect.Value, method reflect.Method, eventMatcher EventMatcherFunc) (eventName string, cb MessageHandlerFunc) {
  147. eventName = method.Name
  148. // if method looks like a system event, i.e
  149. // OnNamespaceConnected, then convert its registered event name
  150. // _OnNamespaceConnected which is the correct.
  151. // We could accept a func like:
  152. // func(s *myConn) _OnNamespaceConnected(msg neffos.Message) error
  153. // but Go linting does not allow this and
  154. // we don't want our users to have yellow boxes everywhere in their editors.
  155. if IsSystemEvent("_" + eventName) {
  156. eventName = "_" + eventName
  157. }
  158. if !IsSystemEvent(eventName) {
  159. if eventMatcher != nil {
  160. newName, ok := eventMatcher(method.Name)
  161. if !ok {
  162. return "", nil
  163. }
  164. eventName = newName
  165. }
  166. }
  167. if isArgOf(method.Type, nsConnType) {
  168. // it should accept NSConn - static "controller".
  169. cb = v.Method(method.Index).Interface().(func(*NSConn, Message) error)
  170. } else {
  171. // the NSConn exists on the "controller" itself which is set dynamically.
  172. cb = func(c *NSConn, msg Message) error {
  173. // load an existing instance which contains the same "c".
  174. return c.value.Method(method.Index).Interface().(func(Message) error)(msg)
  175. }
  176. }
  177. return
  178. }
  179. // StructInjector is a type which injects a dynamic struct value.
  180. // See `Struct.SetInjector` for more.
  181. type StructInjector func(structType reflect.Type, nsConn *NSConn) (structValue reflect.Value)
  182. func nameOf(structType reflect.Type) string {
  183. structType = indirectType(structType)
  184. typName := structType.Name()
  185. pkgPath := structType.PkgPath()
  186. fullname := pkgPath[strings.LastIndexByte(pkgPath, '/')+1:] + "." + typName
  187. return fullname
  188. }
  189. func makeEventsFromStruct(v reflect.Value, eventMatcher EventMatcherFunc, injector StructInjector) Events {
  190. events := make(Events)
  191. typ := v.Type()
  192. // get the index of field of a "NSConn" type.
  193. nsConnFieldIndex := getFieldIndex(typ, nsConnType)
  194. msgHandlerType := makeMessageHandlerFuncType(typ, nsConnFieldIndex)
  195. for i, n := 0, typ.NumMethod(); i < n; i++ {
  196. method := typ.Method(i)
  197. if method.Type != msgHandlerType {
  198. continue
  199. }
  200. eventName, cb := makeEventFromMethod(v, method, eventMatcher)
  201. if cb == nil {
  202. continue
  203. }
  204. Debugf("Event [\"%s\"] is handled by [%s.%s] method", func() dargs {
  205. return dargs{eventName, nameOf(typ), method.Name}
  206. })
  207. events[eventName] = cb
  208. }
  209. if nsConnFieldIndex != -1 {
  210. typ = indirectType(typ)
  211. var staticFields map[int]reflect.Value
  212. if injector == nil {
  213. // maybe this should be added no matter what, I have to check
  214. // some things in our company's production server first.
  215. staticFields = getNonZeroFields(v)
  216. DebugEach(staticFields, func(idx int, f reflect.Value) {
  217. fval := f.Interface()
  218. fname := typ.Field(idx).Name
  219. if fname == "Namespace" {
  220. // let's no log this as user field because
  221. // it's optionally used to provide a namespace on NewStruct.GetNamespaces().
  222. return
  223. }
  224. Debugf("Field [%s.%s] marked as static on value [%v]", nameOf(typ), fname, fval)
  225. })
  226. injector = func(typ reflect.Type, nsConn *NSConn) reflect.Value {
  227. return reflect.New(typ)
  228. }
  229. }
  230. cb, hasNamespaceConnect := events[OnNamespaceConnect]
  231. events[OnNamespaceConnect] = func(c *NSConn, msg Message) error {
  232. cachePtr := injector(typ, c)
  233. cacheElem := cachePtr.Elem()
  234. // set the NSConn dynamic field.
  235. cacheElem.Field(nsConnFieldIndex).Set(reflect.ValueOf(c))
  236. // set any static fields if default injector (see above).
  237. for findex, fvalue := range staticFields {
  238. cacheElem.Field(findex).Set(fvalue)
  239. }
  240. // Store it for the rest of the events inside
  241. // this namespace of that specific connection.
  242. c.value = cachePtr
  243. if hasNamespaceConnect {
  244. return cb(c, msg)
  245. }
  246. return nil
  247. }
  248. }
  249. return events
  250. }