You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.6 KiB

  1. // Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
  2. //
  3. // Permission to use, copy, modify, and distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. // NOTE: Due to the following build constraints, this file will only be compiled
  15. // when the code is not running on Google App Engine, compiled by GopherJS, and
  16. // "-tags safe" is not added to the go build command line. The "disableunsafe"
  17. // tag is deprecated and thus should not be used.
  18. // Go versions prior to 1.4 are disabled because they use a different layout
  19. // for interfaces which make the implementation of unsafeReflectValue more complex.
  20. // +build !js,!appengine,!safe,!disableunsafe,go1.4
  21. package spew
  22. import (
  23. "reflect"
  24. "unsafe"
  25. )
  26. const (
  27. // UnsafeDisabled is a build-time constant which specifies whether or
  28. // not access to the unsafe package is available.
  29. UnsafeDisabled = false
  30. // ptrSize is the size of a pointer on the current arch.
  31. ptrSize = unsafe.Sizeof((*byte)(nil))
  32. )
  33. type flag uintptr
  34. var (
  35. // flagRO indicates whether the value field of a reflect.Value
  36. // is read-only.
  37. flagRO flag
  38. // flagAddr indicates whether the address of the reflect.Value's
  39. // value may be taken.
  40. flagAddr flag
  41. )
  42. // flagKindMask holds the bits that make up the kind
  43. // part of the flags field. In all the supported versions,
  44. // it is in the lower 5 bits.
  45. const flagKindMask = flag(0x1f)
  46. // Different versions of Go have used different
  47. // bit layouts for the flags type. This table
  48. // records the known combinations.
  49. var okFlags = []struct {
  50. ro, addr flag
  51. }{{
  52. // From Go 1.4 to 1.5
  53. ro: 1 << 5,
  54. addr: 1 << 7,
  55. }, {
  56. // Up to Go tip.
  57. ro: 1<<5 | 1<<6,
  58. addr: 1 << 8,
  59. }}
  60. var flagValOffset = func() uintptr {
  61. field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
  62. if !ok {
  63. panic("reflect.Value has no flag field")
  64. }
  65. return field.Offset
  66. }()
  67. // flagField returns a pointer to the flag field of a reflect.Value.
  68. func flagField(v *reflect.Value) *flag {
  69. return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
  70. }
  71. // unsafeReflectValue converts the passed reflect.Value into a one that bypasses
  72. // the typical safety restrictions preventing access to unaddressable and
  73. // unexported data. It works by digging the raw pointer to the underlying
  74. // value out of the protected value and generating a new unprotected (unsafe)
  75. // reflect.Value to it.
  76. //
  77. // This allows us to check for implementations of the Stringer and error
  78. // interfaces to be used for pretty printing ordinarily unaddressable and
  79. // inaccessible values such as unexported struct fields.
  80. func unsafeReflectValue(v reflect.Value) reflect.Value {
  81. if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
  82. return v
  83. }
  84. flagFieldPtr := flagField(&v)
  85. *flagFieldPtr &^= flagRO
  86. *flagFieldPtr |= flagAddr
  87. return v
  88. }
  89. // Sanity checks against future reflect package changes
  90. // to the type or semantics of the Value.flag field.
  91. func init() {
  92. field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
  93. if !ok {
  94. panic("reflect.Value has no flag field")
  95. }
  96. if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
  97. panic("reflect.Value flag field has changed kind")
  98. }
  99. type t0 int
  100. var t struct {
  101. A t0
  102. // t0 will have flagEmbedRO set.
  103. t0
  104. // a will have flagStickyRO set
  105. a t0
  106. }
  107. vA := reflect.ValueOf(t).FieldByName("A")
  108. va := reflect.ValueOf(t).FieldByName("a")
  109. vt0 := reflect.ValueOf(t).FieldByName("t0")
  110. // Infer flagRO from the difference between the flags
  111. // for the (otherwise identical) fields in t.
  112. flagPublic := *flagField(&vA)
  113. flagWithRO := *flagField(&va) | *flagField(&vt0)
  114. flagRO = flagPublic ^ flagWithRO
  115. // Infer flagAddr from the difference between a value
  116. // taken from a pointer and not.
  117. vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
  118. flagNoPtr := *flagField(&vA)
  119. flagPtr := *flagField(&vPtrA)
  120. flagAddr = flagNoPtr ^ flagPtr
  121. // Check that the inferred flags tally with one of the known versions.
  122. for _, f := range okFlags {
  123. if flagRO == f.ro && flagAddr == f.addr {
  124. return
  125. }
  126. }
  127. panic("reflect.Value read-only flag has changed semantics")
  128. }