summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2020-04-20 03:00:00 -0700
committerGitHub <noreply@github.com>2020-04-20 12:00:00 +0200
commitb2720317fa3b31aed298398696124b439fcfa907 (patch)
tree75808f47b701c00355ca187971890aaadc88a0b2
parent77834f0fdabc9d39cf111c4c94e717bd2c4f00d2 (diff)
downloadNim-b2720317fa3b31aed298398696124b439fcfa907.tar.gz
add `--experimental:vmopsDanger`; add generic conversion for vmops (#13813)
* add --experimental:vmopsDanger; vmops cleanups
-rw-r--r--compiler/options.nim1
-rw-r--r--compiler/vmconv.nim41
-rw-r--r--compiler/vmops.nim33
3 files changed, 66 insertions, 9 deletions
diff --git a/compiler/options.nim b/compiler/options.nim
index 813d2d6e5..b9aca23a1 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -147,6 +147,7 @@ type
       ## This requires building nim with `-d:nimHasLibFFI`
       ## which itself requires `nimble install libffi`, see #10150
       ## Note: this feature can't be localized with {.push.}
+    vmopsDanger,
 
   LegacyFeature* = enum
     allowSemcheckedAstModification,
diff --git a/compiler/vmconv.nim b/compiler/vmconv.nim
new file mode 100644
index 000000000..7db3906c2
--- /dev/null
+++ b/compiler/vmconv.nim
@@ -0,0 +1,41 @@
+import ast
+
+template elementType*(T: typedesc): typedesc =
+  typeof(block:
+    var a: T
+    for ai in a: ai)
+
+proc fromLit*(a: PNode, T: typedesc): auto =
+  ## generic PNode => type
+  ## see also reverse operation `toLit`
+  when T is set:
+    result = default(T)
+    type Ti = elementType(T)
+    for ai in a:
+      result.incl Ti(ai.intVal)
+  else:
+    static: doAssert false, "not yet supported: " & $T # add as needed
+
+proc toLit*[T](a: T): PNode =
+  ## generic type => PNode
+  ## see also reverse operation `fromLit`
+  when T is string: newStrNode(nkStrLit, a)
+  elif T is Ordinal: newIntNode(nkIntLit, a.ord)
+  elif T is (proc): newNode(nkNilLit)
+  elif T is ref:
+    if a == nil: newNode(nkNilLit)
+    else: toLit(a[])
+  elif T is tuple:
+    result = newTree(nkTupleConstr)
+    for ai in fields(a): result.add toLit(ai)
+  elif T is object:
+    result = newTree(nkObjConstr)
+    result.add(newNode(nkEmpty))
+    for k, ai in fieldPairs(a):
+      let reti = newNode(nkExprColonExpr)
+      reti.add k.toLit
+      reti.add ai.toLit
+      result.add reti
+  else:
+    static: doAssert false, "not yet supported: " & $T # add as needed
+
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 0ee137178..ef36419a7 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -19,6 +19,9 @@ from sighashes import symBodyDigest
 from times import cpuTime
 
 from hashes import hash
+from osproc import nil
+
+import vmconv
 
 template mathop(op) {.dirty.} =
   registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
@@ -99,13 +102,12 @@ 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))
 
 when defined(nimHasInvariant):
   from std / compilesettings import SingleValueSetting, MultipleValueSetting
 
-  proc querySettingImpl(a: VmArgs, conf: ConfigRef, switch: BiggestInt): string =
+  proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string =
     case SingleValueSetting(switch)
     of arguments: result = conf.arguments
     of outFile: result = conf.outFile.string
@@ -120,7 +122,7 @@ when defined(nimHasInvariant):
     of compileOptions: result = conf.compileOptions
     of ccompilerPath: result = conf.cCompilerPath
 
-  proc querySettingSeqImpl(a: VmArgs, conf: ConfigRef, switch: BiggestInt): seq[string] =
+  proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] =
     template copySeq(field: untyped): untyped =
       for i in field: result.add i.string
 
@@ -134,9 +136,9 @@ when defined(nimHasInvariant):
 
 proc registerAdditionalOps*(c: PCtx) =
   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
@@ -184,9 +186,9 @@ proc registerAdditionalOps*(c: PCtx) =
       setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
     when defined(nimHasInvariant):
       registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) {.nimcall.} =
-        setResult(a, querySettingImpl(a, c.config, getInt(a, 0)))
+        setResult(a, querySettingImpl(c.config, getInt(a, 0)))
       registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) {.nimcall.} =
-        setResult(a, querySettingSeqImpl(a, c.config, getInt(a, 0)))
+        setResult(a, querySettingSeqImpl(c.config, getInt(a, 0)))
 
     if defined(nimsuggest) or c.config.cmd == cmdCheck:
       discard "don't run staticExec for 'nim suggest'"
@@ -238,8 +240,21 @@ 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.os.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, os.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.getTime", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, times.getTime().toLit)