about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/eval.cc105
-rw-r--r--src/libexpr/eval.hh16
2 files changed, 78 insertions, 43 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 2d22a4fdc5c4..56a73b49405a 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -93,7 +93,8 @@ string showType(Value & v)
 
 EvalState::EvalState() : baseEnv(allocEnv())
 {
-    nrValues = nrEnvs = nrEvaluated = 0;
+    nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0;
+    deepestStack = (char *) -1;
 
     initNixExprHelpers();
 
@@ -103,6 +104,12 @@ EvalState::EvalState() : baseEnv(allocEnv())
 }
 
 
+EvalState::~EvalState()
+{
+    assert(recursionDepth == 0);
+}
+
+
 void EvalState::addConstant(const string & name, Value & v)
 {
     baseEnv.bindings[toATerm(name)] = v;
@@ -141,6 +148,11 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
     throw EvalError(format(s) % s2);
 }
 
+LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
+{
+    throw EvalError(format(s) % s2 % s3);
+}
+
 LocalNoInlineNoReturn(void throwTypeError(const char * s))
 {
     throw TypeError(s);
@@ -151,6 +163,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
     throw TypeError(format(s) % s2);
 }
 
+LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s2))
+{
+    throw TypeError(format(s) % s2);
+}
+
 LocalNoInline(void addErrorPrefix(Error & e, const char * s))
 {
     e.addPrefix(s);
@@ -234,7 +251,7 @@ static Value * lookupVar(Env * env, Sym name)
     }
 #endif
     
-    throw Error(format("undefined variable `%1%'") % aterm2String(name));
+    throwEvalError("undefined variable `%1%'", aterm2String(name));
 }
 
 
@@ -295,21 +312,35 @@ void EvalState::evalFile(const Path & path, Value & v)
     try {
         eval(e, v);
     } catch (Error & e) {
-        e.addPrefix(format("while evaluating the file `%1%':\n")
-            % path);
+        addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
         throw;
     }
 }
 
 
-static char * deepestStack = (char *) -1; /* for measuring stack usage */
+struct RecursionCounter
+{
+    EvalState & state;
+    RecursionCounter(EvalState & state) : state(state)
+    {
+        state.recursionDepth++;
+        if (state.recursionDepth > state.maxRecursionDepth)
+            state.maxRecursionDepth = state.recursionDepth;
+    }
+    ~RecursionCounter()
+    {   
+        state.recursionDepth--;
+    }
+};
 
 
 void EvalState::eval(Env & env, Expr e, Value & v)
 {
     /* When changing this function, make sure that you don't cause a
        (large) increase in stack consumption! */
-    
+
+    /* !!! Disable this eventually. */
+    RecursionCounter r(*this);
     char x;
     if (&x < deepestStack) deepestStack = &x;
     
@@ -482,8 +513,7 @@ void EvalState::eval(Env & env, Expr e, Value & v)
         }
         
         if (isPath && !context.empty())
-            throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'")
-                % s.str());
+            throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
 
         if (isPath)
             mkPath(v, s.str().c_str());
@@ -498,7 +528,7 @@ void EvalState::eval(Env & env, Expr e, Value & v)
     /* Assertions. */
     else if (matchAssert(e, e1, e2, pos)) {
         if (!evalBool(env, e1))
-            throw AssertionError(format("assertion failed at %1%") % showPos(pos));
+            throwAssertionError("assertion failed at %1%", showPos(pos));
         eval(env, e2, v);
     }
 
@@ -538,7 +568,7 @@ void EvalState::eval(Env & env, Expr e, Value & v)
         mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
     }
 
-    else throw Error("unsupported term");
+    else abort();
 }
 
 
@@ -615,8 +645,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
                 
             if (j == arg.attrs->end()) {
                 if (!matchDefaultValue(def2, def)) def = 0;
-                if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")    
-                    % aterm2String(name));
+                if (def == 0) throwTypeError("the argument named `%1%' required by the function is missing",
+                    aterm2String(name));
                 mkThunk(v, env2, def);
             } else {
                 attrsUsed++;
@@ -629,7 +659,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
            TODO: show the names of the expected/unexpected
            arguments. */
         if (ellipsis == eFalse && attrsUsed != arg.attrs->size())
-            throw TypeError("function called with unexpected argument");
+            throwTypeError("function called with unexpected argument");
     }
 
     else abort();
@@ -661,8 +691,7 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
         if (j != args.end())
             (*actualArgs.attrs)[name] = j->second;
         else if (!matchDefaultValue(def2, def))
-            throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%      ')")
-                % aterm2String(name));
+            throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", aterm2String(name));
     }
 
     callFunction(fun, actualArgs, res);
@@ -680,7 +709,7 @@ bool EvalState::evalBool(Env & env, Expr e)
     Value v;
     eval(env, e, v);
     if (v.type != tBool)
-        throw TypeError(format("value is %1% while a Boolean was expected") % showType(v));
+        throwTypeError("value is %1% while a Boolean was expected", showType(v));
     return v.boolean;
 }
 
@@ -704,7 +733,7 @@ void EvalState::forceValue(Value & v)
     else if (v.type == tApp)
         callFunction(*v.app.left, *v.app.right, v);
     else if (v.type == tBlackhole)
-        throw EvalError("infinite recursion encountered");
+        throwEvalError("infinite recursion encountered");
 }
 
 
@@ -728,7 +757,7 @@ int EvalState::forceInt(Value & v)
 {
     forceValue(v);
     if (v.type != tInt)
-        throw TypeError(format("value is %1% while an integer was expected") % showType(v));
+        throwTypeError("value is %1% while an integer was expected", showType(v));
     return v.integer;
 }
 
@@ -737,7 +766,7 @@ bool EvalState::forceBool(Value & v)
 {
     forceValue(v);
     if (v.type != tBool)
-        throw TypeError(format("value is %1% while a Boolean was expected") % showType(v));
+        throwTypeError("value is %1% while a Boolean was expected", showType(v));
     return v.boolean;
 }
 
@@ -746,7 +775,7 @@ void EvalState::forceAttrs(Value & v)
 {
     forceValue(v);
     if (v.type != tAttrs)
-        throw TypeError(format("value is %1% while an attribute set was expected") % showType(v));
+        throwTypeError("value is %1% while an attribute set was expected", showType(v));
 }
 
 
@@ -754,7 +783,7 @@ void EvalState::forceList(Value & v)
 {
     forceValue(v);
     if (v.type != tList)
-        throw TypeError(format("value is %1% while a list was expected") % showType(v));
+        throwTypeError("value is %1% while a list was expected", showType(v));
 }
 
 
@@ -762,7 +791,7 @@ void EvalState::forceFunction(Value & v)
 {
     forceValue(v);
     if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp)
-        throw TypeError(format("value is %1% while a function was expected") % showType(v));
+        throwTypeError("value is %1% while a function was expected", showType(v));
 }
 
 
@@ -770,7 +799,7 @@ string EvalState::forceString(Value & v)
 {
     forceValue(v);
     if (v.type != tString)
-        throw TypeError(format("value is %1% while a string was expected") % showType(v));
+        throwTypeError("value is %1% while a string was expected", showType(v));
     return string(v.string.s);
 }
 
@@ -778,10 +807,9 @@ string EvalState::forceString(Value & v)
 string EvalState::forceString(Value & v, PathSet & context)
 {
     string s = forceString(v);
-    if (v.string.context) {
+    if (v.string.context)
         for (const char * * p = v.string.context; *p; ++p) 
             context.insert(*p);
-    }
     return s;
 }
 
@@ -790,8 +818,8 @@ string EvalState::forceStringNoCtx(Value & v)
 {
     string s = forceString(v);
     if (v.string.context)
-        throw EvalError(format("the string `%1%' is not allowed to refer to a store path (such as `%2%')")
-            % v.string.s % v.string.context[0]);
+        throwEvalError("the string `%1%' is not allowed to refer to a store path (such as `%2%')",
+            v.string.s, v.string.context[0]);
     return s;
 }
 
@@ -824,8 +852,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
         if (!copyToStore) return path;
         
         if (nix::isDerivation(path))
-            throw EvalError(format("file names are not allowed to end in `%1%'")
-                % drvExtension);
+            throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
 
         Path dstPath;
         if (srcToStore[path] != "")
@@ -881,7 +908,7 @@ Path EvalState::coerceToPath(Value & v, PathSet & context)
 {
     string path = coerceToString(v, context, false, false);
     if (path == "" || path[0] != '/')
-        throw EvalError(format("string `%1%' doesn't represent an absolute path") % path);
+        throwEvalError("string `%1%' doesn't represent an absolute path", path);
     return path;
 }
 
@@ -932,7 +959,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
             return false;
 
         default:
-            throw Error(format("cannot compare %1% with %2%") % showType(v1) % showType(v2));
+            throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
     }
 }
 
@@ -941,14 +968,14 @@ void EvalState::printStats()
 {
     char x;
     bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
-    printMsg(showStats ? lvlInfo : lvlDebug,
-        format("evaluated %1% expressions, used %2% bytes of stack space, allocated %3% values, allocated %4% environments")
-        % nrEvaluated
-        % (&x - deepestStack)
-        % nrValues
-        % nrEnvs);
-    if (showStats)
-        printATermMapStats();
+    Verbosity v = showStats ? lvlInfo : lvlDebug;
+    printMsg(v, "evaluation statistics:");
+    printMsg(v, format("  expressions evaluated: %1%") % nrEvaluated);
+    printMsg(v, format("  stack space used: %1% bytes") % (&x - deepestStack));
+    printMsg(v, format("  max eval() nesting depth: %1%") % maxRecursionDepth);
+    printMsg(v, format("  stack space per eval() level: %1% bytes") % ((&x - deepestStack) / (float) maxRecursionDepth));
+    printMsg(v, format("  values allocated: %1%") % nrValues);
+    printMsg(v, format("  environments allocated: %1%") % nrEnvs);
 }
 
 
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 0ea474447f1f..fb4dd802e0b0 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -144,10 +144,6 @@ public:
 private:
     SrcToStore srcToStore; 
 
-    unsigned long nrValues;
-    unsigned long nrEnvs;
-    unsigned long nrEvaluated;
-
     bool allowUnsafeEquality;
 
     ATermMap parseTrees;
@@ -155,6 +151,7 @@ private:
 public:
     
     EvalState();
+    ~EvalState();
 
     /* Evaluate an expression read from the given file to normal
        form. */
@@ -242,6 +239,17 @@ public:
 
     /* Print statistics. */
     void printStats();
+
+private:
+    
+    unsigned long nrValues;
+    unsigned long nrEnvs;
+    unsigned long nrEvaluated;
+    unsigned int recursionDepth;
+    unsigned int maxRecursionDepth;
+    char * deepestStack; /* for measuring stack usage */
+    
+    friend class RecursionCounter;
 };