repr.nim 11 KB

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