vmops.nim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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 std/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`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma,
  14. lgamma
  15. from std/sequtils import toSeq
  16. when declared(math.copySign):
  17. # pending bug #18762, avoid renaming math
  18. from std/math as math2 import copySign
  19. when declared(math.signbit):
  20. # ditto
  21. from std/math as math3 import signbit
  22. from std/envvars import getEnv, existsEnv, delEnv, putEnv, envPairs
  23. from std/os import getAppFilename
  24. from std/private/oscommon import dirExists, fileExists
  25. from std/private/osdirs import walkDir, createDir
  26. from std/times import cpuTime
  27. from std/hashes import hash
  28. from std/osproc import nil
  29. when defined(nimPreviewSlimSystem):
  30. import std/syncio
  31. else:
  32. from std/formatfloat import addFloatRoundtrip, addFloatSprintf
  33. # There are some useful procs in vmconv.
  34. import vmconv, vmmarshal
  35. template mathop(op) {.dirty.} =
  36. registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
  37. template osop(op) {.dirty.} =
  38. registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`)
  39. template oscommonop(op) {.dirty.} =
  40. registerCallback(c, "stdlib.oscommon." & astToStr(op), `op Wrapper`)
  41. template osdirsop(op) {.dirty.} =
  42. registerCallback(c, "stdlib.osdirs." & astToStr(op), `op Wrapper`)
  43. template envvarsop(op) {.dirty.} =
  44. registerCallback(c, "stdlib.envvars." & astToStr(op), `op Wrapper`)
  45. template timesop(op) {.dirty.} =
  46. registerCallback(c, "stdlib.times." & astToStr(op), `op Wrapper`)
  47. template systemop(op) {.dirty.} =
  48. registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`)
  49. template ioop(op) {.dirty.} =
  50. registerCallback(c, "stdlib.syncio." & astToStr(op), `op Wrapper`)
  51. template macrosop(op) {.dirty.} =
  52. registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`)
  53. template wrap1fMath(op) {.dirty.} =
  54. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  55. doAssert a.numArgs == 1
  56. setResult(a, op(getFloat(a, 0)))
  57. mathop op
  58. template wrap2fMath(op) {.dirty.} =
  59. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  60. setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
  61. mathop op
  62. template wrap0(op, modop) {.dirty.} =
  63. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  64. setResult(a, op())
  65. modop op
  66. template wrap1s(op, modop) {.dirty.} =
  67. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  68. setResult(a, op(getString(a, 0)))
  69. modop op
  70. template wrap2s(op, modop) {.dirty.} =
  71. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  72. setResult(a, op(getString(a, 0), getString(a, 1)))
  73. modop op
  74. template wrap2si(op, modop) {.dirty.} =
  75. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  76. setResult(a, op(getString(a, 0), getInt(a, 1)))
  77. modop op
  78. template wrap1svoid(op, modop) {.dirty.} =
  79. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  80. op(getString(a, 0))
  81. modop op
  82. template wrap2svoid(op, modop) {.dirty.} =
  83. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  84. op(getString(a, 0), getString(a, 1))
  85. modop op
  86. template wrapDangerous1svoid(op, modop) {.dirty.} =
  87. if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
  88. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  89. discard
  90. modop op
  91. else:
  92. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  93. op(getString(a, 0))
  94. modop op
  95. template wrapDangerous2svoid(op, modop) {.dirty.} =
  96. if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
  97. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  98. discard
  99. modop op
  100. else:
  101. proc `op Wrapper`(a: VmArgs) {.nimcall.} =
  102. op(getString(a, 0), getString(a, 1))
  103. modop op
  104. proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
  105. setResult(a, if a.currentException.isNil: ""
  106. else: a.currentException[3].skipColon.strVal)
  107. proc getCurrentExceptionWrapper(a: VmArgs) {.nimcall.} =
  108. setResult(a, a.currentException)
  109. proc staticWalkDirImpl(path: string, relative: bool): PNode =
  110. result = newNode(nkBracket)
  111. for k, f in walkDir(path, relative):
  112. result.add toLit((k, f))
  113. when defined(nimHasInvariant):
  114. from std / compilesettings import SingleValueSetting, MultipleValueSetting
  115. proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string =
  116. case SingleValueSetting(switch)
  117. of arguments: result = conf.arguments
  118. of outFile: result = conf.outFile.string
  119. of outDir: result = conf.outDir.string
  120. of nimcacheDir: result = conf.getNimcacheDir().string
  121. of projectName: result = conf.projectName
  122. of projectPath: result = conf.projectPath.string
  123. of projectFull: result = conf.projectFull.string
  124. of command: result = conf.command
  125. of commandLine: result = conf.commandLine
  126. of linkOptions: result = conf.linkOptions
  127. of compileOptions: result = conf.compileOptions
  128. of ccompilerPath: result = conf.cCompilerPath
  129. of backend: result = $conf.backend
  130. of libPath: result = conf.libpath.string
  131. of gc: result = $conf.selectedGC
  132. of mm: result = $conf.selectedGC
  133. proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] =
  134. template copySeq(field: untyped): untyped =
  135. for i in field: result.add i.string
  136. case MultipleValueSetting(switch)
  137. of nimblePaths: copySeq(conf.nimblePaths)
  138. of searchPaths: copySeq(conf.searchPaths)
  139. of lazyPaths: copySeq(conf.lazyPaths)
  140. of commandArgs: result = conf.commandArgs
  141. of cincludes: copySeq(conf.cIncludes)
  142. of clibs: copySeq(conf.cLibs)
  143. proc stackTrace2(c: PCtx, msg: string, n: PNode) =
  144. stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info)
  145. proc registerAdditionalOps*(c: PCtx) =
  146. template wrapIterator(fqname: string, iter: untyped) =
  147. registerCallback c, fqname, proc(a: VmArgs) =
  148. setResult(a, toLit(toSeq(iter)))
  149. proc gorgeExWrapper(a: VmArgs) =
  150. let ret = opGorge(getString(a, 0), getString(a, 1), getString(a, 2),
  151. a.currentLineInfo, c.config)
  152. setResult a, ret.toLit
  153. proc getProjectPathWrapper(a: VmArgs) =
  154. setResult a, c.config.projectPath.string
  155. wrap1fMath(sqrt)
  156. wrap1fMath(cbrt)
  157. wrap1fMath(ln)
  158. wrap1fMath(log10)
  159. wrap1fMath(log2)
  160. wrap1fMath(exp)
  161. wrap1fMath(arccos)
  162. wrap1fMath(arcsin)
  163. wrap1fMath(arctan)
  164. wrap1fMath(arcsinh)
  165. wrap1fMath(arccosh)
  166. wrap1fMath(arctanh)
  167. wrap2fMath(arctan2)
  168. wrap1fMath(cos)
  169. wrap1fMath(cosh)
  170. wrap2fMath(hypot)
  171. wrap1fMath(sinh)
  172. wrap1fMath(sin)
  173. wrap1fMath(tan)
  174. wrap1fMath(tanh)
  175. wrap2fMath(pow)
  176. wrap1fMath(trunc)
  177. wrap1fMath(floor)
  178. wrap1fMath(ceil)
  179. wrap1fMath(erf)
  180. wrap1fMath(erfc)
  181. wrap1fMath(gamma)
  182. wrap1fMath(lgamma)
  183. when declared(copySign):
  184. wrap2fMath(copySign)
  185. when declared(signbit):
  186. wrap1fMath(signbit)
  187. registerCallback c, "stdlib.math.round", proc (a: VmArgs) {.nimcall.} =
  188. let n = a.numArgs
  189. case n
  190. of 1: setResult(a, round(getFloat(a, 0)))
  191. of 2: setResult(a, round(getFloat(a, 0), getInt(a, 1).int))
  192. else: doAssert false, $n
  193. proc `mod Wrapper`(a: VmArgs) {.nimcall.} =
  194. setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1)))
  195. registerCallback(c, "stdlib.math.mod", `mod Wrapper`)
  196. when defined(nimcore):
  197. wrap2s(getEnv, envvarsop)
  198. wrap1s(existsEnv, envvarsop)
  199. wrap2svoid(putEnv, envvarsop)
  200. wrap1svoid(delEnv, envvarsop)
  201. wrap1s(dirExists, oscommonop)
  202. wrap1s(fileExists, oscommonop)
  203. wrapDangerous2svoid(writeFile, ioop)
  204. wrapDangerous1svoid(createDir, osdirsop)
  205. wrap1s(readFile, ioop)
  206. wrap2si(readLines, ioop)
  207. systemop getCurrentExceptionMsg
  208. systemop getCurrentException
  209. registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
  210. setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
  211. when defined(nimHasInvariant):
  212. registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) =
  213. setResult(a, querySettingImpl(c.config, getInt(a, 0)))
  214. registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) =
  215. setResult(a, querySettingSeqImpl(c.config, getInt(a, 0)))
  216. if defined(nimsuggest) or c.config.cmd == cmdCheck:
  217. discard "don't run staticExec for 'nim suggest'"
  218. else:
  219. systemop gorgeEx
  220. macrosop getProjectPath
  221. registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} =
  222. setResult(a, getAppFilename())
  223. registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) =
  224. let n = getNode(a, 0)
  225. if n.kind != nkSym:
  226. stackTrace2(c, "symBodyHash() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n)
  227. setResult(a, $symBodyDigest(c.graph, n.sym))
  228. registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) =
  229. let n = getNode(a, 0)
  230. if n.kind != nkSym:
  231. stackTrace2(c, "isExported() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n)
  232. setResult(a, sfExported in n.sym.flags)
  233. registerCallback c, "stdlib.vmutils.vmTrace", proc (a: VmArgs) =
  234. c.config.isVmTrace = getBool(a, 0)
  235. proc hashVmImpl(a: VmArgs) =
  236. var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int)
  237. if c.config.backend == backendJs:
  238. # emulate JS's terrible integers:
  239. res = cast[int32](res)
  240. setResult(a, res)
  241. registerCallback c, "stdlib.hashes.hashVmImpl", hashVmImpl
  242. proc hashVmImplByte(a: VmArgs) =
  243. # nkBracket[...]
  244. let sPos = a.getInt(1).int
  245. let ePos = a.getInt(2).int
  246. let arr = a.getNode(0)
  247. var bytes = newSeq[byte](arr.len)
  248. for i in 0..<arr.len:
  249. bytes[i] = byte(arr[i].intVal and 0xff)
  250. var res = hashes.hash(bytes, sPos, ePos)
  251. if c.config.backend == backendJs:
  252. # emulate JS's terrible integers:
  253. res = cast[int32](res)
  254. setResult(a, res)
  255. registerCallback c, "stdlib.hashes.hashVmImplByte", hashVmImplByte
  256. registerCallback c, "stdlib.hashes.hashVmImplChar", hashVmImplByte
  257. if optBenchmarkVM in c.config.globalOptions or vmopsDanger in c.config.features:
  258. wrap0(cpuTime, timesop)
  259. else:
  260. proc cpuTime(): float = 5.391245e-44 # Randomly chosen
  261. wrap0(cpuTime, timesop)
  262. if vmopsDanger in c.config.features:
  263. ## useful procs but these should be opt-in because they may impact
  264. ## reproducible builds and users need to understand that this runs at CT.
  265. ## Note that `staticExec` can already do equal amount of damage so it's more
  266. ## of a semantic issue than a security issue.
  267. registerCallback c, "stdlib.os.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
  268. setResult(a, os.getCurrentDir())
  269. registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} =
  270. let options = getNode(a, 1).fromLit(set[osproc.ProcessOption])
  271. a.setResult osproc.execCmdEx(getString(a, 0), options).toLit
  272. registerCallback c, "stdlib.times.getTimeImpl", proc (a: VmArgs) =
  273. let obj = a.getNode(0).typ.n
  274. setResult(a, times.getTime().toTimeLit(c, obj, a.currentLineInfo))
  275. proc getEffectList(c: PCtx; a: VmArgs; effectIndex: int) =
  276. let fn = getNode(a, 0)
  277. var list = newNodeI(nkBracket, fn.info)
  278. if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].len >= effectListLen and
  279. fn.typ.n[0][effectIndex] != nil:
  280. for e in fn.typ.n[0][effectIndex]:
  281. list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info, c.idgen)
  282. else:
  283. list.add newIdentNode(getIdent(c.cache, "UncomputedEffects"), fn.info)
  284. setResult(a, list)
  285. registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) =
  286. getEffectList(c, a, exceptionEffects)
  287. registerCallback c, "stdlib.effecttraits.getTagsListImpl", proc (a: VmArgs) =
  288. getEffectList(c, a, tagEffects)
  289. registerCallback c, "stdlib.effecttraits.getForbidsListImpl", proc (a: VmArgs) =
  290. getEffectList(c, a, forbiddenEffects)
  291. registerCallback c, "stdlib.effecttraits.isGcSafeImpl", proc (a: VmArgs) =
  292. let fn = getNode(a, 0)
  293. setResult(a, fn.typ != nil and tfGcSafe in fn.typ.flags)
  294. registerCallback c, "stdlib.effecttraits.hasNoSideEffectsImpl", proc (a: VmArgs) =
  295. let fn = getNode(a, 0)
  296. setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or
  297. (fn.kind == nkSym and fn.sym.kind == skFunc))
  298. registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) =
  299. let fn = getNode(a, 0)
  300. setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure))
  301. registerCallback c, "stdlib.formatfloat.addFloatRoundtrip", proc(a: VmArgs) =
  302. let p = a.getVar(0)
  303. let x = a.getFloat(1)
  304. addFloatRoundtrip(p.strVal, x)
  305. registerCallback c, "stdlib.formatfloat.addFloatSprintf", proc(a: VmArgs) =
  306. let p = a.getVar(0)
  307. let x = a.getFloat(1)
  308. addFloatSprintf(p.strVal, x)
  309. wrapIterator("stdlib.envvars.envPairsImplSeq"): envPairs()
  310. registerCallback c, "stdlib.marshal.toVM", proc(a: VmArgs) =
  311. let typ = a.getNode(0).typ
  312. case typ.kind
  313. of tyInt..tyInt64, tyUInt..tyUInt64:
  314. setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).intVal)
  315. of tyFloat..tyFloat128:
  316. setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).floatVal)
  317. else:
  318. setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen))
  319. registerCallback c, "stdlib.marshal.loadVM", proc(a: VmArgs) =
  320. let typ = a.getNode(0).typ
  321. let p = a.getReg(1)
  322. var res: string
  323. storeAny(res, typ, regToNode(p[]), c.config)
  324. setResult(a, res)