vmops.nim 13 KB

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