Work on Values instead of Exprs

This prevents some duplicate evaluation in nix-env and
nix-instantiate.

Also, when traversing ~/.nix-defexpr, only read regular files with the
extension .nix.  Previously it was reading files like
.../channels/binary-caches/<name>.  The only reason this didn't cause
problems is pure luck (namely, <name> shadows an actual Nix
expression, the binary-caches files happen to be syntactically valid
Nix expressions, and we iterate over the directory contents in just
the right order).
This commit is contained in:
Eelco Dolstra 2013-09-03 13:17:51 +00:00
parent 06bb2d95b4
commit ef4f5ba85e
4 changed files with 62 additions and 50 deletions

View File

@ -6,9 +6,8 @@
namespace nix { namespace nix {
// !!! Shouldn't we return a pointer to a Value? Value * findAlongAttrPath(EvalState & state, const string & attrPath,
void findAlongAttrPath(EvalState & state, const string & attrPath, Bindings & autoArgs, Value & vIn)
Bindings & autoArgs, Expr * e, Value & v)
{ {
Strings tokens = tokenizeString<Strings>(attrPath, "."); Strings tokens = tokenizeString<Strings>(attrPath, ".");
@ -17,7 +16,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
string curPath; string curPath;
state.mkThunk_(v, e); Value * v = &vIn;
foreach (Strings::iterator, i, tokens) { foreach (Strings::iterator, i, tokens) {
@ -31,10 +30,10 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
if (string2Int(attr, attrIndex)) apType = apIndex; if (string2Int(attr, attrIndex)) apType = apIndex;
/* Evaluate the expression. */ /* Evaluate the expression. */
Value vTmp; Value * vNew = state.allocValue();
state.autoCallFunction(autoArgs, v, vTmp); state.autoCallFunction(autoArgs, *v, *vNew);
v = vTmp; v = vNew;
state.forceValue(v); state.forceValue(*v);
/* It should evaluate to either an attribute set or an /* It should evaluate to either an attribute set or an
expression, according to what is specified in the expression, according to what is specified in the
@ -42,31 +41,33 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
if (apType == apAttr) { if (apType == apAttr) {
if (v.type != tAttrs) if (v->type != tAttrs)
throw TypeError( throw TypeError(
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%") format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
% curPath % showType(v)); % curPath % showType(*v));
Bindings::iterator a = v.attrs->find(state.symbols.create(attr)); Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
if (a == v.attrs->end()) if (a == v->attrs->end())
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath); throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
v = *a->value; v = &*a->value;
} }
else if (apType == apIndex) { else if (apType == apIndex) {
if (v.type != tList) if (v->type != tList)
throw TypeError( throw TypeError(
format("the expression selected by the selection path `%1%' should be a list but is %2%") format("the expression selected by the selection path `%1%' should be a list but is %2%")
% curPath % showType(v)); % curPath % showType(*v));
if (attrIndex >= v.list.length) if (attrIndex >= v->list.length)
throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath); throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
v = *v.list.elems[attrIndex]; v = v->list.elems[attrIndex];
} }
} }
return v;
} }

View File

@ -7,7 +7,7 @@
namespace nix { namespace nix {
void findAlongAttrPath(EvalState & state, const string & attrPath, Value * findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings & autoArgs, Expr * e, Value & v); Bindings & autoArgs, Value & vIn);
} }

View File

@ -94,18 +94,14 @@ static bool parseInstallSourceOptions(Globals & globals,
} }
static bool isNixExpr(const Path & path) static bool isNixExpr(const Path & path, struct stat & st)
{ {
struct stat st; return S_ISREG(st.st_mode) || (S_ISDIR(st.st_mode) && pathExists(path + "/default.nix"));
if (stat(path.c_str(), &st) == -1)
throw SysError(format("getting information about `%1%'") % path);
return !S_ISDIR(st.st_mode) || pathExists(path + "/default.nix");
} }
static void getAllExprs(EvalState & state, static void getAllExprs(EvalState & state,
const Path & path, ExprAttrs & attrs) const Path & path, Value & v)
{ {
Strings names = readDirectory(path); Strings names = readDirectory(path);
StringSet namesSorted(names.begin(), names.end()); StringSet namesSorted(names.begin(), names.end());
@ -122,7 +118,7 @@ static void getAllExprs(EvalState & state,
if (stat(path2.c_str(), &st) == -1) if (stat(path2.c_str(), &st) == -1)
continue; // ignore dangling symlinks in ~/.nix-defexpr continue; // ignore dangling symlinks in ~/.nix-defexpr
if (isNixExpr(path2)) { if (isNixExpr(path2, st) && (!S_ISREG(st.st_mode) || hasSuffix(path2, ".nix"))) {
/* Strip off the `.nix' filename suffix (if applicable), /* Strip off the `.nix' filename suffix (if applicable),
otherwise the attribute cannot be selected with the otherwise the attribute cannot be selected with the
`-A' option. Useful if you want to stick a Nix `-A' option. Useful if you want to stick a Nix
@ -130,20 +126,28 @@ static void getAllExprs(EvalState & state,
string attrName = *i; string attrName = *i;
if (hasSuffix(attrName, ".nix")) if (hasSuffix(attrName, ".nix"))
attrName = string(attrName, 0, attrName.size() - 4); attrName = string(attrName, 0, attrName.size() - 4);
attrs.attrs[state.symbols.create(attrName)] = // FIXME: make loading lazy.
ExprAttrs::AttrDef(state.parseExprFromFile(resolveExprPath(absPath(path2))), noPos); Value & v2(*state.allocAttr(v, state.symbols.create(attrName)));
state.evalFile(path2, v2);
} }
else else if (S_ISDIR(st.st_mode))
/* `path2' is a directory (with no default.nix in it); /* `path2' is a directory (with no default.nix in it);
recurse into it. */ recurse into it. */
getAllExprs(state, path2, attrs); getAllExprs(state, path2, v);
} }
} }
static Expr * loadSourceExpr(EvalState & state, const Path & path) static void loadSourceExpr(EvalState & state, const Path & path, Value & v)
{ {
if (isNixExpr(path)) return state.parseExprFromFile(resolveExprPath(absPath(path))); struct stat st;
if (stat(path.c_str(), &st) == -1)
throw SysError(format("getting information about `%1%'") % path);
if (isNixExpr(path, st)) {
state.evalFile(path, v);
return;
}
/* The path is a directory. Put the Nix expressions in the /* The path is a directory. Put the Nix expressions in the
directory in an attribute set, with the file name of each directory in an attribute set, with the file name of each
@ -151,11 +155,12 @@ static Expr * loadSourceExpr(EvalState & state, const Path & path)
(but keep the attribute set flat, not nested, to make it easier (but keep the attribute set flat, not nested, to make it easier
for a user to have a ~/.nix-defexpr directory that includes for a user to have a ~/.nix-defexpr directory that includes
some system-wide directory). */ some system-wide directory). */
ExprAttrs * attrs = new ExprAttrs; if (S_ISDIR(st.st_mode)) {
attrs->attrs[state.symbols.create("_combineChannels")] = state.mkAttrs(v, 16);
ExprAttrs::AttrDef(new ExprList(), noPos); state.mkList(*state.allocAttr(v, state.symbols.create("_combineChannels")), 0);
getAllExprs(state, path, *attrs); getAllExprs(state, path, v);
return attrs; v.attrs->sort();
}
} }
@ -163,8 +168,10 @@ static void loadDerivations(EvalState & state, Path nixExprPath,
string systemFilter, Bindings & autoArgs, string systemFilter, Bindings & autoArgs,
const string & pathPrefix, DrvInfos & elems) const string & pathPrefix, DrvInfos & elems)
{ {
Value v; Value vRoot;
findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath), v); loadSourceExpr(state, nixExprPath, vRoot);
Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot));
getDerivations(state, v, pathPrefix, autoArgs, elems, true); getDerivations(state, v, pathPrefix, autoArgs, elems, true);
@ -356,13 +363,15 @@ static void queryInstSources(EvalState & state,
(import ./foo.nix)' = `(import ./foo.nix).bar'. */ (import ./foo.nix)' = `(import ./foo.nix).bar'. */
case srcNixExprs: { case srcNixExprs: {
Expr * e1 = loadSourceExpr(state, instSource.nixExprPath); Value vArg;
loadSourceExpr(state, instSource.nixExprPath, vArg);
foreach (Strings::const_iterator, i, args) { foreach (Strings::const_iterator, i, args) {
Expr * e2 = state.parseExprFromString(*i, absPath(".")); Expr * eFun = state.parseExprFromString(*i, absPath("."));
Expr * call = new ExprApp(e2, e1); Value vFun, vTmp;
Value v; state.eval(call, v); state.eval(eFun, vFun);
getDerivations(state, v, "", instSource.autoArgs, elems, true); mkApp(vTmp, vFun, vArg);
getDerivations(state, vTmp, "", instSource.autoArgs, elems, true);
} }
break; break;
@ -413,10 +422,10 @@ static void queryInstSources(EvalState & state,
} }
case srcAttrPath: { case srcAttrPath: {
Value vRoot;
loadSourceExpr(state, instSource.nixExprPath, vRoot);
foreach (Strings::const_iterator, i, args) { foreach (Strings::const_iterator, i, args) {
Value v; Value & v(*findAlongAttrPath(state, *i, instSource.autoArgs, vRoot));
findAlongAttrPath(state, *i, instSource.autoArgs,
loadSourceExpr(state, instSource.nixExprPath), v);
getDerivations(state, v, "", instSource.autoArgs, elems, true); getDerivations(state, v, "", instSource.autoArgs, elems, true);
} }
break; break;

View File

@ -44,9 +44,11 @@ void processExpr(EvalState & state, const Strings & attrPaths,
return; return;
} }
Value vRoot;
state.eval(e, vRoot);
foreach (Strings::const_iterator, i, attrPaths) { foreach (Strings::const_iterator, i, attrPaths) {
Value v; Value & v(*findAlongAttrPath(state, *i, autoArgs, vRoot));
findAlongAttrPath(state, *i, autoArgs, e, v);
state.forceValue(v); state.forceValue(v);
PathSet context; PathSet context;