vmops.nim 14 KB

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