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 : }
|