reprjs.nim 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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 for the javascript backend.
  10. proc reprInt(x: int64): string {.compilerproc.} = $x
  11. proc reprFloat(x: float): string {.compilerproc.} = $x
  12. proc reprPointer(p: pointer): string {.compilerproc.} =
  13. # Do we need to generate the full 8bytes ? In js a pointer is an int anyway
  14. var tmp: int
  15. {.emit: "`tmp` = `p`_Idx || 0;".}
  16. result = $tmp
  17. proc reprBool(x: bool): string {.compilerRtl.} =
  18. if x: result = "true"
  19. else: result = "false"
  20. proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
  21. var tmp: bool
  22. let item = typ.node.sons[e]
  23. {.emit: "`tmp` = `item` !== undefined".}
  24. if tmp:
  25. result = makeNimstrLit(item.name)
  26. else:
  27. result = $e & " (invalid data!)"
  28. proc reprChar(x: char): string {.compilerRtl.} =
  29. result = "\'"
  30. case x
  31. of '"': add(result, "\\\"")
  32. of '\\': add(result, "\\\\")
  33. of '\127'..'\255', '\0'..'\31': add(result, "\\" & reprInt(ord(x)))
  34. else: add(result, x)
  35. add(result, "\'")
  36. proc reprStrAux(result: var string, s: cstring | string, len: int) =
  37. add(result, "\"")
  38. for i in 0 .. len-1:
  39. let c = s[i]
  40. case c
  41. of '"': add(result, "\\\"")
  42. of '\\': add(result, "\\\\")
  43. #of '\10': add(result, "\\10\"\n\"")
  44. of '\127'..'\255', '\0'..'\31':
  45. add(result, "\\" & reprInt(ord(c)))
  46. else:
  47. add(result, c)
  48. add(result, "\"")
  49. proc reprStr(s: string): string {.compilerRtl.} =
  50. reprStrAux(result, s, s.len)
  51. proc addSetElem(result: var string, elem: int, typ: PNimType) =
  52. # Dispatch each set element to the correct repr<Type> proc
  53. case typ.kind:
  54. of tyEnum: add(result, reprEnum(elem, typ))
  55. of tyBool: add(result, reprBool(bool(elem)))
  56. of tyChar: add(result, reprChar(chr(elem)))
  57. of tyRange: addSetElem(result, elem, typ.base) # Note the base to advance towards the element type
  58. of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem)
  59. else: # data corrupt --> inform the user
  60. add(result, " (invalid data!)")
  61. iterator setKeys(s: int): int {.inline.} =
  62. # The type of s is a lie, but it's expected to be a set.
  63. # Iterate over the JS object representing a set
  64. # and returns the keys as int.
  65. var len: int
  66. var yieldRes: int
  67. var i: int = 0
  68. {. emit: """
  69. var setObjKeys = Object.getOwnPropertyNames(`s`);
  70. `len` = setObjKeys.length;
  71. """ .}
  72. while i < len:
  73. {. emit: "`yieldRes` = parseInt(setObjKeys[`i`],10);\n" .}
  74. yield yieldRes
  75. inc i
  76. proc reprSetAux(result: var string, s: int, typ: PNimType) =
  77. add(result, "{")
  78. var first: bool = true
  79. for el in setKeys(s):
  80. if first:
  81. first = false
  82. else:
  83. add(result, ", ")
  84. addSetElem(result, el, typ.base)
  85. add(result, "}")
  86. proc reprSet(e: int, typ: PNimType): string {.compilerRtl.} =
  87. reprSetAux(result, e, typ)
  88. type
  89. ReprClosure {.final.} = object
  90. recDepth: int # do not recurse endlessly
  91. indent: int # indentation
  92. proc initReprClosure(cl: var ReprClosure) =
  93. cl.recDepth = -1 # default is to display everything!
  94. cl.indent = 0
  95. proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure)
  96. proc reprArray(a: pointer, typ: PNimType,
  97. cl: var ReprClosure): string {.compilerRtl.} =
  98. # We prepend @ to seq, the C backend prepends the pointer to the seq.
  99. result = if typ.kind == tySequence: "@[" else: "["
  100. var len: int = 0
  101. var i: int = 0
  102. {. emit: "`len` = `a`.length;\n" .}
  103. var dereffed: pointer = a
  104. for i in 0 .. len-1:
  105. if i > 0 :
  106. add(result, ", ")
  107. # advance pointer and point to element at index
  108. {. emit: """
  109. `dereffed`_Idx = `i`;
  110. `dereffed` = `a`[`dereffed`_Idx];
  111. """ .}
  112. reprAux(result, dereffed, typ.base, cl)
  113. add(result, "]")
  114. proc isPointedToNil(p: pointer): bool =
  115. {. emit: "if (`p` === null) {`result` = true};\n" .}
  116. proc reprRef(result: var string, p: pointer, typ: PNimType,
  117. cl: var ReprClosure) =
  118. if p.isPointedToNil:
  119. add(result, "nil")
  120. return
  121. add(result, "ref " & reprPointer(p))
  122. add(result, " --> ")
  123. if typ.base.kind != tyArray:
  124. {. emit: """
  125. if (`p` != null && `p`.length > 0) {
  126. `p` = `p`[`p`_Idx];
  127. }
  128. """ .}
  129. reprAux(result, p, typ.base, cl)
  130. proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) =
  131. add(result, "[")
  132. var first = true
  133. var val = o
  134. if typ.node.len == 0:
  135. # if the object has only one field, len is 0 and sons is nil, the field is in node
  136. let key: cstring = typ.node.name
  137. add(result, $key & " = ")
  138. {. emit: "`val` = `o`[`key`];\n" .}
  139. reprAux(result, val, typ.node.typ, cl)
  140. else:
  141. # if the object has more than one field, sons is not nil and contains the fields.
  142. for i in 0 .. typ.node.len-1:
  143. if first: first = false
  144. else: add(result, ",\n")
  145. let key: cstring = typ.node.sons[i].name
  146. add(result, $key & " = ")
  147. {. emit: "`val` = `o`[`key`];\n" .} # access the field by name
  148. reprAux(result, val, typ.node.sons[i].typ, cl)
  149. add(result, "]")
  150. proc reprRecord(o: pointer, typ: PNimType, cl: var ReprClosure): string {.compilerRtl.} =
  151. reprRecordAux(result, o, typ, cl)
  152. proc reprJsonStringify(p: int): string {.compilerRtl.} =
  153. # As a last resort, use stringify
  154. # We use this for tyOpenArray, tyVarargs while genTypeInfo is not implemented
  155. var tmp: cstring
  156. {. emit: "`tmp` = JSON.stringify(`p`);\n" .}
  157. result = $tmp
  158. proc reprAux(result: var string, p: pointer, typ: PNimType,
  159. cl: var ReprClosure) =
  160. if cl.recDepth == 0:
  161. add(result, "...")
  162. return
  163. dec(cl.recDepth)
  164. case typ.kind
  165. of tyInt..tyInt64, tyUInt..tyUInt64:
  166. add(result, reprInt(cast[int](p)))
  167. of tyChar:
  168. add(result, reprChar(cast[char](p)))
  169. of tyBool:
  170. add(result, reprBool(cast[bool](p)))
  171. of tyFloat..tyFloat128:
  172. add(result, reprFloat(cast[float](p)))
  173. of tyString:
  174. var fp: int
  175. {. emit: "`fp` = `p`;\n" .}
  176. add(result, reprStr(cast[string](p)))
  177. of tyCstring:
  178. var fp: cstring
  179. {. emit: "`fp` = `p`;\n" .}
  180. if fp.isNil:
  181. add(result, "nil")
  182. else:
  183. reprStrAux(result, fp, fp.len)
  184. of tyEnum, tyOrdinal:
  185. var fp: int
  186. {. emit: "`fp` = `p`;\n" .}
  187. add(result, reprEnum(fp, typ))
  188. of tySet:
  189. var fp: int
  190. {. emit: "`fp` = `p`;\n" .}
  191. add(result, reprSet(fp, typ))
  192. of tyRange: reprAux(result, p, typ.base, cl)
  193. of tyObject, tyTuple:
  194. add(result, reprRecord(p, typ, cl))
  195. of tyArray, tyArrayConstr, tySequence:
  196. add(result, reprArray(p, typ, cl))
  197. of tyPointer:
  198. add(result, reprPointer(p))
  199. of tyPtr, tyRef:
  200. reprRef(result, p, typ, cl)
  201. of tyProc:
  202. if p.isPointedToNil:
  203. add(result, "nil")
  204. else:
  205. add(result, reprPointer(p))
  206. else:
  207. add(result, "(invalid data!)" & reprJsonStringify(cast[int](p)))
  208. inc(cl.recDepth)
  209. proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} =
  210. var cl: ReprClosure
  211. initReprClosure(cl)
  212. reprAux(result, p, typ, cl)
  213. when defined(nimLegacyReprWithNewline): # see PR #16034
  214. add result, "\n"