* Keep attribute sets in sorted order to speed up attribute lookups.

* Simplify the representation of attributes in the AST.
* Change the behaviour of listToAttrs() in case of duplicate names.
This commit is contained in:
Eelco Dolstra 2010-10-24 19:52:33 +00:00
parent 2dc6d50941
commit e0b7fb8f27
12 changed files with 186 additions and 148 deletions

View file

@ -20,14 +20,17 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
if (i == argsEnd) throw error; if (i == argsEnd) throw error;
string value = *i++; string value = *i++;
/* !!! check for duplicates! */
Value * v = state.allocValue(); Value * v = state.allocValue();
autoArgs[state.symbols.create(name)].value = v; autoArgs.push_back(Attr(state.symbols.create(name), v));
if (arg == "--arg") if (arg == "--arg")
state.mkThunk_(*v, parseExprFromString(state, value, absPath("."))); state.mkThunk_(*v, parseExprFromString(state, value, absPath(".")));
else else
mkString(*v, value); mkString(*v, value);
autoArgs.sort(); // !!! inefficient
return true; return true;
} }

View file

@ -34,18 +34,16 @@ namespace nix {
Bindings::iterator Bindings::find(const Symbol & name) Bindings::iterator Bindings::find(const Symbol & name)
{ {
iterator i = begin(); Attr key(name, 0);
for ( ; i != end() && i->name != name; ++i) ; iterator i = lower_bound(begin(), end(), key);
return i; if (i != end() && i->name == name) return i;
return end();
} }
Attr & Bindings::operator [] (const Symbol & name) void Bindings::sort()
{ {
iterator i = find(name); std::sort(begin(), end());
if (i != end()) return *i;
push_back(Attr(name, 0));
return back();
} }
@ -178,12 +176,12 @@ void EvalState::addPrimOp(const string & name,
{ {
Value * v = allocValue(); Value * v = allocValue();
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
Symbol sym = symbols.create(name); Symbol sym = symbols.create(name2);
v->type = tPrimOp; v->type = tPrimOp;
v->primOp = NEW PrimOp(primOp, arity, sym); v->primOp = NEW PrimOp(primOp, arity, sym);
staticBaseEnv.vars[sym] = baseEnvDispl; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v)); baseEnv.values[0]->attrs->push_back(Attr(sym, v));
} }
@ -506,30 +504,36 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
void ExprAttrs::eval(EvalState & state, Env & env, Value & v) void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{ {
state.mkAttrs(v); state.mkAttrs(v); // !!! reserve size
if (recursive) { if (recursive) {
/* Create a new environment that contains the attributes in /* Create a new environment that contains the attributes in
this `rec'. */ this `rec'. */
Env & env2(state.allocEnv(attrs.size() + inherited.size())); Env & env2(state.allocEnv(attrs.size()));
env2.up = &env; env2.up = &env;
unsigned int displ = 0; AttrDefs::iterator overrides = attrs.find(state.sOverrides);
bool hasOverrides = overrides != attrs.end();
/* The recursive attributes are evaluated in the new /* The recursive attributes are evaluated in the new
environment. */ environment, while the inherited attributes are evaluated
foreach (Attrs::iterator, i, attrs) { in the original environment. */
Value * vAttr = state.maybeThunk(env2, i->second.first); unsigned int displ = 0;
foreach (AttrDefs::iterator, i, attrs)
if (i->second.inherited) {
/* !!! handle overrides? */
Value * vAttr = state.lookupVar(&env, i->second.var);
env2.values[displ++] = vAttr; env2.values[displ++] = vAttr;
v.attrs->push_back(nix::Attr(i->first, vAttr, &i->second.second)); v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
} } else {
Value * vAttr;
/* The inherited attributes, on the other hand, are if (hasOverrides) {
evaluated in the original environment. */ vAttr = state.allocValue();
foreach (list<Inherited>::iterator, i, inherited) { mkThunk(*vAttr, env2, i->second.e);
Value * vAttr = state.lookupVar(&env, i->first); } else
vAttr = state.maybeThunk(env2, i->second.e);
env2.values[displ++] = vAttr; env2.values[displ++] = vAttr;
v.attrs->push_back(nix::Attr(i->first.name, vAttr, &i->second)); v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
} }
/* If the rec contains an attribute called `__overrides', then /* If the rec contains an attribute called `__overrides', then
@ -540,30 +544,27 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
still reference the original value, because that value has still reference the original value, because that value has
been substituted into the bodies of the other attributes. been substituted into the bodies of the other attributes.
Hence we need __overrides.) */ Hence we need __overrides.) */
Bindings::iterator overrides = v.attrs->find(state.sOverrides); if (hasOverrides) {
if (overrides != v.attrs->end()) { Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
state.forceAttrs(*overrides->value); state.forceAttrs(*vOverrides);
foreach (Bindings::iterator, i, *overrides->value->attrs) { foreach (Bindings::iterator, i, *vOverrides->attrs) {
nix::Attr & a = (*v.attrs)[i->name]; AttrDefs::iterator j = attrs.find(i->name);
if (a.value) if (j != attrs.end()) {
env2.values[displs[i->name]] = i->value; (*v.attrs)[j->second.displ] = *i;
a = *i; env2.values[j->second.displ] = i->value;
} else
v.attrs->push_back(*i);
} }
v.attrs->sort();
} }
} }
else { else {
foreach (Attrs::iterator, i, attrs) { foreach (AttrDefs::iterator, i, attrs)
nix::Attr & a = (*v.attrs)[i->first]; if (i->second.inherited)
a.value = state.maybeThunk(env, i->second.first); v.attrs->push_back(Attr(i->first, state.lookupVar(&env, i->second.var), &i->second.pos));
a.pos = &i->second.second; else
} v.attrs->push_back(Attr(i->first, state.maybeThunk(env, i->second.e), &i->second.pos));
foreach (list<Inherited>::iterator, i, inherited) {
nix::Attr & a = (*v.attrs)[i->first.name];
a.value = state.lookupVar(&env, i->first);
a.pos = &i->second;
}
} }
} }
@ -572,20 +573,18 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
{ {
/* Create a new environment that contains the attributes in this /* Create a new environment that contains the attributes in this
`let'. */ `let'. */
Env & env2(state.allocEnv(attrs->attrs.size() + attrs->inherited.size())); Env & env2(state.allocEnv(attrs->attrs.size()));
env2.up = &env; env2.up = &env;
unsigned int displ = 0; /* The recursive attributes are evaluated in the new environment,
while the inherited attributes are evaluated in the original
/* The recursive attributes are evaluated in the new
environment. */ environment. */
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) unsigned int displ = 0;
env2.values[displ++] = state.maybeThunk(env2, i->second.first); foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
if (i->second.inherited)
/* The inherited attributes, on the other hand, are evaluated in env2.values[displ++] = state.lookupVar(&env, i->second.var);
the original environment. */ else
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited) env2.values[displ++] = state.maybeThunk(env2, i->second.e);
env2.values[displ++] = state.lookupVar(&env, i->first);
state.eval(env2, body, v); state.eval(env2, body, v);
} }
@ -764,11 +763,13 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) { foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
Bindings::iterator j = args.find(i->name); Bindings::iterator j = args.find(i->name);
if (j != args.end()) if (j != args.end())
(*actualArgs.attrs)[i->name] = *j; actualArgs.attrs->push_back(*j);
else if (!i->def) else if (!i->def)
throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name); throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name);
} }
actualArgs.attrs->sort();
callFunction(fun, actualArgs, res); callFunction(fun, actualArgs, res);
} }
@ -851,11 +852,27 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
if (v1.attrs->size() == 0) { v = v2; return; } if (v1.attrs->size() == 0) { v = v2; return; }
if (v2.attrs->size() == 0) { v = v1; return; } if (v2.attrs->size() == 0) { v = v1; return; }
state.cloneAttrs(v1, v); state.mkAttrs(v);
/* !!! fix */ /* Merge the attribute sets, preferring values from the second
foreach (Bindings::iterator, i, *v2.attrs) set. Make sure to keep the resulting vector in sorted
(*v.attrs)[i->name] = *i; order. */
Bindings::iterator i = v1.attrs->begin();
Bindings::iterator j = v2.attrs->begin();
while (i != v1.attrs->end() && j != v2.attrs->end()) {
if (i->name == j->name) {
v.attrs->push_back(*j);
++i; ++j;
}
else if (i->name < j->name)
v.attrs->push_back(*i++);
else
v.attrs->push_back(*j++);
}
while (i != v1.attrs->end()) v.attrs->push_back(*i++);
while (j != v2.attrs->end()) v.attrs->push_back(*j++);
state.nrOpUpdateValuesCopied += v.attrs->size(); state.nrOpUpdateValuesCopied += v.attrs->size();
} }

View file

@ -34,7 +34,7 @@ class Bindings : public BindingsBase
{ {
public: public:
iterator find(const Symbol & name); iterator find(const Symbol & name);
Attr & operator [] (const Symbol & name); void sort();
}; };
@ -142,6 +142,10 @@ struct Attr
Attr(Symbol name, Value * value, Pos * pos = &noPos) Attr(Symbol name, Value * value, Pos * pos = &noPos)
: name(name), value(value), pos(pos) { }; : name(name), value(value), pos(pos) { };
Attr() : pos(&noPos) { }; Attr() : pos(&noPos) { };
bool operator < (const Attr & a) const
{
return name < a.name;
}
}; };

View file

@ -168,7 +168,7 @@ static void getDerivations(EvalState & state, Value & vIn,
foreach (SortedSymbols::iterator, i, attrs) { foreach (SortedSymbols::iterator, i, attrs) {
startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first); startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first);
string pathPrefix2 = addToPath(pathPrefix, i->first); string pathPrefix2 = addToPath(pathPrefix, i->first);
Value & v2(*(*v.attrs)[i->second].value); Value & v2(*v.attrs->find(i->second)->value);
if (combineChannels) if (combineChannels)
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
else if (getDerivation(state, v2, pathPrefix2, drvs, done)) { else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {

View file

@ -55,10 +55,11 @@ void ExprAttrs::show(std::ostream & str)
{ {
if (recursive) str << "rec "; if (recursive) str << "rec ";
str << "{ "; str << "{ ";
foreach (list<Inherited>::iterator, i, inherited) foreach (AttrDefs::iterator, i, attrs)
str << "inherit " << i->first.name << "; "; if (i->second.inherited)
foreach (Attrs::iterator, i, attrs) str << "inherit " << i->first << " " << "; ";
str << i->first << " = " << *i->second.first << "; "; else
str << i->first << " = " << *i->second.e << "; ";
str << "}"; str << "}";
} }
@ -91,10 +92,11 @@ void ExprLambda::show(std::ostream & str)
void ExprLet::show(std::ostream & str) void ExprLet::show(std::ostream & str)
{ {
str << "let "; str << "let ";
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited) foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
str << "inherit " << i->first.name << "; "; if (i->second.inherited)
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) str << "inherit " << i->first << "; ";
str << i->first << " = " << *i->second.first << "; "; else
str << i->first << " = " << *i->second.e << "; ";
str << "in " << *body; str << "in " << *body;
} }
@ -211,26 +213,18 @@ void ExprAttrs::bindVars(const StaticEnv & env)
StaticEnv newEnv(false, &env); StaticEnv newEnv(false, &env);
unsigned int displ = 0; unsigned int displ = 0;
foreach (AttrDefs::iterator, i, attrs)
newEnv.vars[i->first] = i->second.displ = displ++;
foreach (ExprAttrs::Attrs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
displs[i->first] = newEnv.vars[i->first] = displ++; if (i->second.inherited) i->second.var.bind(env);
else i->second.e->bindVars(newEnv);
foreach (list<Inherited>::iterator, i, inherited) {
displs[i->first.name] = newEnv.vars[i->first.name] = displ++;
i->first.bind(env);
} }
foreach (ExprAttrs::Attrs::iterator, i, attrs) else
i->second.first->bindVars(newEnv); foreach (AttrDefs::iterator, i, attrs)
} if (i->second.inherited) i->second.var.bind(env);
else i->second.e->bindVars(env);
else {
foreach (ExprAttrs::Attrs::iterator, i, attrs)
i->second.first->bindVars(env);
foreach (list<Inherited>::iterator, i, inherited)
i->first.bind(env);
}
} }
void ExprList::bindVars(const StaticEnv & env) void ExprList::bindVars(const StaticEnv & env)
@ -263,17 +257,12 @@ void ExprLet::bindVars(const StaticEnv & env)
StaticEnv newEnv(false, &env); StaticEnv newEnv(false, &env);
unsigned int displ = 0; unsigned int displ = 0;
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
newEnv.vars[i->first] = i->second.displ = displ++;
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
newEnv.vars[i->first] = displ++; if (i->second.inherited) i->second.var.bind(env);
else i->second.e->bindVars(newEnv);
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited) {
newEnv.vars[i->first.name] = displ++;
i->first.bind(env);
}
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
i->second.first->bindVars(newEnv);
body->bindVars(newEnv); body->bindVars(newEnv);
} }

View file

@ -101,6 +101,7 @@ struct VarRef
unsigned int level; unsigned int level;
unsigned int displ; unsigned int displ;
VarRef() { };
VarRef(const Symbol & name) : name(name) { }; VarRef(const Symbol & name) : name(name) { };
void bind(const StaticEnv & env); void bind(const StaticEnv & env);
}; };
@ -131,13 +132,18 @@ struct ExprOpHasAttr : Expr
struct ExprAttrs : Expr struct ExprAttrs : Expr
{ {
bool recursive; bool recursive;
typedef std::pair<Expr *, Pos> Attr; struct AttrDef {
typedef std::pair<VarRef, Pos> Inherited; bool inherited;
typedef std::map<Symbol, Attr> Attrs; Expr * e; // if not inherited
Attrs attrs; VarRef var; // if inherited
list<Inherited> inherited; Pos pos;
std::map<Symbol, Pos> attrNames; // used during parsing unsigned int displ; // displacement
std::map<Symbol, unsigned int> displs; AttrDef(Expr * e, const Pos & pos) : inherited(false), e(e), pos(pos) { };
AttrDef(const Symbol & name, const Pos & pos) : inherited(true), var(name), pos(pos) { };
AttrDef() { };
};
typedef std::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs;
ExprAttrs() : recursive(false) { }; ExprAttrs() : recursive(false) { };
COMMON_METHODS COMMON_METHODS
}; };

View file

@ -93,20 +93,20 @@ static void addAttr(ExprAttrs * attrs, const vector<Symbol> & attrPath,
unsigned int n = 0; unsigned int n = 0;
foreach (vector<Symbol>::const_iterator, i, attrPath) { foreach (vector<Symbol>::const_iterator, i, attrPath) {
n++; n++;
ExprAttrs::Attrs::iterator j = attrs->attrs.find(*i); ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*i);
if (j != attrs->attrs.end()) { if (j != attrs->attrs.end()) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.first); if (!j->second.inherited) {
if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.second); ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.pos);
attrs = attrs2; attrs = attrs2;
} else
dupAttr(attrPath, pos, j->second.pos);
} else { } else {
if (attrs->attrNames.find(*i) != attrs->attrNames.end())
dupAttr(attrPath, pos, attrs->attrNames[*i]);
attrs->attrNames[*i] = pos;
if (n == attrPath.size()) if (n == attrPath.size())
attrs->attrs[*i] = ExprAttrs::Attr(e, pos); attrs->attrs[*i] = ExprAttrs::AttrDef(e, pos);
else { else {
ExprAttrs * nested = new ExprAttrs; ExprAttrs * nested = new ExprAttrs;
attrs->attrs[*i] = ExprAttrs::Attr(nested, pos); attrs->attrs[*i] = ExprAttrs::AttrDef(nested, pos);
attrs = nested; attrs = nested;
} }
} }
@ -383,21 +383,19 @@ binds
| binds INHERIT ids ';' | binds INHERIT ids ';'
{ $$ = $1; { $$ = $1;
foreach (vector<Symbol>::iterator, i, *$3) { foreach (vector<Symbol>::iterator, i, *$3) {
if ($$->attrNames.find(*i) != $$->attrNames.end()) if ($$->attrs.find(*i) != $$->attrs.end())
dupAttr(*i, makeCurPos(@3, data), $$->attrNames[*i]); dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
Pos pos = makeCurPos(@3, data); Pos pos = makeCurPos(@3, data);
$$->inherited.push_back(ExprAttrs::Inherited(*i, pos)); $$->attrs[*i] = ExprAttrs::AttrDef(*i, pos);
$$->attrNames[*i] = pos;
} }
} }
| binds INHERIT '(' expr ')' ids ';' | binds INHERIT '(' expr ')' ids ';'
{ $$ = $1; { $$ = $1;
/* !!! Should ensure sharing of the expression in $4. */ /* !!! Should ensure sharing of the expression in $4. */
foreach (vector<Symbol>::iterator, i, *$6) { foreach (vector<Symbol>::iterator, i, *$6) {
if ($$->attrNames.find(*i) != $$->attrNames.end()) if ($$->attrs.find(*i) != $$->attrs.end())
dupAttr(*i, makeCurPos(@6, data), $$->attrNames[*i]); dupAttr(*i, makeCurPos(@6, data), $$->attrs[*i].pos);
$$->attrs[*i] = ExprAttrs::Attr(new ExprSelect($4, *i), makeCurPos(@6, data)); $$->attrs[*i] = ExprAttrs::AttrDef(new ExprSelect($4, *i), makeCurPos(@6, data));
$$->attrNames[*i] = makeCurPos(@6, data);
}} }}
| { $$ = new ExprAttrs; } | { $$ = new ExprAttrs; }

View file

@ -209,14 +209,13 @@ static void prim_tryEval(EvalState & state, Value * * args, Value & v)
state.mkAttrs(v); state.mkAttrs(v);
try { try {
state.forceValue(*args[0]); state.forceValue(*args[0]);
printMsg(lvlError, format("%1%") % *args[0]); v.attrs->push_back(Attr(state.symbols.create("value"), args[0]));
(*v.attrs)[state.symbols.create("value")].value = args[0];
mkBool(*state.allocAttr(v, state.symbols.create("success")), true); mkBool(*state.allocAttr(v, state.symbols.create("success")), true);
printMsg(lvlError, format("%1%") % v);
} catch (AssertionError & e) { } catch (AssertionError & e) {
mkBool(*state.allocAttr(v, state.symbols.create("value")), false); mkBool(*state.allocAttr(v, state.symbols.create("value")), false);
mkBool(*state.allocAttr(v, state.symbols.create("success")), false); mkBool(*state.allocAttr(v, state.symbols.create("success")), false);
} }
v.attrs->sort();
} }
@ -488,6 +487,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
state.mkAttrs(v); state.mkAttrs(v);
mkString(*state.allocAttr(v, state.sOutPath), outPath, singleton<PathSet>(drvPath)); mkString(*state.allocAttr(v, state.sOutPath), outPath, singleton<PathSet>(drvPath));
mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath)); mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
v.attrs->sort();
} }
@ -742,7 +742,9 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v)
names.insert(state.symbols.create(args[1]->list.elems[i]->string.s)); names.insert(state.symbols.create(args[1]->list.elems[i]->string.s));
} }
/* Copy all attributes not in that set. */ /* Copy all attributes not in that set. Note that we don't need
to sort v.attrs because it's a subset of an already sorted
vector. */
state.mkAttrs(v); state.mkAttrs(v);
foreach (Bindings::iterator, i, *args[0]->attrs) { foreach (Bindings::iterator, i, *args[0]->attrs) {
if (names.find(i->name) == names.end()) if (names.find(i->name) == names.end())
@ -761,6 +763,8 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
state.mkAttrs(v); state.mkAttrs(v);
std::set<Symbol> seen;
for (unsigned int i = 0; i < args[0]->list.length; ++i) { for (unsigned int i = 0; i < args[0]->list.length; ++i) {
Value & v2(*args[0]->list.elems[i]); Value & v2(*args[0]->list.elems[i]);
state.forceAttrs(v2); state.forceAttrs(v2);
@ -774,10 +778,15 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
if (j2 == v2.attrs->end()) if (j2 == v2.attrs->end())
throw TypeError("`value' attribute missing in a call to `listToAttrs'"); throw TypeError("`value' attribute missing in a call to `listToAttrs'");
Attr & a = (*v.attrs)[state.symbols.create(name)]; Symbol sym = state.symbols.create(name);
a.value = j2->value; if (seen.find(sym) == seen.end()) {
a.pos = j2->pos; v.attrs->push_back(Attr(sym, j2->value, j2->pos));
seen.insert(sym);
} }
/* !!! Throw an error if `name' already exists? */
}
v.attrs->sort();
} }
@ -825,6 +834,8 @@ static void prim_functionArgs(EvalState & state, Value * * args, Value & v)
foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals) foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals)
// !!! should optimise booleans (allocate only once) // !!! should optimise booleans (allocate only once)
mkBool(*state.allocAttr(v, i->name), i->def); mkBool(*state.allocAttr(v, i->name), i->def);
v.attrs->sort();
} }
@ -1007,6 +1018,7 @@ static void prim_parseDrvName(EvalState & state, Value * * args, Value & v)
state.mkAttrs(v); state.mkAttrs(v);
mkString(*state.allocAttr(v, state.sName), parsed.name); mkString(*state.allocAttr(v, state.sName), parsed.name);
mkString(*state.allocAttr(v, state.symbols.create("version")), parsed.version); mkString(*state.allocAttr(v, state.symbols.create("version")), parsed.version);
v.attrs->sort();
} }
@ -1120,6 +1132,10 @@ void EvalState::createBaseEnv()
// Versions // Versions
addPrimOp("__parseDrvName", 1, prim_parseDrvName); addPrimOp("__parseDrvName", 1, prim_parseDrvName);
addPrimOp("__compareVersions", 2, prim_compareVersions); addPrimOp("__compareVersions", 2, prim_compareVersions);
/* Now that we've added all primops, sort the `builtins' attribute
set, because attribute lookups expect it to be sorted. */
baseEnv.values[0]->attrs->sort();
} }

View file

@ -37,7 +37,7 @@ static void showAttrs(EvalState & state, bool strict, bool location,
names.insert(i->name); names.insert(i->name);
foreach (StringSet::iterator, i, names) { foreach (StringSet::iterator, i, names) {
Attr & a(attrs[state.symbols.create(*i)]); Attr & a(*attrs.find(state.symbols.create(*i)));
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
xmlAttrs["name"] = *i; xmlAttrs["name"] = *i;

View file

@ -132,7 +132,7 @@ static void getAllExprs(EvalState & state,
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)] = attrs.attrs[state.symbols.create(attrName)] =
ExprAttrs::Attr(parseExprFromFile(state, absPath(path2)), noPos); ExprAttrs::AttrDef(parseExprFromFile(state, absPath(path2)), noPos);
} }
else else
/* `path2' is a directory (with no default.nix in it); /* `path2' is a directory (with no default.nix in it);
@ -154,7 +154,7 @@ static Expr * loadSourceExpr(EvalState & state, const Path & path)
some system-wide directory). */ some system-wide directory). */
ExprAttrs * attrs = new ExprAttrs; ExprAttrs * attrs = new ExprAttrs;
attrs->attrs[state.symbols.create("_combineChannels")] = attrs->attrs[state.symbols.create("_combineChannels")] =
ExprAttrs::Attr(new ExprList(), noPos); ExprAttrs::AttrDef(new ExprList(), noPos);
getAllExprs(state, path, *attrs); getAllExprs(state, path, *attrs);
return attrs; return attrs;
} }

View file

@ -70,12 +70,13 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
if (drvPath != "") if (drvPath != "")
mkString(*state.allocAttr(v, state.sDrvPath), i->queryDrvPath(state)); mkString(*state.allocAttr(v, state.sDrvPath), i->queryDrvPath(state));
state.mkAttrs(*state.allocAttr(v, state.sMeta)); Value & vMeta = *state.allocAttr(v, state.sMeta);
state.mkAttrs(vMeta);
MetaInfo meta = i->queryMetaInfo(state); MetaInfo meta = i->queryMetaInfo(state);
foreach (MetaInfo::const_iterator, j, meta) { foreach (MetaInfo::const_iterator, j, meta) {
Value & v2(*state.allocAttr(*(*v.attrs)[state.sMeta].value, state.symbols.create(j->first))); Value & v2(*state.allocAttr(vMeta, state.symbols.create(j->first)));
switch (j->second.type) { switch (j->second.type) {
case MetaValue::tpInt: mkInt(v2, j->second.intValue); break; case MetaValue::tpInt: mkInt(v2, j->second.intValue); break;
case MetaValue::tpString: mkString(v2, j->second.stringValue); break; case MetaValue::tpString: mkString(v2, j->second.stringValue); break;
@ -92,6 +93,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
} }
} }
vMeta.attrs->sort();
v.attrs->sort();
/* This is only necessary when installing store paths, e.g., /* This is only necessary when installing store paths, e.g.,
`nix-env -i /nix/store/abcd...-foo'. */ `nix-env -i /nix/store/abcd...-foo'. */
store->addTempRoot(i->queryOutPath(state)); store->addTempRoot(i->queryOutPath(state));
@ -118,7 +122,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
mkString(*state.allocAttr(args, state.sSystem), thisSystem); mkString(*state.allocAttr(args, state.sSystem), thisSystem);
mkString(*state.allocAttr(args, state.symbols.create("manifest")), mkString(*state.allocAttr(args, state.symbols.create("manifest")),
manifestFile, singleton<PathSet>(manifestFile)); manifestFile, singleton<PathSet>(manifestFile));
(*args.attrs)[state.symbols.create("derivations")].value = &manifest; args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest));
args.attrs->sort();
mkApp(topLevel, envBuilder, args); mkApp(topLevel, envBuilder, args);
/* Evaluate it. */ /* Evaluate it. */

View file

@ -7,5 +7,5 @@ let
a = builtins.listToAttrs list; a = builtins.listToAttrs list;
b = builtins.listToAttrs ( list ++ list ); b = builtins.listToAttrs ( list ++ list );
r = builtins.listToAttrs [ (asi "result" [ a b ]) ( asi "throw" (throw "this should not be thrown")) ]; r = builtins.listToAttrs [ (asi "result" [ a b ]) ( asi "throw" (throw "this should not be thrown")) ];
x = builtins.listToAttrs [ (asi "foo" "bla") (asi "foo" "bar") ]; x = builtins.listToAttrs [ (asi "foo" "bar") (asi "foo" "bla") ];
in concat (map (x: x.a) r.result) + x.foo in concat (map (x: x.a) r.result) + x.foo