diff options
Diffstat (limited to 'compiler/vmops.nim')
-rw-r--r-- | compiler/vmops.nim | 329 |
1 files changed, 268 insertions, 61 deletions
diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 1e3a258f3..45194e633 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -9,16 +9,39 @@ # Unfortunately this cannot be a module yet: #import vmdeps, vm -from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, +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` + 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 -from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename -from md5 import getMD5 -from sighashes import symBodyDigest -from times import cpuTime +when declared(math.signbit): + # ditto + from std/math as math3 import signbit -from hashes import hash + +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`) @@ -26,6 +49,15 @@ template mathop(op) {.dirty.} = 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`) @@ -33,24 +65,27 @@ template systemop(op) {.dirty.} = registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`) template ioop(op) {.dirty.} = - registerCallback(c, "stdlib.io." & astToStr(op), `op Wrapper`) + registerCallback(c, "stdlib.syncio." & astToStr(op), `op Wrapper`) template macrosop(op) {.dirty.} = registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`) -template md5op(op) {.dirty.} = - registerCallback(c, "stdlib.md5." & astToStr(op), `op Wrapper`) - -template wrap1f_math(op) {.dirty.} = +template wrap1fMath(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = + doAssert a.numArgs == 1 setResult(a, op(getFloat(a, 0))) mathop op -template wrap2f_math(op) {.dirty.} = +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()) @@ -81,13 +116,25 @@ template wrap2svoid(op, modop) {.dirty.} = op(getString(a, 0), getString(a, 1)) modop op -template wrapDangerous(op, modop) {.dirty.} = - proc `op Wrapper`(a: VmArgs) {.nimcall.} = - if defined(nimsuggest) or c.config.cmd == cmdCheck: +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 - else: + 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 + modop op proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = setResult(a, if a.currentException.isNil: "" @@ -99,59 +146,133 @@ proc getCurrentExceptionWrapper(a: VmArgs) {.nimcall.} = proc staticWalkDirImpl(path: string, relative: bool): PNode = result = newNode(nkBracket) for k, f in walkDir(path, relative): - result.add newTree(nkTupleConstr, newIntNode(nkIntLit, k.ord), - newStrNode(nkStrLit, f)) + 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 (s, e) = opGorge(getString(a, 0), getString(a, 1), getString(a, 2), + let ret = opGorge(getString(a, 0), getString(a, 1), getString(a, 2), a.currentLineInfo, c.config) - setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) + setResult a, ret.toLit proc getProjectPathWrapper(a: VmArgs) = setResult a, c.config.projectPath.string - wrap1f_math(sqrt) - wrap1f_math(ln) - wrap1f_math(log10) - wrap1f_math(log2) - wrap1f_math(exp) - wrap1f_math(round) - wrap1f_math(arccos) - wrap1f_math(arcsin) - wrap1f_math(arctan) - wrap2f_math(arctan2) - wrap1f_math(cos) - wrap1f_math(cosh) - wrap2f_math(hypot) - wrap1f_math(sinh) - wrap1f_math(sin) - wrap1f_math(tan) - wrap1f_math(tanh) - wrap2f_math(pow) - wrap1f_math(trunc) - wrap1f_math(floor) - wrap1f_math(ceil) - - wrap1s(getMD5, md5op) + 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, osop) - wrap1s(existsEnv, osop) - wrap2svoid(putEnv, osop) - wrap1s(dirExists, osop) - wrap1s(fileExists, osop) - wrapDangerous(writeFile, ioop) + 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.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = + 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'" @@ -162,23 +283,30 @@ proc registerAdditionalOps*(c: PCtx) = registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} = setResult(a, getAppFilename()) - registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) {.nimcall.} = + registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) = let n = getNode(a, 0) if n.kind != nkSym: - stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, - "symBodyHash() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info) + 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) {.nimcall.} = + registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) = let n = getNode(a, 0) if n.kind != nkSym: - stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, - "isExported() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info) + 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.cmd == cmdCompileToJS: + if c.config.backend == backendJs: # emulate JS's terrible integers: res = cast[int32](res) setResult(a, res) @@ -195,7 +323,7 @@ proc registerAdditionalOps*(c: PCtx) = bytes[i] = byte(arr[i].intVal and 0xff) var res = hashes.hash(bytes, sPos, ePos) - if c.config.cmd == cmdCompileToJS: + if c.config.backend == backendJs: # emulate JS's terrible integers: res = cast[int32](res) setResult(a, res) @@ -203,8 +331,87 @@ proc registerAdditionalOps*(c: PCtx) = registerCallback c, "stdlib.hashes.hashVmImplByte", hashVmImplByte registerCallback c, "stdlib.hashes.hashVmImplChar", hashVmImplByte - if optBenchmarkVM in c.config.globalOptions: + 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) |