LTP GCOV extension - code coverage report
Current view: directory - src/libexpr - eval.cc
Test: app.info
Date: 2008-11-20 Instrumented lines: 378
Code covered: 87.6 % Executed lines: 331

       1                 : #include "eval.hh"
       2                 : #include "parser.hh"
       3                 : #include "hash.hh"
       4                 : #include "util.hh"
       5                 : #include "store-api.hh"
       6                 : #include "derivations.hh"
       7                 : #include "nixexpr-ast.hh"
       8                 : #include "globals.hh"
       9                 : 
      10                 : 
      11                 : #define LocalNoInline(f) static f __attribute__((noinline)); f
      12                 : #define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
      13                 : 
      14                 : 
      15                 : namespace nix {
      16                 :     
      17                 : 
      18             236 : EvalState::EvalState()
      19             236 :     : normalForms(32768), primOps(128)
      20                 : {
      21             236 :     nrEvaluated = nrCached = 0;
      22                 : 
      23             236 :     initNixExprHelpers();
      24                 : 
      25             236 :     addPrimOps();
      26             236 : }
      27                 : 
      28                 : 
      29                 : void EvalState::addPrimOp(const string & name,
      30           11564 :     unsigned int arity, PrimOp primOp)
      31                 : {
      32           11564 :     primOps.set(toATerm(name), makePrimOpDef(arity, ATmakeBlob(0, (void *) primOp)));
      33           11564 : }
      34                 : 
      35                 : 
      36                 : /* Every "format" object (even temporary) takes up a few hundred bytes
      37                 :    of stack space, which is a real killer in the recursive
      38                 :    evaluator.  So here are some helper functions for throwing
      39                 :    exceptions. */
      40                 : 
      41               2 : LocalNoInlineNoReturn(void throwEvalError(const char * s))
      42                 : {
      43               2 :     throw EvalError(s);
      44                 : }
      45                 : 
      46               1 : LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
      47                 : {
      48               1 :     throw EvalError(format(s) % s2);
      49                 : }
      50                 : 
      51               0 : LocalNoInlineNoReturn(void throwTypeError(const char * s))
      52                 : {
      53               0 :     throw TypeError(s);
      54                 : }
      55                 : 
      56               0 : LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
      57                 : {
      58               0 :     throw TypeError(format(s) % s2);
      59                 : }
      60                 : 
      61               0 : LocalNoInline(void addErrorPrefix(Error & e, const char * s))
      62                 : {
      63               0 :     e.addPrefix(s);
      64               0 : }
      65                 : 
      66               4 : LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
      67                 : {
      68               4 :     e.addPrefix(format(s) % s2);
      69               4 : }
      70                 : 
      71               8 : LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const string & s3))
      72                 : {
      73               8 :     e.addPrefix(format(s) % s2 % s3);
      74               8 : }
      75                 : 
      76                 : 
      77                 : /* Pattern-match `pat' against `arg'.  The result is a set of
      78                 :    substitutions (`subs') and a set of recursive substitutions
      79                 :    (`subsRecursive').  The latter can refer to the variables bound by
      80                 :    both `subs' and `subsRecursive'. */
      81                 : static void patternMatch(EvalState & state,
      82            2125 :     Pattern pat, Expr arg, ATermMap & subs, ATermMap & subsRecursive)
      83                 : {
      84                 :     ATerm name;
      85                 :     ATermList formals;
      86                 :     Pattern pat1, pat2;
      87                 :     ATermBool ellipsis;
      88                 :     
      89            2125 :     if (matchVarPat(pat, name)) 
      90            1972 :         subs.set(name, arg);
      91                 : 
      92             153 :     else if (matchAttrsPat(pat, formals, ellipsis)) {
      93                 : 
      94             147 :         arg = evalExpr(state, arg);
      95                 : 
      96                 :         /* Get the actual arguments. */
      97             147 :         ATermMap attrs;
      98             147 :         queryAllAttrs(arg, attrs);
      99             147 :         unsigned int nrAttrs = attrs.size();
     100                 : 
     101                 :         /* For each formal argument, get the actual argument.  If
     102                 :            there is no matching actual argument but the formal
     103                 :            argument has a default, use the default. */
     104             147 :         unsigned int attrsUsed = 0;
     105             498 :         for (ATermIterator i(formals); i; ++i) {
     106                 :             Expr name, def;
     107                 :             DefaultValue def2;
     108             352 :             if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
     109                 : 
     110             352 :             Expr value = attrs[name];
     111                 : 
     112             352 :             if (value == 0) {
     113              75 :                 if (!matchDefaultValue(def2, def)) def = 0;
     114              75 :                 if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
     115               1 :                     % aterm2String(name));
     116              74 :                 subsRecursive.set(name, def);
     117                 :             } else {
     118             277 :                 attrsUsed++;
     119             277 :                 attrs.remove(name);
     120             277 :                 subs.set(name, value);
     121                 :             }
     122                 : 
     123                 :         }
     124                 : 
     125                 :         /* Check that each actual argument is listed as a formal
     126                 :            argument (unless the attribute match specifies a `...'). */
     127             146 :         if (ellipsis == eFalse && attrsUsed != nrAttrs)
     128                 :             throw TypeError(format("the function does not expect an argument named `%1%'")
     129               1 :                 % aterm2String(attrs.begin()->key));
     130                 :     }
     131                 : 
     132               6 :     else if (matchAtPat(pat, pat1, pat2)) {
     133               6 :         patternMatch(state, pat1, arg, subs, subsRecursive);
     134               6 :         patternMatch(state, pat2, arg, subs, subsRecursive);
     135                 :     }
     136                 : 
     137               0 :     else abort();
     138            2123 : }
     139                 : 
     140                 : 
     141                 : /* Substitute an argument set into the body of a function. */
     142                 : static Expr substArgs(EvalState & state,
     143            2113 :     Expr body, Pattern pat, Expr arg)
     144                 : {
     145            2113 :     ATermMap subs(16), subsRecursive(16);
     146                 :     
     147            2113 :     patternMatch(state, pat, arg, subs, subsRecursive);
     148                 : 
     149                 :     /* If we used any default values, make a recursive attribute set
     150                 :        out of the (argument-name, value) tuples.  This is so that we
     151                 :        can support default values that refer to each other, e.g.  ({x,
     152                 :        y ? x + x}: y) {x = "foo";} evaluates to "foofoo". */
     153            2111 :     if (subsRecursive.size() != 0) {
     154              44 :         ATermList recAttrs = ATempty;
     155              62 :         foreach (ATermMap::const_iterator, i, subs)
     156              18 :             recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
     157             118 :         foreach (ATermMap::const_iterator, i, subsRecursive)
     158              74 :             recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
     159              44 :         Expr rec = makeRec(recAttrs, ATempty);
     160             118 :         foreach (ATermMap::const_iterator, i, subsRecursive)
     161              74 :             subs.set(i->key, makeSelect(rec, i->key));
     162                 :     }
     163                 : 
     164            2111 :     return substitute(Substitution(0, &subs), body);
     165                 : }
     166                 : 
     167                 : 
     168                 : /* Transform a mutually recursive set into a non-recursive set.  Each
     169                 :    attribute is transformed into an expression that has all references
     170                 :    to attributes substituted with selection expressions on the
     171                 :    original set.  E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f
     172                 :    (e.x) (e.y); y = e.x;}'. */
     173             324 : LocalNoInline(ATerm expandRec(EvalState & state, ATerm e, ATermList rbnds, ATermList nrbnds))
     174                 : {
     175                 :     ATerm name;
     176                 :     Expr e2;
     177                 :     Pos pos;
     178             324 :     Expr eOverrides = 0;
     179                 : 
     180                 :     /* Create the substitution list. */
     181             324 :     ATermMap subs(ATgetLength(rbnds) + ATgetLength(nrbnds));
     182            1337 :     for (ATermIterator i(rbnds); i; ++i) {
     183            1013 :         if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
     184            1013 :         subs.set(name, makeSelect(e, name));
     185                 :     }
     186             325 :     for (ATermIterator i(nrbnds); i; ++i) {
     187               1 :         if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
     188               1 :         if (name == sOverrides) eOverrides = e2;
     189               1 :         subs.set(name, e2);
     190                 :     }
     191                 : 
     192                 :     /* If the rec contains an attribute called `__overrides', then
     193                 :        evaluate it, and add the attributes in that set to the rec.
     194                 :        This allows overriding of recursive attributes, which is
     195                 :        otherwise not possible.  (You can use the // operator to
     196                 :        replace an attribute, but other attributes in the rec will
     197                 :        still reference the original value, because that value has been
     198                 :        substituted into the bodies of the other attributes.  Hence we
     199                 :        need __overrides.) */
     200             324 :     ATermMap overrides;
     201             324 :     if (eOverrides) {
     202               0 :         eOverrides = evalExpr(state, eOverrides);
     203               0 :         queryAllAttrs(eOverrides, overrides, false);
     204               0 :         foreach (ATermMap::const_iterator, i, overrides)
     205               0 :             subs.set(i->key, i->value);
     206                 :     }
     207                 : 
     208             324 :     Substitution subs_(0, &subs);
     209                 : 
     210                 :     /* Create the non-recursive set. */
     211             324 :     ATermMap as(ATgetLength(rbnds) + ATgetLength(nrbnds));
     212            1337 :     for (ATermIterator i(rbnds); i; ++i) {
     213            1013 :         if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
     214            1013 :         as.set(name, makeAttrRHS(substitute(subs_, e2), pos));
     215                 :     }
     216                 : 
     217             324 :     if (eOverrides)
     218               0 :         foreach (ATermMap::const_iterator, i, overrides)
     219               0 :             as.set(i->key, makeAttrRHS(i->value, makeNoPos()));
     220                 : 
     221                 :     /* Copy the non-recursive bindings.  !!! inefficient */
     222             325 :     for (ATermIterator i(nrbnds); i; ++i) {
     223               1 :         if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
     224               1 :         as.set(name, makeAttrRHS(e2, pos));
     225                 :     }
     226                 : 
     227             324 :     return makeAttrs(as);
     228                 : }
     229                 : 
     230                 : 
     231             149 : LocalNoInline(Expr updateAttrs(Expr e1, Expr e2))
     232                 : {
     233                 :     /* Note: e1 and e2 should be in normal form. */
     234                 : 
     235             149 :     ATermMap attrs;
     236             149 :     queryAllAttrs(e1, attrs, true);
     237             149 :     queryAllAttrs(e2, attrs, true);
     238                 : 
     239             149 :     return makeAttrs(attrs);
     240                 : }
     241                 : 
     242                 : 
     243            1284 : string evalString(EvalState & state, Expr e, PathSet & context)
     244                 : {
     245            1284 :     e = evalExpr(state, e);
     246            1284 :     string s;
     247            1284 :     if (!matchStr(e, s, context))
     248               0 :         throwTypeError("value is %1% while a string was expected", showType(e));
     249               0 :     return s;
     250                 : }
     251                 : 
     252                 : 
     253            1271 : string evalStringNoCtx(EvalState & state, Expr e)
     254                 : {
     255            1271 :     PathSet context;
     256            1271 :     string s = evalString(state, e, context);
     257            1271 :     if (!context.empty())
     258                 :         throw EvalError(format("the string `%1%' is not allowed to refer to a store path (such as `%2%')")
     259               0 :             % s % *(context.begin()));
     260            1271 :     return s;
     261                 : }
     262                 : 
     263                 : 
     264            1406 : int evalInt(EvalState & state, Expr e)
     265                 : {
     266            1406 :     e = evalExpr(state, e);
     267                 :     int i;
     268            1406 :     if (!matchInt(e, i))
     269               0 :         throwTypeError("value is %1% while an integer was expected", showType(e));
     270            1406 :     return i;
     271                 : }
     272                 : 
     273                 : 
     274            1524 : bool evalBool(EvalState & state, Expr e)
     275                 : {
     276            1524 :     e = evalExpr(state, e);
     277            1524 :     if (e == eTrue) return true;
     278            1088 :     else if (e == eFalse) return false;
     279               0 :     else throwTypeError("value is %1% while a boolean was expected", showType(e));
     280                 : }
     281                 : 
     282                 : 
     283            1653 : ATermList evalList(EvalState & state, Expr e)
     284                 : {
     285            1653 :     e = evalExpr(state, e);
     286                 :     ATermList list;
     287            1653 :     if (!matchList(e, list))
     288               0 :         throwTypeError("value is %1% while a list was expected", showType(e));
     289            1653 :     return list;
     290                 : }
     291                 : 
     292                 : 
     293             326 : static void flattenList(EvalState & state, Expr e, ATermList & result)
     294                 : {
     295                 :     ATermList es;
     296             326 :     e = evalExpr(state, e);
     297             326 :     if (matchList(e, es))
     298             326 :         for (ATermIterator i(es); i; ++i)
     299             162 :             flattenList(state, *i, result);
     300                 :     else
     301             162 :         result = ATinsert(result, e);
     302             326 : }
     303                 : 
     304                 : 
     305             164 : ATermList flattenList(EvalState & state, Expr e)
     306                 : {
     307             164 :     ATermList result = ATempty;
     308             164 :     flattenList(state, e, result);
     309             164 :     return ATreverse(result);
     310                 : }
     311                 : 
     312                 : 
     313                 : string coerceToString(EvalState & state, Expr e, PathSet & context,
     314            2406 :     bool coerceMore, bool copyToStore)
     315                 : {
     316            2406 :     e = evalExpr(state, e);
     317                 : 
     318            2405 :     string s;
     319                 : 
     320            2405 :     if (matchStr(e, s, context)) return s;
     321                 : 
     322                 :     ATerm s2;
     323             521 :     if (matchPath(e, s2)) {
     324             241 :         Path path(canonPath(aterm2String(s2)));
     325                 : 
     326             311 :         if (!copyToStore) return path;
     327                 :         
     328             171 :         if (isDerivation(path))
     329                 :             throw EvalError(format("file names are not allowed to end in `%1%'")
     330               0 :                 % drvExtension);
     331                 : 
     332             171 :         Path dstPath;
     333             171 :         if (state.srcToStore[path] != "")
     334              28 :             dstPath = state.srcToStore[path];
     335                 :         else {
     336                 :             dstPath = readOnlyMode
     337                 :                 ? computeStorePathForPath(path).first
     338             143 :                 : store->addToStore(path);
     339             143 :             state.srcToStore[path] = dstPath;
     340             143 :             printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
     341                 :                 % path % dstPath);
     342                 :         }
     343                 : 
     344             171 :         context.insert(dstPath);
     345             171 :         return dstPath;
     346                 :     }
     347                 :         
     348                 :     ATermList es;
     349             280 :     if (matchAttrs(e, es)) {
     350             107 :         Expr e2 = queryAttr(e, "outPath");
     351             107 :         if (!e2) throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
     352             107 :         return coerceToString(state, e2, context, coerceMore, copyToStore);
     353                 :     }
     354                 : 
     355             173 :     if (coerceMore) {
     356                 : 
     357                 :         /* Note that `false' is represented as an empty string for
     358                 :            shell scripting convenience, just like `null'. */
     359             173 :         if (e == eTrue) return "1";
     360             173 :         if (e == eFalse) return "";
     361                 :         int n;
     362             173 :         if (matchInt(e, n)) return int2String(n);
     363             164 :         if (matchNull(e)) return "";
     364                 : 
     365             164 :         if (matchList(e, es)) {
     366             164 :             string result;
     367             164 :             es = flattenList(state, e);
     368             164 :             bool first = true;
     369             326 :             for (ATermIterator i(es); i; ++i) {
     370             162 :                 if (!first) result += " "; else first = false;
     371                 :                 result += coerceToString(state, *i,
     372             162 :                     context, coerceMore, copyToStore);
     373                 :             }
     374             164 :             return result;
     375                 :         }
     376                 :     }
     377                 :     
     378               0 :     throwTypeError("cannot coerce %1% to a string", showType(e));
     379                 : }
     380                 : 
     381                 : 
     382                 : /* Common implementation of `+', ConcatStrings and `~'. */
     383                 : static ATerm concatStrings(EvalState & state, ATermVector & args,
     384             174 :     string separator = "")
     385                 : {
     386             174 :     if (args.empty()) return makeStr("", PathSet());
     387                 :     
     388             174 :     PathSet context;
     389             174 :     std::ostringstream s;
     390                 : 
     391                 :     /* If the first element is a path, then the result will also be a
     392                 :        path, we don't copy anything (yet - that's done later, since
     393                 :        paths are copied when they are used in a derivation), and none
     394                 :        of the strings are allowed to have contexts. */
     395                 :     ATerm dummy;
     396             174 :     args.front() = evalExpr(state, args.front());
     397             174 :     bool isPath = matchPath(args.front(), dummy);
     398                 : 
     399             553 :     for (ATermVector::const_iterator i = args.begin(); i != args.end(); ++i) {
     400             379 :         if (i != args.begin()) s << separator;
     401             379 :         s << coerceToString(state, *i, context, false, !isPath);
     402                 :     }
     403                 : 
     404             174 :     if (isPath && !context.empty())
     405                 :         throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'")
     406               0 :             % s.str());
     407                 :     
     408                 :     return isPath
     409                 :         ? makePath(toATerm(s.str()))
     410             174 :         : makeStr(s.str(), context);
     411                 : }
     412                 : 
     413                 : 
     414             275 : Path coerceToPath(EvalState & state, Expr e, PathSet & context)
     415                 : {
     416             275 :     string path = coerceToString(state, e, context, false, false);
     417             275 :     if (path == "" || path[0] != '/')
     418               1 :         throw EvalError(format("string `%1%' doesn't represent an absolute path") % path);
     419               1 :     return path;
     420                 : }
     421                 : 
     422                 : 
     423             192 : Expr autoCallFunction(Expr e, const ATermMap & args)
     424                 : {
     425                 :     Pattern pat;
     426                 :     ATerm body, pos;
     427                 :     ATermList formals;
     428                 :     ATermBool ellipsis;
     429                 :     
     430                 :     /* !!! this should be more general */
     431             192 :     if (matchFunction(e, pat, body, pos) && matchAttrsPat(pat, formals, ellipsis)) {
     432              28 :         ATermMap actualArgs(ATgetLength(formals));
     433                 :         
     434              86 :         for (ATermIterator i(formals); i; ++i) {
     435                 :             Expr name, def, value; ATerm def2;
     436              58 :             if (!matchFormal(*i, name, def2)) abort();
     437              58 :             if ((value = args.get(name)))
     438               2 :                 actualArgs.set(name, makeAttrRHS(value, makeNoPos()));
     439              56 :             else if (!matchDefaultValue(def2, def))
     440                 :                 throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')")
     441               0 :                     % aterm2String(name));
     442                 :         }
     443                 :         
     444              28 :         e = makeCall(e, makeAttrs(actualArgs));
     445                 :     }
     446                 :     
     447             192 :     return e;
     448                 : }
     449                 : 
     450                 : 
     451                 : /* Evaluation of various language constructs.  These have been taken
     452                 :    out of evalExpr2 to reduce stack space usage.  (GCC is really dumb
     453                 :    about stack space: it just adds up all the local variables and
     454                 :    temporaries of every scope into one huge stack frame.  This is
     455                 :    really bad for deeply recursive functions.) */
     456                 : 
     457                 : 
     458             348 : LocalNoInline(Expr evalVar(EvalState & state, ATerm name))
     459                 : {
     460             348 :     ATerm primOp = state.primOps.get(name);
     461             348 :     if (!primOp)
     462               0 :         throw EvalError(format("impossible: undefined variable `%1%'") % aterm2String(name));
     463                 :     int arity;
     464                 :     ATermBlob fun;
     465             348 :     if (!matchPrimOpDef(primOp, arity, fun)) abort();
     466             348 :     if (arity == 0)
     467                 :         /* !!! backtrace for primop call */
     468              37 :         return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
     469                 :     else
     470             311 :         return makePrimOp(arity, fun, ATempty);
     471                 : }
     472                 : 
     473                 : 
     474            4957 : LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
     475                 : {
     476                 :     Pattern pat;
     477                 :     ATerm pos;
     478                 :     Expr body;
     479                 :         
     480                 :     /* Evaluate the left-hand side. */
     481            4957 :     fun = evalExpr(state, fun);
     482                 : 
     483                 :     /* Is it a primop or a function? */
     484                 :     int arity;
     485                 :     ATermBlob funBlob;
     486                 :     ATermList args;
     487            4957 :     if (matchPrimOp(fun, arity, funBlob, args)) {
     488            2844 :         args = ATinsert(args, arg);
     489            2844 :         if (ATgetLength(args) == arity) {
     490                 :             /* Put the arguments in a vector in reverse (i.e.,
     491                 :                actual) order. */
     492            2267 :             ATermVector args2(arity);
     493            5387 :             for (ATermIterator i(args); i; ++i)
     494            3120 :                 args2[--arity] = *i;
     495                 :             /* !!! backtrace for primop call */
     496                 :             return ((PrimOp) ATgetBlobData(funBlob))
     497            2267 :                 (state, args2);
     498                 :         } else
     499                 :             /* Need more arguments, so propagate the primop. */
     500             577 :             return makePrimOp(arity, funBlob, args);
     501                 :     }
     502                 : 
     503            2113 :     else if (matchFunction(fun, pat, body, pos)) {
     504                 :         try {
     505            2113 :             return evalExpr(state, substArgs(state, body, pat, arg));
     506               8 :         } catch (Error & e) {
     507                 :             addErrorPrefix(e, "while evaluating the function at %1%:\n",
     508               4 :                 showPos(pos));
     509               4 :             throw;
     510                 :         }
     511                 :     }
     512                 :         
     513                 :     else throwTypeError(
     514                 :         "attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
     515               0 :         showType(fun));
     516                 : }
     517                 : 
     518                 : 
     519            1688 : LocalNoInline(Expr evalSelect(EvalState & state, Expr e, ATerm name))
     520                 : {
     521                 :     ATerm pos;
     522            1688 :     string s = aterm2String(name);
     523            3374 :     Expr a = queryAttr(evalExpr(state, e), s, pos);
     524            1686 :     if (!a) throwEvalError("attribute `%1%' missing", s);
     525                 :     try {
     526            1685 :         return evalExpr(state, a);
     527              16 :     } catch (Error & e) {
     528                 :         addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
     529               8 :             s, showPos(pos));
     530               8 :         throw;
     531            1688 :     }
     532                 : }
     533                 : 
     534                 : 
     535              33 : LocalNoInline(Expr evalAssert(EvalState & state, Expr cond, Expr body, ATerm pos))
     536                 : {
     537              33 :     if (!evalBool(state, cond))
     538               1 :         throw AssertionError(format("assertion failed at %1%") % showPos(pos));
     539              32 :     return evalExpr(state, body);
     540                 : }
     541                 : 
     542                 : 
     543              16 : LocalNoInline(Expr evalWith(EvalState & state, Expr defs, Expr body, ATerm pos))
     544                 : {
     545              16 :     ATermMap attrs;
     546                 :     try {
     547              16 :         defs = evalExpr(state, defs);
     548              16 :         queryAllAttrs(defs, attrs);
     549               0 :     } catch (Error & e) {
     550                 :         addErrorPrefix(e, "while evaluating the `with' definitions at %1%:\n",
     551               0 :             showPos(pos));
     552               0 :         throw;
     553                 :     }
     554                 :     try {
     555              32 :         body = substitute(Substitution(0, &attrs), body);
     556              16 :         checkVarDefs(state.primOps, body);
     557              16 :         return evalExpr(state, body);
     558               0 :     } catch (Error & e) {
     559                 :         addErrorPrefix(e, "while evaluating the `with' body at %1%:\n",
     560               0 :             showPos(pos));
     561               0 :         throw;
     562              16 :     } 
     563                 : }
     564                 : 
     565                 : 
     566             134 : LocalNoInline(Expr evalHasAttr(EvalState & state, Expr e, ATerm name))
     567                 : {
     568             134 :     ATermMap attrs;
     569             134 :     queryAllAttrs(evalExpr(state, e), attrs);
     570             134 :     return makeBool(attrs.get(name) != 0);
     571                 : }
     572                 : 
     573                 : 
     574             158 : LocalNoInline(Expr evalPlusConcat(EvalState & state, Expr e))
     575                 : {
     576                 :     Expr e1, e2;
     577                 :     ATermList es;
     578                 :     
     579             158 :     ATermVector args;
     580                 :     
     581             158 :     if (matchOpPlus(e, e1, e2)) {
     582                 : 
     583                 :         /* !!! Awful compatibility hack for `drv + /path'.
     584                 :            According to regular concatenation, /path should be
     585                 :            copied to the store and its store path should be
     586                 :            appended to the string.  However, in Nix <= 0.10, /path
     587                 :            was concatenated.  So handle that case separately, but
     588                 :            do print out a warning.  This code can go in Nix 0.12,
     589                 :            maybe. */
     590             138 :         e1 = evalExpr(state, e1);
     591             137 :         e2 = evalExpr(state, e2);
     592                 : 
     593                 :         ATermList as;
     594                 :         ATerm p;
     595             137 :         if (matchAttrs(e1, as) && matchPath(e2, p)) {
     596                 :             static bool haveWarned = false;
     597                 :             warnOnce(haveWarned, format(
     598                 :                     "concatenation of a derivation and a path is deprecated; "
     599                 :                     "you should write `drv + \"%1%\"' instead of `drv + %1%'")
     600               0 :                 % aterm2String(p));
     601               0 :             PathSet context;
     602                 :             return makeStr(
     603                 :                 coerceToString(state, makeSelect(e1, toATerm("outPath")), context)
     604               0 :                 + aterm2String(p), context);
     605                 :         }
     606                 : 
     607             137 :         args.push_back(e1);
     608             137 :         args.push_back(e2);
     609                 :     }
     610                 : 
     611              20 :     else if (matchConcatStrings(e, es))
     612              20 :         for (ATermIterator i(es); i; ++i) args.push_back(*i);
     613                 :         
     614                 :     try {
     615             157 :         return concatStrings(state, args);
     616               0 :     } catch (Error & e) {
     617               0 :         addErrorPrefix(e, "in a string concatenation:\n");
     618               0 :         throw;
     619               1 :     }
     620                 : }
     621                 : 
     622                 : 
     623              17 : LocalNoInline(Expr evalSubPath(EvalState & state, Expr e1, Expr e2))
     624                 : {
     625                 :     static bool haveWarned = false;
     626              17 :     warnOnce(haveWarned, "the subpath operator (~) is deprecated, use string concatenation (+) instead");
     627              34 :     ATermVector args;
     628              17 :     args.push_back(e1);
     629              17 :     args.push_back(e2);
     630              17 :     return concatStrings(state, args, "/");
     631                 : }
     632                 : 
     633                 : 
     634             324 : LocalNoInline(Expr evalOpConcat(EvalState & state, Expr e1, Expr e2))
     635                 : {
     636                 :     try {
     637             324 :         ATermList l1 = evalList(state, e1);
     638             324 :         ATermList l2 = evalList(state, e2);
     639             324 :         return makeList(ATconcat(l1, l2));
     640               0 :     } catch (Error & e) {
     641               0 :         addErrorPrefix(e, "in a list concatenation:\n");
     642               0 :         throw;
     643                 :     }
     644                 : }
     645                 : 
     646                 : 
     647                 : static char * deepestStack = (char *) -1; /* for measuring stack usage */
     648                 : 
     649                 : 
     650           17248 : Expr evalExpr2(EvalState & state, Expr e)
     651                 : {
     652                 :     /* When changing this function, make sure that you don't cause a
     653                 :        (large) increase in stack consumption! */
     654                 :     
     655                 :     char x;
     656           17248 :     if (&x < deepestStack) deepestStack = &x;
     657                 :     
     658                 :     Expr e1, e2, e3;
     659                 :     ATerm name, pos;
     660           17248 :     AFun sym = ATgetAFun(e);
     661                 : 
     662                 :     /* Normal forms. */
     663           17248 :     if (sym == symStr ||
     664                 :         sym == symPath ||
     665                 :         sym == symNull ||
     666                 :         sym == symInt ||
     667                 :         sym == symBool ||
     668                 :         sym == symFunction ||
     669                 :         sym == symAttrs ||
     670                 :         sym == symList ||
     671                 :         sym == symPrimOp)
     672            4857 :         return e;
     673                 :     
     674                 :     /* The `Closed' constructor is just a way to prevent substitutions
     675                 :        into expressions not containing free variables. */
     676           12391 :     if (matchClosed(e, e1))
     677            2202 :         return evalExpr(state, e1);
     678                 : 
     679                 :     /* Any encountered variables must be primops (since undefined
     680                 :        variables are detected after parsing). */
     681           10189 :     if (matchVar(e, name)) return evalVar(state, name);
     682                 : 
     683                 :     /* Function application. */
     684            9841 :     if (matchCall(e, e1, e2)) return evalCall(state, e1, e2);
     685                 : 
     686                 :     /* Attribute selection. */
     687            4884 :     if (matchSelect(e, e1, name)) return evalSelect(state, e1, name);
     688                 : 
     689                 :     /* Mutually recursive sets. */
     690                 :     ATermList rbnds, nrbnds;
     691            3196 :     if (matchRec(e, rbnds, nrbnds))
     692             324 :         return expandRec(state, e, rbnds, nrbnds);
     693                 : 
     694                 :     /* Conditionals. */
     695            2872 :     if (matchIf(e, e1, e2, e3))
     696            1170 :         return evalExpr(state, evalBool(state, e1) ? e2 : e3);
     697                 : 
     698                 :     /* Assertions. */
     699            1702 :     if (matchAssert(e, e1, e2, pos)) return evalAssert(state, e1, e2, pos);
     700                 : 
     701                 :     /* Withs. */
     702            1669 :     if (matchWith(e, e1, e2, pos)) return evalWith(state, e1, e2, pos);
     703                 : 
     704                 :     /* Generic equality/inequality.  Note that the behaviour on
     705                 :        composite data (lists, attribute sets) and functions is
     706                 :        undefined, since the subterms of those terms are not evaluated.
     707                 :        However, we don't want to make (==) strict, because that would
     708                 :        make operations like `big_derivation == null' very slow (unless
     709                 :        we were to evaluate them side-by-side). */
     710            1653 :     if (matchOpEq(e, e1, e2))
     711             693 :         return makeBool(evalExpr(state, e1) == evalExpr(state, e2));
     712                 : 
     713             960 :     if (matchOpNEq(e, e1, e2))
     714              12 :         return makeBool(evalExpr(state, e1) != evalExpr(state, e2));
     715                 : 
     716                 :     /* Negation. */
     717             948 :     if (matchOpNot(e, e1))
     718              18 :         return makeBool(!evalBool(state, e1));
     719                 : 
     720                 :     /* Implication. */
     721             930 :     if (matchOpImpl(e, e1, e2))
     722               1 :         return makeBool(!evalBool(state, e1) || evalBool(state, e2));
     723                 : 
     724                 :     /* Conjunction (logical AND). */
     725             929 :     if (matchOpAnd(e, e1, e2))
     726             146 :         return makeBool(evalBool(state, e1) && evalBool(state, e2));
     727                 : 
     728                 :     /* Disjunction (logical OR). */
     729             783 :     if (matchOpOr(e, e1, e2))
     730               1 :         return makeBool(evalBool(state, e1) || evalBool(state, e2));
     731                 : 
     732                 :     /* Attribute set update (//). */
     733             782 :     if (matchOpUpdate(e, e1, e2))
     734             149 :         return updateAttrs(evalExpr(state, e1), evalExpr(state, e2));
     735                 : 
     736                 :     /* Attribute existence test (?). */
     737             633 :     if (matchOpHasAttr(e, e1, name)) return evalHasAttr(state, e1, name);
     738                 : 
     739                 :     /* String or path concatenation. */
     740             499 :     if (sym == symOpPlus || sym == symConcatStrings)
     741             158 :         return evalPlusConcat(state, e);
     742                 : 
     743                 :     /* Backwards compatability: subpath operator (~). */
     744             341 :     if (matchSubPath(e, e1, e2)) return evalSubPath(state, e1, e2);
     745                 : 
     746                 :     /* List concatenation. */
     747             324 :     if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
     748                 : 
     749                 :     /* Barf. */
     750               0 :     abort();
     751                 : }
     752                 : 
     753                 : 
     754           27740 : Expr evalExpr(EvalState & state, Expr e)
     755                 : {
     756           27740 :     checkInterrupt();
     757                 : 
     758                 : #if 0
     759                 :     startNest(nest, lvlVomit,
     760                 :         format("evaluating expression: %1%") % e);
     761                 : #endif
     762                 : 
     763           27740 :     state.nrEvaluated++;
     764                 : 
     765                 :     /* Consult the memo table to quickly get the normal form of
     766                 :        previously evaluated expressions. */
     767           27740 :     Expr nf = state.normalForms.get(e);
     768           27740 :     if (nf) {
     769           10492 :         if (nf == makeBlackHole())
     770               2 :             throwEvalError("infinite recursion encountered");
     771           10490 :         state.nrCached++;
     772           10490 :         return nf;
     773                 :     }
     774                 : 
     775                 :     /* Otherwise, evaluate and memoize. */
     776           17248 :     state.normalForms.set(e, makeBlackHole());
     777                 :     try {
     778           17248 :         nf = evalExpr2(state, e);
     779              56 :     } catch (Error & err) {
     780              28 :         state.normalForms.remove(e);
     781              28 :         throw;
     782                 :     }
     783           17220 :     state.normalForms.set(e, nf);
     784           17220 :     return nf;
     785                 : }
     786                 : 
     787                 : 
     788              19 : Expr evalFile(EvalState & state, const Path & path)
     789                 : {
     790              19 :     startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
     791              19 :     Expr e = parseExprFromFile(state, path);
     792                 :     try {
     793              19 :         return evalExpr(state, e);
     794               0 :     } catch (Error & e) {
     795                 :         e.addPrefix(format("while evaluating the file `%1%':\n")
     796               0 :             % path);
     797               0 :         throw;
     798              19 :     }
     799                 : }
     800                 : 
     801                 : 
     802                 : static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs);
     803                 : 
     804                 : 
     805             291 : static Expr strictEvalExpr_(EvalState & state, Expr e, ATermMap & nfs)
     806                 : {
     807             291 :     e = evalExpr(state, e);
     808                 : 
     809                 :     ATermList as;
     810             291 :     if (matchAttrs(e, as)) {
     811             121 :         ATermList as2 = ATempty;
     812             322 :         for (ATermIterator i(as); i; ++i) {
     813                 :             ATerm name; Expr e; ATerm pos;
     814             201 :             if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
     815             201 :             as2 = ATinsert(as2, makeBind(name, strictEvalExpr(state, e, nfs), pos));
     816                 :         }
     817             121 :         return makeAttrs(ATreverse(as2));
     818                 :     }
     819                 :     
     820                 :     ATermList es;
     821             170 :     if (matchList(e, es)) {
     822               2 :         ATermList es2 = ATempty;
     823              51 :         for (ATermIterator i(es); i; ++i)
     824              49 :             es2 = ATinsert(es2, strictEvalExpr(state, *i, nfs));
     825               2 :         return makeList(ATreverse(es2));
     826                 :     }
     827                 :     
     828             168 :     return e;
     829                 : }
     830                 : 
     831                 : 
     832             326 : static Expr strictEvalExpr(EvalState & state, Expr e, ATermMap & nfs)
     833                 : {
     834             326 :     Expr nf = nfs.get(e);
     835             326 :     if (nf) return nf;
     836                 : 
     837             291 :     nf = strictEvalExpr_(state, e, nfs);
     838                 : 
     839             291 :     nfs.set(e, nf);
     840                 :     
     841             291 :     return nf;
     842                 : }
     843                 : 
     844                 : 
     845              76 : Expr strictEvalExpr(EvalState & state, Expr e)
     846                 : {
     847              76 :     ATermMap strictNormalForms;
     848              76 :     return strictEvalExpr(state, e, strictNormalForms);
     849                 : }
     850                 : 
     851                 : 
     852                 : /* Yes, this is a really bad idea... */
     853                 : extern "C" {
     854                 :     unsigned long AT_calcAllocatedSize();
     855                 : }
     856                 : 
     857             212 : void printEvalStats(EvalState & state)
     858                 : {
     859                 :     char x;
     860             212 :     bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
     861             212 :     printMsg(showStats ? lvlInfo : lvlDebug,
     862                 :         format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space")
     863                 :         % state.nrEvaluated % state.nrCached
     864                 :         % ((float) state.nrCached / (float) state.nrEvaluated * 100)
     865                 :         % AT_calcAllocatedSize()
     866                 :         % (&x - deepestStack));
     867             212 :     if (showStats)
     868               0 :         printATermMapStats();
     869             212 : }
     870                 : 
     871               0 :  
     872             478 : }

Generated by: LTP GCOV extension version 1.6