1048 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1048 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "eval.hh"
 | |
| #include "parser.hh"
 | |
| #include "hash.hh"
 | |
| #include "util.hh"
 | |
| #include "store-api.hh"
 | |
| #include "derivations.hh"
 | |
| #include "globals.hh"
 | |
| 
 | |
| #include <cstring>
 | |
| 
 | |
| 
 | |
| #define LocalNoInline(f) static f __attribute__((noinline)); f
 | |
| #define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
 | |
| 
 | |
| 
 | |
| namespace nix {
 | |
|     
 | |
| 
 | |
| std::ostream & operator << (std::ostream & str, Value & v)
 | |
| {
 | |
|     switch (v.type) {
 | |
|     case tInt:
 | |
|         str << v.integer;
 | |
|         break;
 | |
|     case tBool:
 | |
|         str << (v.boolean ? "true" : "false");
 | |
|         break;
 | |
|     case tString:
 | |
|         str << "\"";
 | |
|         for (const char * i = v.string.s; *i; i++)
 | |
|             if (*i == '\"' || *i == '\\') str << "\\" << *i;
 | |
|             else if (*i == '\n') str << "\\n";
 | |
|             else if (*i == '\r') str << "\\r";
 | |
|             else if (*i == '\t') str << "\\t";
 | |
|             else str << *i;
 | |
|         str << "\"";
 | |
|         break;
 | |
|     case tPath:
 | |
|         str << v.path; // !!! escaping?
 | |
|         break;
 | |
|     case tNull:
 | |
|         str << "true";
 | |
|         break;
 | |
|     case tAttrs:
 | |
|         str << "{ ";
 | |
|         foreach (Bindings::iterator, i, *v.attrs)
 | |
|             str << (string) i->first << " = " << i->second << "; ";
 | |
|         str << "}";
 | |
|         break;
 | |
|     case tList:
 | |
|         str << "[ ";
 | |
|         for (unsigned int n = 0; n < v.list.length; ++n)
 | |
|             str << v.list.elems[n] << " ";
 | |
|         str << "]";
 | |
|         break;
 | |
|     case tThunk:
 | |
|     case tCopy:
 | |
|         str << "<CODE>";
 | |
|         break;
 | |
|     case tLambda:
 | |
|         str << "<LAMBDA>";
 | |
|         break;
 | |
|     case tPrimOp:
 | |
|         str << "<PRIMOP>";
 | |
|         break;
 | |
|     case tPrimOpApp:
 | |
|         str << "<PRIMOP-APP>";
 | |
|         break;
 | |
|     default:
 | |
|         throw Error("invalid value");
 | |
|     }
 | |
|     return str;
 | |
| }
 | |
| 
 | |
| 
 | |
| string showType(Value & v)
 | |
| {
 | |
|     switch (v.type) {
 | |
|         case tInt: return "an integer";
 | |
|         case tBool: return "a boolean";
 | |
|         case tString: return "a string";
 | |
|         case tPath: return "a path";
 | |
|         case tAttrs: return "an attribute set";
 | |
|         case tList: return "a list";
 | |
|         case tNull: return "null";
 | |
|         case tLambda: return "a function";
 | |
|         case tPrimOp: return "a built-in function";
 | |
|         case tPrimOpApp: return "a partially applied built-in function";
 | |
|         default: throw Error(format("unknown type: %1%") % v.type);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| EvalState::EvalState()
 | |
|     : sWith(symbols.create("<with>"))
 | |
|     , sOutPath(symbols.create("outPath"))
 | |
|     , sDrvPath(symbols.create("drvPath"))
 | |
|     , sType(symbols.create("type"))
 | |
|     , sMeta(symbols.create("meta"))
 | |
|     , sName(symbols.create("name"))
 | |
|     , baseEnv(allocEnv(128))
 | |
|     , baseEnvDispl(0)
 | |
|     , staticBaseEnv(false, 0)
 | |
| {
 | |
|     nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0;
 | |
|     deepestStack = (char *) -1;
 | |
| 
 | |
|     createBaseEnv();
 | |
|     
 | |
|     allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
 | |
| }
 | |
| 
 | |
| 
 | |
| EvalState::~EvalState()
 | |
| {
 | |
|     assert(recursionDepth == 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::addConstant(const string & name, Value & v)
 | |
| {
 | |
|     staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
 | |
|     baseEnv.values[baseEnvDispl++] = v;
 | |
|     string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
 | |
|     (*baseEnv.values[0].attrs)[symbols.create(name2)] = v;
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::addPrimOp(const string & name,
 | |
|     unsigned int arity, PrimOp primOp)
 | |
| {
 | |
|     Value v;
 | |
|     v.type = tPrimOp;
 | |
|     v.primOp.arity = arity;
 | |
|     v.primOp.fun = primOp;
 | |
|     staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
 | |
|     baseEnv.values[baseEnvDispl++] = v;
 | |
|     string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
 | |
|     (*baseEnv.values[0].attrs)[symbols.create(name2)] = v;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Every "format" object (even temporary) takes up a few hundred bytes
 | |
|    of stack space, which is a real killer in the recursive
 | |
|    evaluator.  So here are some helper functions for throwing
 | |
|    exceptions. */
 | |
| 
 | |
| LocalNoInlineNoReturn(void throwEvalError(const char * s))
 | |
| {
 | |
|     throw EvalError(s);
 | |
| }
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
 | |
| {
 | |
|     throw TypeError(format(s) % s2);
 | |
| }
 | |
| 
 | |
| LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2))
 | |
| {
 | |
|     throw TypeError(format(s) % pos % s2);
 | |
| }
 | |
| 
 | |
| LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
 | |
| {
 | |
|     throw TypeError(format(s) % pos);
 | |
| }
 | |
| 
 | |
| LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
 | |
| {
 | |
|     throw AssertionError(format(s) % pos);
 | |
| }
 | |
| 
 | |
| LocalNoInline(void addErrorPrefix(Error & e, const char * s))
 | |
| {
 | |
|     e.addPrefix(s);
 | |
| }
 | |
| 
 | |
| LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
 | |
| {
 | |
|     e.addPrefix(format(s) % s2);
 | |
| }
 | |
| 
 | |
| LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos))
 | |
| {
 | |
|     e.addPrefix(format(s) % pos);
 | |
| }
 | |
| 
 | |
| LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const string & s3))
 | |
| {
 | |
|     e.addPrefix(format(s) % s2 % s3);
 | |
| }
 | |
| 
 | |
| 
 | |
| void mkString(Value & v, const char * s)
 | |
| {
 | |
|     v.type = tString;
 | |
|     v.string.s = strdup(s);
 | |
|     v.string.context = 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void mkString(Value & v, const string & s, const PathSet & context)
 | |
| {
 | |
|     mkString(v, s.c_str());
 | |
|     if (!context.empty()) {
 | |
|         unsigned int n = 0;
 | |
|         v.string.context = new const char *[context.size() + 1];
 | |
|         foreach (PathSet::const_iterator, i, context) 
 | |
|             v.string.context[n++] = strdup(i->c_str());
 | |
|         v.string.context[n] = 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void mkPath(Value & v, const char * s)
 | |
| {
 | |
|     v.type = tPath;
 | |
|     v.path = strdup(s);
 | |
| }
 | |
| 
 | |
| 
 | |
| Value * EvalState::lookupVar(Env * env, const VarRef & var)
 | |
| {
 | |
|     for (unsigned int l = var.level; l; --l, env = env->up) ;
 | |
|     
 | |
|     if (var.fromWith) {
 | |
|         Bindings::iterator j = env->values[0].attrs->find(var.name);
 | |
|         if (j == env->values[0].attrs->end())
 | |
|             throwEvalError("undefined variable `%1%'", var.name);
 | |
|         return &j->second;
 | |
|     } else
 | |
|         return &env->values[var.displ];
 | |
| }
 | |
| 
 | |
| 
 | |
| Value * EvalState::allocValues(unsigned int count)
 | |
| {
 | |
|     nrValues += count;
 | |
|     return new Value[count]; // !!! check destructor
 | |
| }
 | |
| 
 | |
| 
 | |
| Env & EvalState::allocEnv(unsigned int size)
 | |
| {
 | |
|     nrEnvs++;
 | |
|     Env * env = (Env *) malloc(sizeof(Env) + size * sizeof(Value));
 | |
|     return *env;
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::mkList(Value & v, unsigned int length)
 | |
| {
 | |
|     v.type = tList;
 | |
|     v.list.length = length;
 | |
|     v.list.elems = allocValues(length);
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::mkAttrs(Value & v)
 | |
| {
 | |
|     v.type = tAttrs;
 | |
|     v.attrs = new Bindings;
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::mkThunk_(Value & v, Expr * expr)
 | |
| {
 | |
|     mkThunk(v, baseEnv, expr);
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::cloneAttrs(Value & src, Value & dst)
 | |
| {
 | |
|     mkAttrs(dst);
 | |
|     foreach (Bindings::iterator, i, *src.attrs)
 | |
|         mkCopy((*dst.attrs)[i->first], i->second);
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::evalFile(const Path & path, Value & v)
 | |
| {
 | |
|     startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
 | |
| 
 | |
|     Expr * e = parseTrees[path];
 | |
| 
 | |
|     if (!e) {
 | |
|         e = parseExprFromFile(*this, path);
 | |
|         parseTrees[path] = e;
 | |
|     }
 | |
|     
 | |
|     try {
 | |
|         eval(e, v);
 | |
|     } catch (Error & e) {
 | |
|         addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
 | |
|         throw;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| 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;
 | |
|     
 | |
|     //debug(format("eval: %1%") % *e);
 | |
| 
 | |
|     checkInterrupt();
 | |
| 
 | |
|     nrEvaluated++;
 | |
| 
 | |
|     e->eval(*this, env, v);
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::eval(Expr * e, Value & v)
 | |
| {
 | |
|     eval(baseEnv, e, v);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool EvalState::evalBool(Env & env, Expr * e)
 | |
| {
 | |
|     Value v;
 | |
|     eval(env, e, v);
 | |
|     if (v.type != tBool)
 | |
|         throwTypeError("value is %1% while a Boolean was expected", showType(v));
 | |
|     return v.boolean;
 | |
| }
 | |
| 
 | |
| 
 | |
| void Expr::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     abort();
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprInt::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     mkInt(v, n);
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprString::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     mkString(v, s.c_str());
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprPath::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     mkPath(v, s.c_str());
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     state.mkAttrs(v);
 | |
| 
 | |
|     if (recursive) {
 | |
|         /* Create a new environment that contains the attributes in
 | |
|            this `rec'. */
 | |
|         Env & env2(state.allocEnv(attrs.size() + inherited.size()));
 | |
|         env2.up = &env;
 | |
| 
 | |
|         unsigned int displ = 0;
 | |
|         
 | |
|         /* The recursive attributes are evaluated in the new
 | |
|            environment. */
 | |
|         foreach (Attrs::iterator, i, attrs) {
 | |
|             Value & v2 = (*v.attrs)[i->first];
 | |
|             mkCopy(v2, env2.values[displ]);
 | |
|             mkThunk(env2.values[displ++], env2, i->second);
 | |
|         }
 | |
| 
 | |
|         /* The inherited attributes, on the other hand, are
 | |
|            evaluated in the original environment. */
 | |
|         foreach (list<VarRef>::iterator, i, inherited) {
 | |
|             Value & v2 = (*v.attrs)[i->name];
 | |
|             Value * v3 = state.lookupVar(&env, *i);
 | |
|             mkCopy(v2, *v3);
 | |
|             mkCopy(env2.values[displ++], *v3);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     else {
 | |
|         foreach (Attrs::iterator, i, attrs) {
 | |
|             Value & v2 = (*v.attrs)[i->first];
 | |
|             mkThunk(v2, env, i->second);
 | |
|         }
 | |
| 
 | |
|         foreach (list<VarRef>::iterator, i, inherited) {
 | |
|             Value & v2 = (*v.attrs)[i->name];
 | |
|             mkCopy(v2, *state.lookupVar(&env, *i));
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprLet::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     /* Create a new environment that contains the attributes in this
 | |
|        `let'. */
 | |
|     Env & env2(state.allocEnv(attrs->attrs.size() + attrs->inherited.size()));
 | |
|     env2.up = &env;
 | |
| 
 | |
|     unsigned int displ = 0;
 | |
| 
 | |
|     /* The recursive attributes are evaluated in the new
 | |
|        environment. */
 | |
|     foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
 | |
|         mkThunk(env2.values[displ++], env2, i->second);
 | |
| 
 | |
|     /* The inherited attributes, on the other hand, are evaluated in
 | |
|        the original environment. */
 | |
|     foreach (list<VarRef>::iterator, i, attrs->inherited)
 | |
|         mkCopy(env2.values[displ++], *state.lookupVar(&env, *i));
 | |
| 
 | |
|     state.eval(env2, body, v);
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprList::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     state.mkList(v, elems.size());
 | |
|     for (unsigned int n = 0; n < v.list.length; ++n)
 | |
|         mkThunk(v.list.elems[n], env, elems[n]);
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprVar::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Value * v2 = state.lookupVar(&env, info);
 | |
|     state.forceValue(*v2);
 | |
|     v = *v2;
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprSelect::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Value v2;
 | |
|     state.eval(env, e, v2);
 | |
|     state.forceAttrs(v2); // !!! eval followed by force is slightly inefficient
 | |
|     Bindings::iterator i = v2.attrs->find(name);
 | |
|     if (i == v2.attrs->end())
 | |
|         throwEvalError("attribute `%1%' missing", name);
 | |
|     try {            
 | |
|         state.forceValue(i->second);
 | |
|     } catch (Error & e) {
 | |
|         addErrorPrefix(e, "while evaluating the attribute `%1%':\n", name);
 | |
|         throw;
 | |
|     }
 | |
|     v = i->second;
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Value vAttrs;
 | |
|     state.eval(env, e, vAttrs);
 | |
|     state.forceAttrs(vAttrs);
 | |
|     mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprLambda::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     v.type = tLambda;
 | |
|     v.lambda.env = &env;
 | |
|     v.lambda.fun = this;
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprApp::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Value vFun;
 | |
|     state.eval(env, e1, vFun);
 | |
|     Value vArg;
 | |
|     mkThunk(vArg, env, e2); // !!! should this be on the heap?
 | |
|     state.callFunction(vFun, vArg, v);
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::callFunction(Value & fun, Value & arg, Value & v)
 | |
| {
 | |
|     if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
 | |
|         unsigned int argsLeft =
 | |
|             fun.type == tPrimOp ? fun.primOp.arity : fun.primOpApp.argsLeft;
 | |
|         if (argsLeft == 1) {
 | |
|             /* We have all the arguments, so call the primop.  First
 | |
|                find the primop. */
 | |
|             Value * primOp = &fun;
 | |
|             while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left;
 | |
|             assert(primOp->type == tPrimOp);
 | |
|             unsigned int arity = primOp->primOp.arity;
 | |
|                 
 | |
|             /* Put all the arguments in an array. */
 | |
|             Value * vArgs[arity];
 | |
|             unsigned int n = arity - 1;
 | |
|             vArgs[n--] = &arg;
 | |
|             for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left)
 | |
|                 vArgs[n--] = arg->primOpApp.right;
 | |
| 
 | |
|             /* And call the primop. */
 | |
|             primOp->primOp.fun(*this, vArgs, v);
 | |
|         } else {
 | |
|             Value * v2 = allocValues(2);
 | |
|             v2[0] = fun;
 | |
|             v2[1] = arg;
 | |
|             v.type = tPrimOpApp;
 | |
|             v.primOpApp.left = &v2[0];
 | |
|             v.primOpApp.right = &v2[1];
 | |
|             v.primOpApp.argsLeft = argsLeft - 1;
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     
 | |
|     if (fun.type != tLambda)
 | |
|         throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
 | |
|             showType(fun));
 | |
| 
 | |
|     unsigned int size =
 | |
|         (fun.lambda.fun->arg.empty() ? 0 : 1) +
 | |
|         (fun.lambda.fun->matchAttrs ? fun.lambda.fun->formals->formals.size() : 0);
 | |
|     Env & env2(allocEnv(size));
 | |
|     env2.up = fun.lambda.env;
 | |
| 
 | |
|     unsigned int displ = 0;
 | |
| 
 | |
|     if (!fun.lambda.fun->matchAttrs)
 | |
|         env2.values[displ++] = arg;
 | |
| 
 | |
|     else {
 | |
|         forceAttrs(arg);
 | |
|         
 | |
|         if (!fun.lambda.fun->arg.empty())
 | |
|             env2.values[displ++] = arg;
 | |
| 
 | |
|         /* For each formal argument, get the actual argument.  If
 | |
|            there is no matching actual argument but the formal
 | |
|            argument has a default, use the default. */
 | |
|         unsigned int attrsUsed = 0;
 | |
|         foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
 | |
|             Bindings::iterator j = arg.attrs->find(i->name);
 | |
|             if (j == arg.attrs->end()) {
 | |
|                 if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
 | |
|                     fun.lambda.fun->pos, i->name);   
 | |
|                 mkThunk(env2.values[displ++], env2, i->def);
 | |
|             } else {
 | |
|                 attrsUsed++;
 | |
|                 mkCopy(env2.values[displ++], j->second);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* Check that each actual argument is listed as a formal
 | |
|            argument (unless the attribute match specifies a `...').
 | |
|            TODO: show the names of the expected/unexpected
 | |
|            arguments. */
 | |
|         if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size())
 | |
|             throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
 | |
|     }
 | |
| 
 | |
|     try {
 | |
|         eval(env2, fun.lambda.fun->body, v);
 | |
|     } catch (Error & e) {
 | |
|         addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
 | |
|         throw;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res)
 | |
| {
 | |
|     forceValue(fun);
 | |
| 
 | |
|     if (fun.type != tLambda || !fun.lambda.fun->matchAttrs) {
 | |
|         res = fun;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     Value actualArgs;
 | |
|     mkAttrs(actualArgs);
 | |
| 
 | |
|     foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
 | |
|         Bindings::const_iterator j = args.find(i->name);
 | |
|         if (j != args.end())
 | |
|             (*actualArgs.attrs)[i->name] = j->second;
 | |
|         else if (!i->def)
 | |
|             throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name);
 | |
|     }
 | |
| 
 | |
|     callFunction(fun, actualArgs, res);
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprWith::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Env & env2(state.allocEnv(1));
 | |
|     env2.up = &env;
 | |
| 
 | |
|     state.eval(env, attrs, env2.values[0]);
 | |
|     state.forceAttrs(env2.values[0]);
 | |
| 
 | |
|     /* If there is an enclosing `with', copy all attributes that don't
 | |
|        appear in this `with'. */
 | |
|     if (prevWith != -1) {
 | |
|         Env * env3 = &env;
 | |
|         for (unsigned int l = prevWith; l; --l, env3 = env3->up) ;
 | |
| 
 | |
|         foreach (Bindings::iterator, i, *env3->values[0].attrs) {
 | |
|             Bindings::iterator j = env2.values[0].attrs->find(i->first);
 | |
|             if (j == env2.values[0].attrs->end())
 | |
|                 (*env2.values[0].attrs)[i->first] = i->second; // !!! sharing
 | |
|         }
 | |
|     }
 | |
|         
 | |
|     state.eval(env2, body, v);
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprIf::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     state.eval(env, state.evalBool(env, cond) ? then : else_, v);
 | |
| }
 | |
| 
 | |
|     
 | |
| void ExprAssert::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     if (!state.evalBool(env, cond))
 | |
|         throwAssertionError("assertion failed at %1%", pos);
 | |
|     state.eval(env, body, v);
 | |
| }
 | |
| 
 | |
|     
 | |
| void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     mkBool(v, !state.evalBool(env, e));
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Value v1; state.eval(env, e1, v1);
 | |
|     Value v2; state.eval(env, e2, v2);
 | |
|     mkBool(v, state.eqValues(v1, v2));
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Value v1; state.eval(env, e1, v1);
 | |
|     Value v2; state.eval(env, e2, v2);
 | |
|     mkBool(v, !state.eqValues(v1, v2));
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     mkBool(v, state.evalBool(env, e1) && state.evalBool(env, e2));
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     mkBool(v, state.evalBool(env, e1) || state.evalBool(env, e2));
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     mkBool(v, !state.evalBool(env, e1) || state.evalBool(env, e2));
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Value v2;
 | |
|     state.eval(env, e1, v2);
 | |
|     state.forceAttrs(v2);
 | |
|         
 | |
|     state.cloneAttrs(v2, v);
 | |
|         
 | |
|     state.eval(env, e2, v2);
 | |
|     state.forceAttrs(v2);
 | |
|     
 | |
|     foreach (Bindings::iterator, i, *v2.attrs)
 | |
|         (*v.attrs)[i->first] = i->second; // !!! sharing
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     Value v1; state.eval(env, e1, v1);
 | |
|     state.forceList(v1);
 | |
|     Value v2; state.eval(env, e2, v2);
 | |
|     state.forceList(v2);
 | |
|     state.mkList(v, v1.list.length + v2.list.length);
 | |
|     /* !!! This loses sharing with the original lists.  We could use a
 | |
|        tCopy node, but that would use more memory. */
 | |
|     for (unsigned int n = 0; n < v1.list.length; ++n)
 | |
|         v.list.elems[n] = v1.list.elems[n];
 | |
|     for (unsigned int n = 0; n < v2.list.length; ++n)
 | |
|         v.list.elems[n + v1.list.length] = v2.list.elems[n];
 | |
| }
 | |
| 
 | |
| 
 | |
| void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
 | |
| {
 | |
|     PathSet context;
 | |
|     std::ostringstream s;
 | |
|         
 | |
|     bool first = true, isPath = false;
 | |
|     Value vStr;
 | |
| 
 | |
|     foreach (vector<Expr *>::iterator, i, *es) {
 | |
|         state.eval(env, *i, vStr);
 | |
| 
 | |
|         /* If the first element is a path, then the result will also
 | |
|            be a path, we don't copy anything (yet - that's done later,
 | |
|            since paths are copied when they are used in a derivation),
 | |
|            and none of the strings are allowed to have contexts. */
 | |
|         if (first) {
 | |
|             isPath = vStr.type == tPath;
 | |
|             first = false;
 | |
|         }
 | |
|             
 | |
|         s << state.coerceToString(vStr, context, false, !isPath);
 | |
|     }
 | |
|         
 | |
|     if (isPath && !context.empty())
 | |
|         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());
 | |
|     else
 | |
|         mkString(v, s.str(), context);
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::forceValue(Value & v)
 | |
| {
 | |
|     if (v.type == tThunk) {
 | |
|         ValueType saved = v.type;
 | |
|         try {
 | |
|             v.type = tBlackhole;
 | |
|             eval(*v.thunk.env, v.thunk.expr, v);
 | |
|         } catch (Error & e) {
 | |
|             v.type = saved;
 | |
|             throw;
 | |
|         }
 | |
|     }
 | |
|     else if (v.type == tCopy) {
 | |
|         forceValue(*v.val);
 | |
|         v = *v.val;
 | |
|     }
 | |
|     else if (v.type == tApp)
 | |
|         callFunction(*v.app.left, *v.app.right, v);
 | |
|     else if (v.type == tBlackhole)
 | |
|         throwEvalError("infinite recursion encountered");
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::strictForceValue(Value & v)
 | |
| {
 | |
|     forceValue(v);
 | |
|     
 | |
|     if (v.type == tAttrs) {
 | |
|         foreach (Bindings::iterator, i, *v.attrs)
 | |
|             strictForceValue(i->second);
 | |
|     }
 | |
|     
 | |
|     else if (v.type == tList) {
 | |
|         for (unsigned int n = 0; n < v.list.length; ++n)
 | |
|             strictForceValue(v.list.elems[n]);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| int EvalState::forceInt(Value & v)
 | |
| {
 | |
|     forceValue(v);
 | |
|     if (v.type != tInt)
 | |
|         throwTypeError("value is %1% while an integer was expected", showType(v));
 | |
|     return v.integer;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool EvalState::forceBool(Value & v)
 | |
| {
 | |
|     forceValue(v);
 | |
|     if (v.type != tBool)
 | |
|         throwTypeError("value is %1% while a Boolean was expected", showType(v));
 | |
|     return v.boolean;
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::forceAttrs(Value & v)
 | |
| {
 | |
|     forceValue(v);
 | |
|     if (v.type != tAttrs)
 | |
|         throwTypeError("value is %1% while an attribute set was expected", showType(v));
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::forceList(Value & v)
 | |
| {
 | |
|     forceValue(v);
 | |
|     if (v.type != tList)
 | |
|         throwTypeError("value is %1% while a list was expected", showType(v));
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::forceFunction(Value & v)
 | |
| {
 | |
|     forceValue(v);
 | |
|     if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp)
 | |
|         throwTypeError("value is %1% while a function was expected", showType(v));
 | |
| }
 | |
| 
 | |
| 
 | |
| string EvalState::forceString(Value & v)
 | |
| {
 | |
|     forceValue(v);
 | |
|     if (v.type != tString)
 | |
|         throwTypeError("value is %1% while a string was expected", showType(v));
 | |
|     return string(v.string.s);
 | |
| }
 | |
| 
 | |
| 
 | |
| string EvalState::forceString(Value & v, PathSet & context)
 | |
| {
 | |
|     string s = forceString(v);
 | |
|     if (v.string.context)
 | |
|         for (const char * * p = v.string.context; *p; ++p) 
 | |
|             context.insert(*p);
 | |
|     return s;
 | |
| }
 | |
| 
 | |
| 
 | |
| string EvalState::forceStringNoCtx(Value & v)
 | |
| {
 | |
|     string s = forceString(v);
 | |
|     if (v.string.context)
 | |
|         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;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool EvalState::isDerivation(Value & v)
 | |
| {
 | |
|     if (v.type != tAttrs) return false;
 | |
|     Bindings::iterator i = v.attrs->find(sType);
 | |
|     return i != v.attrs->end() && forceStringNoCtx(i->second) == "derivation";
 | |
| }
 | |
| 
 | |
| 
 | |
| string EvalState::coerceToString(Value & v, PathSet & context,
 | |
|     bool coerceMore, bool copyToStore)
 | |
| {
 | |
|     forceValue(v);
 | |
| 
 | |
|     string s;
 | |
| 
 | |
|     if (v.type == tString) {
 | |
|         if (v.string.context) 
 | |
|             for (const char * * p = v.string.context; *p; ++p) 
 | |
|                 context.insert(*p);
 | |
|         return v.string.s;
 | |
|     }
 | |
| 
 | |
|     if (v.type == tPath) {
 | |
|         Path path(canonPath(v.path));
 | |
| 
 | |
|         if (!copyToStore) return path;
 | |
|         
 | |
|         if (nix::isDerivation(path))
 | |
|             throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
 | |
| 
 | |
|         Path dstPath;
 | |
|         if (srcToStore[path] != "")
 | |
|             dstPath = srcToStore[path];
 | |
|         else {
 | |
|             dstPath = readOnlyMode
 | |
|                 ? computeStorePathForPath(path).first
 | |
|                 : store->addToStore(path);
 | |
|             srcToStore[path] = dstPath;
 | |
|             printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
 | |
|                 % path % dstPath);
 | |
|         }
 | |
| 
 | |
|         context.insert(dstPath);
 | |
|         return dstPath;
 | |
|     }
 | |
| 
 | |
|     if (v.type == tAttrs) {
 | |
|         Bindings::iterator i = v.attrs->find(sOutPath);
 | |
|         if (i == v.attrs->end())
 | |
|             throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
 | |
|         return coerceToString(i->second, context, coerceMore, copyToStore);
 | |
|     }
 | |
| 
 | |
|     if (coerceMore) {
 | |
| 
 | |
|         /* Note that `false' is represented as an empty string for
 | |
|            shell scripting convenience, just like `null'. */
 | |
|         if (v.type == tBool && v.boolean) return "1";
 | |
|         if (v.type == tBool && !v.boolean) return "";
 | |
|         if (v.type == tInt) return int2String(v.integer);
 | |
|         if (v.type == tNull) return "";
 | |
| 
 | |
|         if (v.type == tList) {
 | |
|             string result;
 | |
|             for (unsigned int n = 0; n < v.list.length; ++n) {
 | |
|                 result += coerceToString(v.list.elems[n],
 | |
|                     context, coerceMore, copyToStore);
 | |
|                 if (n < v.list.length - 1
 | |
|                     /* !!! not quite correct */
 | |
|                     && (v.list.elems[n].type != tList || v.list.elems[n].list.length != 0))
 | |
|                     result += " ";
 | |
|             }
 | |
|             return result;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     throwTypeError("cannot coerce %1% to a string", showType(v));
 | |
| }
 | |
| 
 | |
| 
 | |
| Path EvalState::coerceToPath(Value & v, PathSet & context)
 | |
| {
 | |
|     string path = coerceToString(v, context, false, false);
 | |
|     if (path == "" || path[0] != '/')
 | |
|         throwEvalError("string `%1%' doesn't represent an absolute path", path);
 | |
|     return path;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool EvalState::eqValues(Value & v1, Value & v2)
 | |
| {
 | |
|     forceValue(v1);
 | |
|     forceValue(v2);
 | |
| 
 | |
|     /* !!! Hack to support some old broken code that relies on pointer
 | |
|        equality tests between attribute sets.  (Specifically,
 | |
|        builderDefs calls uniqList on a list of attribute sets.)  Will
 | |
|        remove this eventually. */
 | |
|     if (&v1 == &v2) return true;
 | |
| 
 | |
|     if (v1.type != v2.type) return false;
 | |
| 
 | |
|     switch (v1.type) {
 | |
| 
 | |
|         case tInt:
 | |
|             return v1.integer == v2.integer;
 | |
| 
 | |
|         case tBool:
 | |
|             return v1.boolean == v2.boolean;
 | |
| 
 | |
|         case tString:
 | |
|             /* !!! contexts */
 | |
|             return strcmp(v1.string.s, v2.string.s) == 0;
 | |
| 
 | |
|         case tPath:
 | |
|             return strcmp(v1.path, v2.path) == 0;
 | |
| 
 | |
|         case tNull:
 | |
|             return true;
 | |
| 
 | |
|         case tList:
 | |
|             if (v2.type != tList || v1.list.length != v2.list.length) return false;
 | |
|             for (unsigned int n = 0; n < v1.list.length; ++n)
 | |
|                 if (!eqValues(v1.list.elems[n], v2.list.elems[n])) return false;
 | |
|             return true;
 | |
| 
 | |
|         case tAttrs: {
 | |
|             if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false;
 | |
|             Bindings::iterator i, j;
 | |
|             for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
 | |
|                 if (!eqValues(i->second, j->second)) return false;
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         /* Functions are incomparable. */
 | |
|         case tLambda:
 | |
|         case tPrimOp:
 | |
|         case tPrimOpApp:
 | |
|             return false;
 | |
| 
 | |
|         default:
 | |
|             throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void EvalState::printStats()
 | |
| {
 | |
|     char x;
 | |
|     bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
 | |
|     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);
 | |
|     printMsg(v, format("  symbols in symbol table: %1%") % symbols.size());
 | |
| }
 | |
| 
 | |
| 
 | |
| }
 |