repr.nim 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # The generic ``repr`` procedure. It is an invaluable debugging tool.
  10. when not defined(useNimRtl):
  11. proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl, gcsafe.}
  12. proc reprInt(x: int64): string {.compilerproc.} = return $x
  13. proc reprFloat(x: float): string {.compilerproc.} = return $x
  14. proc reprPointer(x: pointer): string {.compilerproc.} =
  15. result = newString(60)
  16. let n = c_sprintf(cast[cstring](addr result[0]), "%p", x)
  17. setLen(result, n)
  18. proc reprStrAux(result: var string, s: cstring; len: int) =
  19. if cast[pointer](s) == nil:
  20. add result, "nil"
  21. return
  22. if len > 0:
  23. add result, reprPointer(cast[pointer](s))
  24. add result, "\""
  25. for i in 0 .. pred(len):
  26. let c = s[i]
  27. case c
  28. of '"': add result, "\\\""
  29. of '\\': add result, "\\\\" # BUGFIX: forgotten
  30. of '\10': add result, "\\10\"\n\"" # " \n " # better readability
  31. of '\127' .. '\255', '\0'..'\9', '\11'..'\31':
  32. add result, "\\" & reprInt(ord(c))
  33. else:
  34. result.add(c)
  35. add result, "\""
  36. proc reprStr(s: string): string {.compilerRtl.} =
  37. result = ""
  38. reprStrAux(result, s, s.len)
  39. proc reprBool(x: bool): string {.compilerRtl.} =
  40. if x: result = "true"
  41. else: result = "false"
  42. proc reprChar(x: char): string {.compilerRtl.} =
  43. result = "\'"
  44. case x
  45. of '"': add result, "\\\""
  46. of '\\': add result, "\\\\"
  47. of '\127' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x))
  48. else: add result, x
  49. add result, "\'"
  50. proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
  51. ## Return string representation for enumeration values
  52. var n = typ.node
  53. if ntfEnumHole notin typ.flags:
  54. let o = e - n.sons[0].offset
  55. if o >= 0 and o <% typ.node.len:
  56. return $n.sons[o].name
  57. else:
  58. # ugh we need a slow linear search:
  59. var s = n.sons
  60. for i in 0 .. n.len-1:
  61. if s[i].offset == e:
  62. return $s[i].name
  63. result = $e & " (invalid data!)"
  64. include system/repr_impl
  65. type
  66. PByteArray = ptr UncheckedArray[byte] # array[0xffff, byte]
  67. proc addSetElem(result: var string, elem: int, typ: PNimType) {.benign.} =
  68. case typ.kind
  69. of tyEnum: add result, reprEnum(elem, typ)
  70. of tyBool: add result, reprBool(bool(elem))
  71. of tyChar: add result, reprChar(chr(elem))
  72. of tyRange: addSetElem(result, elem, typ.base)
  73. of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem)
  74. else: # data corrupt --> inform the user
  75. add result, " (invalid data!)"
  76. proc reprSetAux(result: var string, p: pointer, typ: PNimType) =
  77. # "typ.slots.len" field is for sets the "first" field
  78. var elemCounter = 0 # we need this flag for adding the comma at
  79. # the right places
  80. add result, "{"
  81. var u: uint64
  82. case typ.size
  83. of 1: u = cast[ptr uint8](p)[]
  84. of 2: u = cast[ptr uint16](p)[]
  85. of 4: u = cast[ptr uint32](p)[]
  86. of 8: u = cast[ptr uint64](p)[]
  87. else:
  88. u = uint64(0)
  89. var a = cast[PByteArray](p)
  90. for i in 0 .. typ.size*8-1:
  91. if (uint(a[i shr 3]) and (1'u shl (i and 7))) != 0:
  92. if elemCounter > 0: add result, ", "
  93. addSetElem(result, i+typ.node.len, typ.base)
  94. inc(elemCounter)
  95. if typ.size <= 8:
  96. for i in 0..sizeof(int64)*8-1:
  97. if (u and (1'u64 shl uint64(i))) != 0'u64:
  98. if elemCounter > 0: add result, ", "
  99. addSetElem(result, i+typ.node.len, typ.base)
  100. inc(elemCounter)
  101. add result, "}"
  102. proc reprSet(p: pointer, typ: PNimType): string {.compilerRtl.} =
  103. result = ""
  104. reprSetAux(result, p, typ)
  105. type
  106. ReprClosure {.final.} = object # we cannot use a global variable here
  107. # as this wouldn't be thread-safe
  108. when declared(CellSet):
  109. marked: CellSet
  110. recdepth: int # do not recurse endlessly
  111. indent: int # indentation
  112. when not defined(useNimRtl):
  113. proc initReprClosure(cl: var ReprClosure) =
  114. # Important: cellsets does not lock the heap when doing allocations! We
  115. # have to do it here ...
  116. when hasThreadSupport and hasSharedHeap and declared(heapLock):
  117. AcquireSys(HeapLock)
  118. when declared(CellSet):
  119. init(cl.marked)
  120. cl.recdepth = -1 # default is to display everything!
  121. cl.indent = 0
  122. proc deinitReprClosure(cl: var ReprClosure) =
  123. when declared(CellSet): deinit(cl.marked)
  124. when hasThreadSupport and hasSharedHeap and declared(heapLock):
  125. ReleaseSys(HeapLock)
  126. proc reprBreak(result: var string, cl: ReprClosure) =
  127. add result, "\n"
  128. for i in 0..cl.indent-1: add result, ' '
  129. proc reprAux(result: var string, p: pointer, typ: PNimType,
  130. cl: var ReprClosure) {.benign.}
  131. proc reprArray(result: var string, p: pointer, typ: PNimType,
  132. cl: var ReprClosure) =
  133. add result, "["
  134. var bs = typ.base.size
  135. for i in 0..typ.size div bs - 1:
  136. if i > 0: add result, ", "
  137. reprAux(result, cast[pointer](cast[int](p) + i*bs), typ.base, cl)
  138. add result, "]"
  139. when defined(nimSeqsV2):
  140. type
  141. GenericSeq = object
  142. len: int
  143. p: pointer
  144. PGenericSeq = ptr GenericSeq
  145. const payloadOffset = sizeof(int) + sizeof(pointer)
  146. # see seqs.nim: cap: int
  147. # region: Allocator
  148. template payloadPtr(x: untyped): untyped = cast[PGenericSeq](x).p
  149. else:
  150. const payloadOffset = GenericSeqSize ## the payload offset always depends on the alignment of the member type.
  151. template payloadPtr(x: untyped): untyped = x
  152. proc reprSequence(result: var string, p: pointer, typ: PNimType,
  153. cl: var ReprClosure) =
  154. if p == nil:
  155. add result, "[]"
  156. return
  157. result.add(reprPointer(p))
  158. result.add "@["
  159. var bs = typ.base.size
  160. for i in 0..cast[PGenericSeq](p).len-1:
  161. if i > 0: add result, ", "
  162. reprAux(result, cast[pointer](cast[int](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs),
  163. typ.base, cl)
  164. add result, "]"
  165. proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
  166. cl: var ReprClosure) {.benign.} =
  167. case n.kind
  168. of nkNone: sysAssert(false, "reprRecordAux")
  169. of nkSlot:
  170. add result, $n.name
  171. add result, " = "
  172. reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl)
  173. of nkList:
  174. for i in 0..n.len-1:
  175. if i > 0: add result, ",\n"
  176. reprRecordAux(result, p, n.sons[i], cl)
  177. of nkCase:
  178. var m = selectBranch(p, n)
  179. reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl)
  180. if m != nil: reprRecordAux(result, p, m, cl)
  181. proc reprRecord(result: var string, p: pointer, typ: PNimType,
  182. cl: var ReprClosure) =
  183. add result, "["
  184. var curTyp = typ
  185. var first = true
  186. while curTyp != nil:
  187. var part = ""
  188. reprRecordAux(part, p, curTyp.node, cl)
  189. if part.len > 0:
  190. if not first:
  191. add result, ",\n"
  192. add result, part
  193. first = false
  194. curTyp = curTyp.base
  195. add result, "]"
  196. proc reprRef(result: var string, p: pointer, typ: PNimType,
  197. cl: var ReprClosure) =
  198. # we know that p is not nil here:
  199. when declared(CellSet):
  200. when defined(boehmGC) or defined(gogc) or defined(nogc) or usesDestructors:
  201. var cell = cast[PCell](p)
  202. else:
  203. var cell = usrToCell(p)
  204. add result, if typ.kind == tyPtr: "ptr " else: "ref "
  205. add result, reprPointer(p)
  206. if cell notin cl.marked:
  207. # only the address is shown:
  208. incl(cl.marked, cell)
  209. add result, " --> "
  210. reprAux(result, p, typ.base, cl)
  211. proc getInt(p: pointer, size: int): int =
  212. case size
  213. of 1: return int(cast[ptr uint8](p)[])
  214. of 2: return int(cast[ptr uint16](p)[])
  215. of 4: return int(cast[ptr uint32](p)[])
  216. of 8: return int(cast[ptr uint64](p)[])
  217. else: discard
  218. proc reprAux(result: var string, p: pointer, typ: PNimType,
  219. cl: var ReprClosure) =
  220. if cl.recdepth == 0:
  221. add result, "..."
  222. return
  223. dec(cl.recdepth)
  224. case typ.kind
  225. of tySet: reprSetAux(result, p, typ)
  226. of tyArray, tyArrayConstr: reprArray(result, p, typ, cl)
  227. of tyTuple: reprRecord(result, p, typ, cl)
  228. of tyObject:
  229. var t = cast[ptr PNimType](p)[]
  230. reprRecord(result, p, t, cl)
  231. of tyRef, tyPtr:
  232. sysAssert(p != nil, "reprAux")
  233. if cast[PPointer](p)[] == nil: add result, "nil"
  234. else: reprRef(result, cast[PPointer](p)[], typ, cl)
  235. of tySequence:
  236. reprSequence(result, cast[PPointer](p)[], typ, cl)
  237. of tyInt: add result, $(cast[ptr int](p)[])
  238. of tyInt8: add result, $int(cast[ptr int8](p)[])
  239. of tyInt16: add result, $int(cast[ptr int16](p)[])
  240. of tyInt32: add result, $int(cast[ptr int32](p)[])
  241. of tyInt64: add result, $(cast[ptr int64](p)[])
  242. of tyUInt: add result, $(cast[ptr uint](p)[])
  243. of tyUInt8: add result, $(cast[ptr uint8](p)[])
  244. of tyUInt16: add result, $(cast[ptr uint16](p)[])
  245. of tyUInt32: add result, $(cast[ptr uint32](p)[])
  246. of tyUInt64: add result, $(cast[ptr uint64](p)[])
  247. of tyFloat: add result, $(cast[ptr float](p)[])
  248. of tyFloat32: add result, $(cast[ptr float32](p)[])
  249. of tyFloat64: add result, $(cast[ptr float64](p)[])
  250. of tyEnum: add result, reprEnum(getInt(p, typ.size), typ)
  251. of tyBool: add result, reprBool(cast[ptr bool](p)[])
  252. of tyChar: add result, reprChar(cast[ptr char](p)[])
  253. of tyString:
  254. let sp = cast[ptr string](p)
  255. reprStrAux(result, sp[].cstring, sp[].len)
  256. of tyCstring:
  257. let cs = cast[ptr cstring](p)[]
  258. if cs.isNil: add result, "nil"
  259. else: reprStrAux(result, cs, cs.len)
  260. of tyRange: reprAux(result, p, typ.base, cl)
  261. of tyProc, tyPointer:
  262. if cast[PPointer](p)[] == nil: add result, "nil"
  263. else: add result, reprPointer(cast[PPointer](p)[])
  264. of tyUncheckedArray:
  265. add result, "[...]"
  266. else:
  267. add result, "(invalid data!)"
  268. inc(cl.recdepth)
  269. when not defined(useNimRtl):
  270. proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {.
  271. compilerRtl.} =
  272. var
  273. cl: ReprClosure
  274. initReprClosure(cl)
  275. result = "["
  276. var bs = elemtyp.size
  277. for i in 0..length - 1:
  278. if i > 0: add result, ", "
  279. reprAux(result, cast[pointer](cast[int](p) + i*bs), elemtyp, cl)
  280. add result, "]"
  281. deinitReprClosure(cl)
  282. when not defined(useNimRtl):
  283. proc reprAny(p: pointer, typ: PNimType): string =
  284. var
  285. cl: ReprClosure
  286. initReprClosure(cl)
  287. result = ""
  288. if typ.kind in {tyObject, tyTuple, tyArray, tyArrayConstr, tySet}:
  289. reprAux(result, p, typ, cl)
  290. else:
  291. var p = p
  292. reprAux(result, addr(p), typ, cl)
  293. when defined(nimLegacyReprWithNewline): # see PR #16034
  294. add result, "\n"
  295. deinitReprClosure(cl)