repr.nim 11 KB

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