vmops.nim 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # Unfortunately this cannot be a module yet:
  10. #import vmdeps, vm
  11. from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
  12. arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
  13. floor, ceil, `mod`
  14. from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename
  15. from md5 import getMD5
  16. from sighashes import symBodyDigest
  17. from times import cpuTime
  18. from hashes import hash
  19. from osproc import nil
  20. import vmconv
  21. template mathop(op) {.dirty.} =
  22. registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
  23. template osop(op) {.dirty.} =
  24. registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`)
  25. template timesop(op) {.dirty.} =
  26. registerCallback(c, "stdlib.times." & astToStr(op), `op Wrapper`)
  27. template systemop(op) {.dirty.} =
  28. registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`)
  29. template ioop(op) {.dirty.} =
  30. registerCallback(c, "stdlib.io." & astToStr(op), `op Wrapper`)
  31. template macrosop(op) {.dirty.} =
  32. registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`)
  33. template md5op(op) {.dirty.} =
  34. registerCallback(c, "stdlib.md5." & astToStr(op), `op Wrapper`)
  35. template wrap1f_math(op) {.dirty.} =
  36. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  37. setResult(a, op(getFloat(a, 0)))
  38. mathop op
  39. template wrap2f_math(op) {.dirty.} =
  40. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  41. setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
  42. mathop op
  43. template wrap0(op, modop) {.dirty.} =
  44. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  45. setResult(a, op())
  46. modop op
  47. template wrap1s(op, modop) {.dirty.} =
  48. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  49. setResult(a, op(getString(a, 0)))
  50. modop op
  51. template wrap2s(op, modop) {.dirty.} =
  52. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  53. setResult(a, op(getString(a, 0), getString(a, 1)))
  54. modop op
  55. template wrap2si(op, modop) {.dirty.} =
  56. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  57. setResult(a, op(getString(a, 0), getInt(a, 1)))
  58. modop op
  59. template wrap1svoid(op, modop) {.dirty.} =
  60. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  61. op(getString(a, 0))
  62. modop op
  63. template wrap2svoid(op, modop) {.dirty.} =
  64. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  65. op(getString(a, 0), getString(a, 1))
  66. modop op
  67. template wrapDangerous(op, modop) {.dirty.} =
  68. if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
  69. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  70. discard
  71. modop op
  72. else:
  73. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  74. op(getString(a, 0), getString(a, 1))
  75. modop op
  76. proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
  77. setResult(a, if a.currentException.isNil: ""
  78. else: a.currentException[3].skipColon.strVal)
  79. proc getCurrentExceptionWrapper(a: VmArgs) {.nimcall.} =
  80. setResult(a, a.currentException)
  81. proc staticWalkDirImpl(path: string, relative: bool): PNode =
  82. result = newNode(nkBracket)
  83. for k, f in walkDir(path, relative):
  84. result.add toLit((k, f))
  85. when defined(nimHasInvariant):
  86. from std / compilesettings import SingleValueSetting, MultipleValueSetting
  87. proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string =
  88. case SingleValueSetting(switch)
  89. of arguments: result = conf.arguments
  90. of outFile: result = conf.outFile.string
  91. of outDir: result = conf.outDir.string
  92. of nimcacheDir: result = conf.getNimcacheDir().string
  93. of projectName: result = conf.projectName
  94. of projectPath: result = conf.projectPath.string
  95. of projectFull: result = conf.projectFull.string
  96. of command: result = conf.command
  97. of commandLine: result = conf.commandLine
  98. of linkOptions: result = conf.linkOptions
  99. of compileOptions: result = conf.compileOptions
  100. of ccompilerPath: result = conf.cCompilerPath
  101. of backend: result = $conf.backend
  102. proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] =
  103. template copySeq(field: untyped): untyped =
  104. for i in field: result.add i.string
  105. case MultipleValueSetting(switch)
  106. of nimblePaths: copySeq(conf.nimblePaths)
  107. of searchPaths: copySeq(conf.searchPaths)
  108. of lazyPaths: copySeq(conf.lazyPaths)
  109. of commandArgs: result = conf.commandArgs
  110. of cincludes: copySeq(conf.cIncludes)
  111. of clibs: copySeq(conf.cLibs)
  112. proc registerAdditionalOps*(c: PCtx) =
  113. proc gorgeExWrapper(a: VmArgs) =
  114. let ret = opGorge(getString(a, 0), getString(a, 1), getString(a, 2),
  115. a.currentLineInfo, c.config)
  116. setResult a, ret.toLit
  117. proc getProjectPathWrapper(a: VmArgs) =
  118. setResult a, c.config.projectPath.string
  119. wrap1f_math(sqrt)
  120. wrap1f_math(ln)
  121. wrap1f_math(log10)
  122. wrap1f_math(log2)
  123. wrap1f_math(exp)
  124. wrap1f_math(round)
  125. wrap1f_math(arccos)
  126. wrap1f_math(arcsin)
  127. wrap1f_math(arctan)
  128. wrap2f_math(arctan2)
  129. wrap1f_math(cos)
  130. wrap1f_math(cosh)
  131. wrap2f_math(hypot)
  132. wrap1f_math(sinh)
  133. wrap1f_math(sin)
  134. wrap1f_math(tan)
  135. wrap1f_math(tanh)
  136. wrap2f_math(pow)
  137. wrap1f_math(trunc)
  138. wrap1f_math(floor)
  139. wrap1f_math(ceil)
  140. wrap1s(getMD5, md5op)
  141. proc `mod Wrapper`(a: VmArgs) {.nimcall.} =
  142. setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1)))
  143. registerCallback(c, "stdlib.math.mod", `mod Wrapper`)
  144. when defined(nimcore):
  145. wrap2s(getEnv, osop)
  146. wrap1s(existsEnv, osop)
  147. wrap2svoid(putEnv, osop)
  148. wrap1s(dirExists, osop)
  149. wrap1s(fileExists, osop)
  150. wrapDangerous(writeFile, ioop)
  151. wrap1s(readFile, ioop)
  152. wrap2si(readLines, ioop)
  153. systemop getCurrentExceptionMsg
  154. systemop getCurrentException
  155. registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
  156. setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
  157. when defined(nimHasInvariant):
  158. registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) =
  159. setResult(a, querySettingImpl(c.config, getInt(a, 0)))
  160. registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) =
  161. setResult(a, querySettingSeqImpl(c.config, getInt(a, 0)))
  162. if defined(nimsuggest) or c.config.cmd == cmdCheck:
  163. discard "don't run staticExec for 'nim suggest'"
  164. else:
  165. systemop gorgeEx
  166. macrosop getProjectPath
  167. registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} =
  168. setResult(a, getAppFilename())
  169. registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) =
  170. let n = getNode(a, 0)
  171. if n.kind != nkSym:
  172. stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr,
  173. "symBodyHash() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info)
  174. setResult(a, $symBodyDigest(c.graph, n.sym))
  175. registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) =
  176. let n = getNode(a, 0)
  177. if n.kind != nkSym:
  178. stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr,
  179. "isExported() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info)
  180. setResult(a, sfExported in n.sym.flags)
  181. proc hashVmImpl(a: VmArgs) =
  182. var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int)
  183. if c.config.backend == backendJs:
  184. # emulate JS's terrible integers:
  185. res = cast[int32](res)
  186. setResult(a, res)
  187. registerCallback c, "stdlib.hashes.hashVmImpl", hashVmImpl
  188. proc hashVmImplByte(a: VmArgs) =
  189. # nkBracket[...]
  190. let sPos = a.getInt(1).int
  191. let ePos = a.getInt(2).int
  192. let arr = a.getNode(0)
  193. var bytes = newSeq[byte](arr.len)
  194. for i in 0..<arr.len:
  195. bytes[i] = byte(arr[i].intVal and 0xff)
  196. var res = hashes.hash(bytes, sPos, ePos)
  197. if c.config.backend == backendJs:
  198. # emulate JS's terrible integers:
  199. res = cast[int32](res)
  200. setResult(a, res)
  201. registerCallback c, "stdlib.hashes.hashVmImplByte", hashVmImplByte
  202. registerCallback c, "stdlib.hashes.hashVmImplChar", hashVmImplByte
  203. if optBenchmarkVM in c.config.globalOptions or vmopsDanger in c.config.features:
  204. wrap0(cpuTime, timesop)
  205. else:
  206. proc cpuTime(): float = 5.391245e-44 # Randomly chosen
  207. wrap0(cpuTime, timesop)
  208. if vmopsDanger in c.config.features:
  209. ## useful procs but these should be opt-in because they may impact
  210. ## reproducible builds and users need to understand that this runs at CT.
  211. ## Note that `staticExec` can already do equal amount of damage so it's more
  212. ## of a semantic issue than a security issue.
  213. registerCallback c, "stdlib.os.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
  214. setResult(a, os.getCurrentDir())
  215. registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} =
  216. let options = getNode(a, 1).fromLit(set[osproc.ProcessOption])
  217. a.setResult osproc.execCmdEx(getString(a, 0), options).toLit
  218. registerCallback c, "stdlib.times.getTime", proc (a: VmArgs) {.nimcall.} =
  219. setResult(a, times.getTime().toLit)
  220. proc getEffectList(c: PCtx; a: VmArgs; effectIndex: int) =
  221. let fn = getNode(a, 0)
  222. if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].len >= effectListLen and
  223. fn.typ.n[0][effectIndex] != nil:
  224. var list = newNodeI(nkBracket, fn.info)
  225. for e in fn.typ.n[0][effectIndex]:
  226. list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info, c.idgen)
  227. setResult(a, list)
  228. registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) =
  229. getEffectList(c, a, exceptionEffects)
  230. registerCallback c, "stdlib.effecttraits.getTagsListImpl", proc (a: VmArgs) =
  231. getEffectList(c, a, tagEffects)
  232. registerCallback c, "stdlib.effecttraits.isGcSafeImpl", proc (a: VmArgs) =
  233. let fn = getNode(a, 0)
  234. setResult(a, fn.typ != nil and tfGcSafe in fn.typ.flags)
  235. registerCallback c, "stdlib.effecttraits.hasNoSideEffectsImpl", proc (a: VmArgs) =
  236. let fn = getNode(a, 0)
  237. setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or
  238. (fn.kind == nkSym and fn.sym.kind == skFunc))