vmops.nim 14 KB

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