summary refs log tree commit diff stats
path: root/compiler/vmops.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/vmops.nim')
-rw-r--r--compiler/vmops.nim417
1 files changed, 417 insertions, 0 deletions
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
new file mode 100644
index 000000000..45194e633
--- /dev/null
+++ b/compiler/vmops.nim
@@ -0,0 +1,417 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Unfortunately this cannot be a module yet:
+#import vmdeps, vm
+from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
+  arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
+  floor, ceil, `mod`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma,
+  lgamma, divmod
+from std/sequtils import toSeq
+when declared(math.copySign):
+  # pending bug #18762, avoid renaming math
+  from std/math as math2 import copySign
+
+when declared(math.signbit):
+  # ditto
+  from std/math as math3 import signbit
+
+
+from std/envvars import getEnv, existsEnv, delEnv, putEnv, envPairs
+from std/os import getAppFilename
+from std/private/oscommon import dirExists, fileExists
+from std/private/osdirs import walkDir, createDir
+from std/private/ospaths2 import getCurrentDir
+
+from std/times import cpuTime
+from std/hashes import hash
+from std/osproc import nil
+
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+else:
+  from std/formatfloat import addFloatRoundtrip, addFloatSprintf
+
+
+# There are some useful procs in vmconv.
+import vmconv, vmmarshal
+
+template mathop(op) {.dirty.} =
+  registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
+
+template osop(op) {.dirty.} =
+  registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`)
+
+template oscommonop(op) {.dirty.} =
+  registerCallback(c, "stdlib.oscommon." & astToStr(op), `op Wrapper`)
+
+template osdirsop(op) {.dirty.} =
+  registerCallback(c, "stdlib.osdirs." & astToStr(op), `op Wrapper`)
+
+template envvarsop(op) {.dirty.} =
+  registerCallback(c, "stdlib.envvars." & astToStr(op), `op Wrapper`)
+
+template timesop(op) {.dirty.} =
+  registerCallback(c, "stdlib.times." & astToStr(op), `op Wrapper`)
+
+template systemop(op) {.dirty.} =
+  registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`)
+
+template ioop(op) {.dirty.} =
+  registerCallback(c, "stdlib.syncio." & astToStr(op), `op Wrapper`)
+
+template macrosop(op) {.dirty.} =
+  registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`)
+
+template wrap1fMath(op) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    doAssert a.numArgs == 1
+    setResult(a, op(getFloat(a, 0)))
+  mathop op
+
+template wrap2fMath(op) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
+  mathop op
+
+template wrap2iMath(op) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getInt(a, 0), getInt(a, 1)))
+  mathop op
+
+template wrap0(op, modop) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op())
+  modop op
+
+template wrap1s(op, modop) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getString(a, 0)))
+  modop op
+
+template wrap2s(op, modop) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getString(a, 0), getString(a, 1)))
+  modop op
+
+template wrap2si(op, modop) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getString(a, 0), getInt(a, 1)))
+  modop op
+
+template wrap1svoid(op, modop) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    op(getString(a, 0))
+  modop op
+
+template wrap2svoid(op, modop) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    op(getString(a, 0), getString(a, 1))
+  modop op
+
+template wrapDangerous1svoid(op, modop) {.dirty.} =
+  if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      discard
+    modop op
+  else:
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      op(getString(a, 0))
+    modop op
+
+template wrapDangerous2svoid(op, modop) {.dirty.} =
+  if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      discard
+    modop op
+  else:
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      op(getString(a, 0), getString(a, 1))
+    modop op
+
+proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
+  setResult(a, if a.currentException.isNil: ""
+               else: a.currentException[3].skipColon.strVal)
+
+proc getCurrentExceptionWrapper(a: VmArgs) {.nimcall.} =
+  setResult(a, a.currentException)
+
+proc staticWalkDirImpl(path: string, relative: bool): PNode =
+  result = newNode(nkBracket)
+  for k, f in walkDir(path, relative):
+    result.add toLit((k, f))
+
+from std / compilesettings import SingleValueSetting, MultipleValueSetting
+
+proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string =
+  {.push warning[Deprecated]:off.}
+  case SingleValueSetting(switch)
+  of arguments: result = conf.arguments
+  of outFile: result = conf.outFile.string
+  of outDir: result = conf.outDir.string
+  of nimcacheDir: result = conf.getNimcacheDir().string
+  of projectName: result = conf.projectName
+  of projectPath: result = conf.projectPath.string
+  of projectFull: result = conf.projectFull.string
+  of command: result = conf.command
+  of commandLine: result = conf.commandLine
+  of linkOptions: result = conf.linkOptions
+  of compileOptions: result = conf.compileOptions
+  of ccompilerPath: result = conf.cCompilerPath
+  of backend: result = $conf.backend
+  of libPath: result = conf.libpath.string
+  of gc: result = $conf.selectedGC
+  of mm: result = $conf.selectedGC
+  {.pop.}
+
+proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] =
+  template copySeq(field: untyped): untyped =
+    result = @[]
+    for i in field: result.add i.string
+
+  case MultipleValueSetting(switch)
+  of nimblePaths: copySeq(conf.nimblePaths)
+  of searchPaths: copySeq(conf.searchPaths)
+  of lazyPaths: copySeq(conf.lazyPaths)
+  of commandArgs: result = conf.commandArgs
+  of cincludes: copySeq(conf.cIncludes)
+  of clibs: copySeq(conf.cLibs)
+
+proc stackTrace2(c: PCtx, msg: string, n: PNode) =
+  stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info)
+
+
+proc registerAdditionalOps*(c: PCtx) =
+
+  template wrapIterator(fqname: string, iter: untyped) =
+    registerCallback c, fqname, proc(a: VmArgs) =
+      setResult(a, toLit(toSeq(iter)))
+
+
+  proc gorgeExWrapper(a: VmArgs) =
+    let ret = opGorge(getString(a, 0), getString(a, 1), getString(a, 2),
+                         a.currentLineInfo, c.config)
+    setResult a, ret.toLit
+
+  proc getProjectPathWrapper(a: VmArgs) =
+    setResult a, c.config.projectPath.string
+
+  wrap1fMath(sqrt)
+  wrap1fMath(cbrt)
+  wrap1fMath(ln)
+  wrap1fMath(log10)
+  wrap1fMath(log2)
+  wrap1fMath(exp)
+  wrap1fMath(arccos)
+  wrap1fMath(arcsin)
+  wrap1fMath(arctan)
+  wrap1fMath(arcsinh)
+  wrap1fMath(arccosh)
+  wrap1fMath(arctanh)
+  wrap2fMath(arctan2)
+  wrap1fMath(cos)
+  wrap1fMath(cosh)
+  wrap2fMath(hypot)
+  wrap1fMath(sinh)
+  wrap1fMath(sin)
+  wrap1fMath(tan)
+  wrap1fMath(tanh)
+  wrap2fMath(pow)
+  wrap1fMath(trunc)
+  wrap1fMath(floor)
+  wrap1fMath(ceil)
+  wrap1fMath(erf)
+  wrap1fMath(erfc)
+  wrap1fMath(gamma)
+  wrap1fMath(lgamma)
+  wrap2iMath(divmod)
+
+  when declared(copySign):
+    wrap2fMath(copySign)
+
+  when declared(signbit):
+    wrap1fMath(signbit)
+
+  registerCallback c, "stdlib.math.round", proc (a: VmArgs) {.nimcall.} =
+    let n = a.numArgs
+    case n
+    of 1: setResult(a, round(getFloat(a, 0)))
+    of 2: setResult(a, round(getFloat(a, 0), getInt(a, 1).int))
+    else: raiseAssert $n
+
+  proc `mod Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1)))
+  registerCallback(c, "stdlib.math.mod", `mod Wrapper`)
+
+  when defined(nimcore):
+    wrap2s(getEnv, envvarsop)
+    wrap1s(existsEnv, envvarsop)
+    wrap2svoid(putEnv, envvarsop)
+    wrap1svoid(delEnv, envvarsop)
+    wrap1s(dirExists, oscommonop)
+    wrap1s(fileExists, oscommonop)
+    wrapDangerous2svoid(writeFile, ioop)
+    wrapDangerous1svoid(createDir, osdirsop)
+    wrap1s(readFile, ioop)
+    wrap2si(readLines, ioop)
+    systemop getCurrentExceptionMsg
+    systemop getCurrentException
+    registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
+    registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, dirExists(getString(a, 0)))
+    registerCallback c, "stdlib.staticos.staticFileExists", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, fileExists(getString(a, 0)))
+    registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) =
+      setResult(a, querySettingImpl(c.config, getInt(a, 0)))
+    registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) =
+      setResult(a, querySettingSeqImpl(c.config, getInt(a, 0)))
+
+    if defined(nimsuggest) or c.config.cmd == cmdCheck:
+      discard "don't run staticExec for 'nim suggest'"
+    else:
+      systemop gorgeEx
+  macrosop getProjectPath
+
+  registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} =
+    setResult(a, getAppFilename())
+
+  registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) =
+    let n = getNode(a, 0)
+    if n.kind != nkSym:
+      stackTrace2(c, "symBodyHash() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n)
+    setResult(a, $symBodyDigest(c.graph, n.sym))
+
+  registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) =
+    let n = getNode(a, 0)
+    if n.kind != nkSym:
+      stackTrace2(c, "isExported() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n)
+    setResult(a, sfExported in n.sym.flags)
+
+  registerCallback c, "stdlib.macrocache.hasKey", proc (a: VmArgs) =
+    let
+      table = getString(a, 0)
+      key = getString(a, 1)
+    setResult(a, table in c.graph.cacheTables and key in c.graph.cacheTables[table])
+
+  registerCallback c, "stdlib.vmutils.vmTrace", proc (a: VmArgs) =
+    c.config.isVmTrace = getBool(a, 0)
+
+  proc hashVmImpl(a: VmArgs) =
+    var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int)
+    if c.config.backend == backendJs:
+      # emulate JS's terrible integers:
+      res = cast[int32](res)
+    setResult(a, res)
+
+  registerCallback c, "stdlib.hashes.hashVmImpl", hashVmImpl
+
+  proc hashVmImplByte(a: VmArgs) =
+    # nkBracket[...]
+    let sPos = a.getInt(1).int
+    let ePos = a.getInt(2).int
+    let arr = a.getNode(0)
+    var bytes = newSeq[byte](arr.len)
+    for i in 0..<arr.len:
+      bytes[i] = byte(arr[i].intVal and 0xff)
+
+    var res = hashes.hash(bytes, sPos, ePos)
+    if c.config.backend == backendJs:
+      # emulate JS's terrible integers:
+      res = cast[int32](res)
+    setResult(a, res)
+
+  registerCallback c, "stdlib.hashes.hashVmImplByte", hashVmImplByte
+  registerCallback c, "stdlib.hashes.hashVmImplChar", hashVmImplByte
+
+  if optBenchmarkVM in c.config.globalOptions or vmopsDanger in c.config.features:
+    wrap0(cpuTime, timesop)
+  else:
+    proc cpuTime(): float = 5.391245e-44  # Randomly chosen
+    wrap0(cpuTime, timesop)
+
+  if vmopsDanger in c.config.features:
+    ## useful procs but these should be opt-in because they may impact
+    ## reproducible builds and users need to understand that this runs at CT.
+    ## Note that `staticExec` can already do equal amount of damage so it's more
+    ## of a semantic issue than a security issue.
+    registerCallback c, "stdlib.ospaths2.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, getCurrentDir())
+    registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} =
+      let options = getNode(a, 1).fromLit(set[osproc.ProcessOption])
+      a.setResult osproc.execCmdEx(getString(a, 0), options).toLit
+    registerCallback c, "stdlib.times.getTimeImpl", proc (a: VmArgs) =
+      let obj = a.getNode(0).typ.n
+      setResult(a, times.getTime().toTimeLit(c, obj, a.currentLineInfo))
+
+  proc getEffectList(c: PCtx; a: VmArgs; effectIndex: int) =
+    let fn = getNode(a, 0)
+    var list = newNodeI(nkBracket, fn.info)
+    if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].len >= effectListLen and
+        fn.typ.n[0][effectIndex] != nil:
+      for e in fn.typ.n[0][effectIndex]:
+        list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info, c.idgen)
+    else:
+      list.add newIdentNode(getIdent(c.cache, "UncomputedEffects"), fn.info)
+
+    setResult(a, list)
+
+  registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) =
+    getEffectList(c, a, exceptionEffects)
+  registerCallback c, "stdlib.effecttraits.getTagsListImpl", proc (a: VmArgs) =
+    getEffectList(c, a, tagEffects)
+  registerCallback c, "stdlib.effecttraits.getForbidsListImpl", proc (a: VmArgs) =
+    getEffectList(c, a, forbiddenEffects)
+
+  registerCallback c, "stdlib.effecttraits.isGcSafeImpl", proc (a: VmArgs) =
+    let fn = getNode(a, 0)
+    setResult(a, fn.typ != nil and tfGcSafe in fn.typ.flags)
+
+  registerCallback c, "stdlib.effecttraits.hasNoSideEffectsImpl", proc (a: VmArgs) =
+    let fn = getNode(a, 0)
+    setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or
+                 (fn.kind == nkSym and fn.sym.kind == skFunc))
+
+  registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) =
+    let fn = getNode(a, 0)
+    setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure))
+
+  registerCallback c, "stdlib.formatfloat.addFloatRoundtrip", proc(a: VmArgs) =
+    let p = a.getVar(0)
+    let x = a.getFloat(1)
+    addFloatRoundtrip(p.strVal, x)
+
+  registerCallback c, "stdlib.formatfloat.addFloatSprintf", proc(a: VmArgs) =
+    let p = a.getVar(0)
+    let x = a.getFloat(1)
+    addFloatSprintf(p.strVal, x)
+
+  registerCallback c, "stdlib.strutils.formatBiggestFloat", proc(a: VmArgs) =
+    setResult(a, formatBiggestFloat(a.getFloat(0), FloatFormatMode(a.getInt(1)),
+                                    a.getInt(2), chr(a.getInt(3))))
+
+  wrapIterator("stdlib.envvars.envPairsImplSeq"): envPairs()
+
+  registerCallback c, "stdlib.marshal.toVM", proc(a: VmArgs) =
+    let typ = a.getNode(0).typ
+    case typ.kind
+    of tyInt..tyInt64, tyUInt..tyUInt64:
+      setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).intVal)
+    of tyFloat..tyFloat128:
+      setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).floatVal)
+    else:
+      setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen))
+
+  registerCallback c, "stdlib.marshal.loadVM", proc(a: VmArgs) =
+    let typ = a.getNode(0).typ
+    let p = a.getReg(1)
+    var res: string = ""
+    storeAny(res, typ, regToNode(p[]), c.config)
+    setResult(a, res)