vmops.nim 14 KB

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