reprjs.nim 8.1 KB

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