* Merge the GC branch.

This commit is contained in:
Eelco Dolstra 2010-10-29 15:04:39 +00:00
commit 4aced7f8d0
30 changed files with 681 additions and 416 deletions

View File

@ -2,6 +2,8 @@ SUBDIRS = externals src scripts corepkgs doc misc tests
EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \ EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \
nix.conf.example NEWS version nix.conf.example NEWS version
pkginclude_HEADERS = config.h
include ./substitute.mk include ./substitute.mk
nix.spec: nix.spec.in nix.spec: nix.spec.in

View File

@ -250,6 +250,19 @@ AC_SUBST(bzip2_bin)
AC_SUBST(bzip2_bin_test) AC_SUBST(bzip2_bin_test)
# Whether to use the Boehm garbage collector.
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
[enable garbage collection in the Nix expression evaluator (requires Boehm GC)]),
gc=$enableval, gc=)
if test -n "$gc"; then
PKG_CHECK_MODULES([BDW_GC], [bdw-gc])
boehmgc_lib="-L$boehmgc/lib -lgc"
CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS"
AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.])
fi
AC_SUBST(boehmgc_lib)
AC_ARG_ENABLE(init-state, AC_HELP_STRING([--disable-init-state], AC_ARG_ENABLE(init-state, AC_HELP_STRING([--disable-init-state],
[do not initialise DB etc. in `make install']), [do not initialise DB etc. in `make install']),
init_state=$enableval, init_state=yes) init_state=$enableval, init_state=yes)

View File

@ -271,6 +271,17 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry> </varlistentry>
<varlistentry><term><envar>GC_INITIAL_HEAP_SIZE</envar></term>
<listitem><para>If Nix has been configured to use the Boehm garbage
collector, this variable sets the initial size of the heap in bytes.
It defaults to 384 MiB. Setting it to a low value reduces memory
consumption, but will increase runtime due to the overhead of
garbage collection.</para></listitem>
</varlistentry>
</variablelist> </variablelist>

View File

@ -105,6 +105,13 @@ this packages. Alternatively, if you already have it installed, you
can use <command>configure</command>'s <option>--with-bzip2</option> can use <command>configure</command>'s <option>--with-bzip2</option>
options to point to their respective locations.</para> options to point to their respective locations.</para>
<para>Nix can optionally use the <link
xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/">Boehm
garbage collector</link> to reduce the evaluators memory consumption.
To enable it, install <literal>pkgconfig</literal> and the Boehm
garbage collector, and pass the flag <option>--enable-gc</option> to
<command>configure</command>.</para>
</section> </section>

View File

@ -6,6 +6,27 @@
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.0"><title>Release 1.0 (TBA)</title>
<para>This release has the following improvements:</para>
<itemizedlist>
<listitem>
<para>Nix can now optionally use the Boehm garbage collector.
This significantly reduces the Nix evaluators memory footprint,
especially when evaluating large NixOS system configurations. It
can be enabled using the <option>--enable-gc</option> configure
option.</para>
</listitem>
</itemizedlist>
</section>
<!--==================================================================--> <!--==================================================================-->
<section xml:id="ssec-relnotes-0.16"><title>Release 0.16 (August 17, 2010)</title> <section xml:id="ssec-relnotes-0.16"><title>Release 0.16 (August 17, 2010)</title>

View File

@ -18,8 +18,8 @@ let
inherit officialRelease; inherit officialRelease;
buildInputs = buildInputs =
[ curl bison flex2533 perl libxml2 libxslt w3m bzip2 [ curl bison24 flex2535 perl libxml2 libxslt w3m bzip2
tetex dblatex nukeReferences tetex dblatex nukeReferences pkgconfig
]; ];
configureFlags = '' configureFlags = ''
@ -67,11 +67,12 @@ let
name = "nix"; name = "nix";
src = tarball; src = tarball;
buildInputs = [ curl perl bzip2 openssl ]; buildInputs = [ curl perl bzip2 openssl pkgconfig boehmgc ];
configureFlags = '' configureFlags = ''
--disable-init-state --disable-init-state
--with-bzip2=${bzip2} --with-bzip2=${bzip2}
--enable-gc
''; '';
}; };

View File

@ -11,7 +11,7 @@ pkginclude_HEADERS = \
names.hh symbol-table.hh names.hh symbol-table.hh
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \ libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
../boost/format/libformat.la ../boost/format/libformat.la @boehmgc_lib@
BUILT_SOURCES = \ BUILT_SOURCES = \
parser-tab.hh lexer-tab.hh parser-tab.cc lexer-tab.cc parser-tab.hh lexer-tab.hh parser-tab.cc lexer-tab.cc

View File

@ -5,8 +5,9 @@
namespace nix { namespace nix {
// !!! Shouldn't we return a pointer to a Value?
void findAlongAttrPath(EvalState & state, const string & attrPath, void findAlongAttrPath(EvalState & state, const string & attrPath,
const Bindings & autoArgs, Expr * e, Value & v) Bindings & autoArgs, Expr * e, Value & v)
{ {
Strings tokens = tokenizeString(attrPath, "."); Strings tokens = tokenizeString(attrPath, ".");
@ -48,7 +49,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
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->second.value; v = *a->value;
} }
else if (apType == apIndex) { else if (apType == apIndex) {

View File

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

View File

@ -20,13 +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++;
Value & v(autoArgs[state.symbols.create(name)].value); /* !!! check for duplicates! */
Value * v = state.allocValue();
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

@ -8,12 +8,43 @@
#include <cstring> #include <cstring>
#if HAVE_BOEHMGC
#include <gc/gc.h>
#include <gc/gc_cpp.h>
#define NEW new (UseGC)
#else
#define GC_STRDUP strdup
#define GC_MALLOC malloc
#define NEW new
#endif
#define LocalNoInline(f) static f __attribute__((noinline)); f #define LocalNoInline(f) static f __attribute__((noinline)); f
#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f #define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
namespace nix { namespace nix {
Bindings::iterator Bindings::find(const Symbol & name)
{
Attr key(name, 0);
iterator i = lower_bound(begin(), end(), key);
if (i != end() && i->name == name) return i;
return end();
}
void Bindings::sort()
{
std::sort(begin(), end());
}
std::ostream & operator << (std::ostream & str, const Value & v) std::ostream & operator << (std::ostream & str, const Value & v)
@ -46,7 +77,7 @@ std::ostream & operator << (std::ostream & str, const Value & v)
typedef std::map<string, Value *> Sorted; typedef std::map<string, Value *> Sorted;
Sorted sorted; Sorted sorted;
foreach (Bindings::iterator, i, *v.attrs) foreach (Bindings::iterator, i, *v.attrs)
sorted[i->first] = &i->second.value; sorted[i->name] = i->value;
foreach (Sorted::iterator, i, sorted) foreach (Sorted::iterator, i, sorted)
str << i->first << " = " << *i->second << "; "; str << i->first << " = " << *i->second << "; ";
str << "}"; str << "}";
@ -60,7 +91,6 @@ std::ostream & operator << (std::ostream & str, const Value & v)
break; break;
case tThunk: case tThunk:
case tApp: case tApp:
case tCopy:
str << "<CODE>"; str << "<CODE>";
break; break;
case tLambda: case tLambda:
@ -92,7 +122,6 @@ string showType(const Value & v)
case tThunk: return "a thunk"; case tThunk: return "a thunk";
case tApp: return "a function application"; case tApp: return "a function application";
case tLambda: return "a function"; case tLambda: return "a function";
case tCopy: return "a copy";
case tBlackhole: return "a black hole"; case tBlackhole: return "a black hole";
case tPrimOp: return "a built-in function"; case tPrimOp: return "a built-in function";
case tPrimOpApp: return "a partially applied built-in function"; case tPrimOpApp: return "a partially applied built-in function";
@ -116,6 +145,7 @@ EvalState::EvalState()
{ {
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0; nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
nrEvaluated = recursionDepth = maxRecursionDepth = 0; nrEvaluated = recursionDepth = maxRecursionDepth = 0;
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
deepestStack = (char *) -1; deepestStack = (char *) -1;
createBaseEnv(); createBaseEnv();
@ -132,25 +162,26 @@ EvalState::~EvalState()
void EvalState::addConstant(const string & name, Value & v) void EvalState::addConstant(const string & name, Value & v)
{ {
Value * v2 = allocValue();
*v2 = v;
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v2;
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
(*baseEnv.values[0].attrs)[symbols.create(name2)].value = v; baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v2));
} }
void EvalState::addPrimOp(const string & name, void EvalState::addPrimOp(const string & name,
unsigned int arity, PrimOp primOp) unsigned int arity, PrimOpFun primOp)
{ {
Value v; Value * v = allocValue();
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
v.type = tPrimOp; Symbol sym = symbols.create(name2);
v.primOp.arity = arity; v->type = tPrimOp;
v.primOp.fun = primOp; v->primOp = NEW PrimOp(primOp, arity, sym);
v.primOp.name = strdup(name2.c_str());
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
(*baseEnv.values[0].attrs)[symbols.create(name2)].value = v; baseEnv.values[0]->attrs->push_back(Attr(sym, v));
} }
@ -218,7 +249,7 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
void mkString(Value & v, const char * s) void mkString(Value & v, const char * s)
{ {
v.type = tString; v.type = tString;
v.string.s = strdup(s); v.string.s = GC_STRDUP(s);
v.string.context = 0; v.string.context = 0;
} }
@ -228,18 +259,28 @@ void mkString(Value & v, const string & s, const PathSet & context)
mkString(v, s.c_str()); mkString(v, s.c_str());
if (!context.empty()) { if (!context.empty()) {
unsigned int n = 0; unsigned int n = 0;
v.string.context = new const char *[context.size() + 1]; v.string.context = (const char * *)
foreach (PathSet::const_iterator, i, context) GC_MALLOC((context.size() + 1) * sizeof(char *));
v.string.context[n++] = strdup(i->c_str()); foreach (PathSet::const_iterator, i, context)
v.string.context[n++] = GC_STRDUP(i->c_str());
v.string.context[n] = 0; v.string.context[n] = 0;
} }
} }
void mkString(Value & v, const Symbol & s)
{
v.type = tString;
v.string.s = ((string) s).c_str();
v.string.context = 0;
}
void mkPath(Value & v, const char * s) void mkPath(Value & v, const char * s)
{ {
clearValue(v);
v.type = tPath; v.type = tPath;
v.path = strdup(s); v.path = GC_STRDUP(s);
} }
@ -249,22 +290,22 @@ Value * EvalState::lookupVar(Env * env, const VarRef & var)
if (var.fromWith) { if (var.fromWith) {
while (1) { while (1) {
Bindings::iterator j = env->values[0].attrs->find(var.name); Bindings::iterator j = env->values[0]->attrs->find(var.name);
if (j != env->values[0].attrs->end()) if (j != env->values[0]->attrs->end())
return &j->second.value; return j->value;
if (env->prevWith == 0) if (env->prevWith == 0)
throwEvalError("undefined variable `%1%'", var.name); throwEvalError("undefined variable `%1%'", var.name);
for (unsigned int l = env->prevWith; l; --l, env = env->up) ; for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
} }
} else } else
return &env->values[var.displ]; return env->values[var.displ];
} }
Value * EvalState::allocValues(unsigned int count) Value * EvalState::allocValue()
{ {
nrValues += count; nrValues++;
return new Value[count]; // !!! check destructor return (Value *) GC_MALLOC(sizeof(Value));
} }
@ -272,24 +313,51 @@ Env & EvalState::allocEnv(unsigned int size)
{ {
nrEnvs++; nrEnvs++;
nrValuesInEnvs += size; nrValuesInEnvs += size;
Env * env = (Env *) malloc(sizeof(Env) + size * sizeof(Value)); Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
/* Clear the values because maybeThunk() expects this. */
for (unsigned i = 0; i < size; ++i)
env->values[i] = 0;
return *env; return *env;
} }
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
{
Value * v = allocValue();
vAttrs.attrs->push_back(Attr(name, v));
return v;
}
void EvalState::mkList(Value & v, unsigned int length) void EvalState::mkList(Value & v, unsigned int length)
{ {
v.type = tList; v.type = tList;
v.list.length = length; v.list.length = length;
v.list.elems = new Value *[length]; v.list.elems = (Value * *) GC_MALLOC(length * sizeof(Value *));
nrListElems += length; nrListElems += length;
} }
void EvalState::mkAttrs(Value & v) void EvalState::mkAttrs(Value & v, unsigned int expected)
{ {
clearValue(v);
v.type = tAttrs; v.type = tAttrs;
v.attrs = new Bindings; v.attrs = NEW Bindings;
v.attrs->reserve(expected);
nrAttrsets++;
}
unsigned long nrThunks = 0;
static inline void mkThunk(Value & v, Env & env, Expr * expr)
{
v.type = tThunk;
v.thunk.env = &env;
v.thunk.expr = expr;
nrThunks++;
} }
@ -299,14 +367,28 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
} }
void EvalState::cloneAttrs(Value & src, Value & dst) unsigned long nrAvoided = 0;
/* Create a thunk for the delayed computation of the given expression
in the given environment. But if the expression is a variable,
then look it up right away. This significantly reduces the number
of thunks allocated. */
Value * EvalState::maybeThunk(Env & env, Expr * expr)
{ {
mkAttrs(dst); ExprVar * var;
foreach (Bindings::iterator, i, *src.attrs) { /* Ignore variables from `withs' because they can throw an
Attr & a = (*dst.attrs)[i->first]; exception. */
mkCopy(a.value, i->second.value); if ((var = dynamic_cast<ExprVar *>(expr))) {
a.pos = i->second.pos; Value * v = lookupVar(&env, var->info);
/* The value might not be initialised in the environment yet.
In that case, ignore it. */
if (v) { nrAvoided++; return v; }
} }
Value * v = allocValue();
mkThunk(*v, env, expr);
return v;
} }
@ -404,7 +486,7 @@ void ExprInt::eval(EvalState & state, Env & env, Value & v)
void ExprString::eval(EvalState & state, Env & env, Value & v) void ExprString::eval(EvalState & state, Env & env, Value & v)
{ {
mkString(v, s.c_str()); mkString(v, s);
} }
@ -416,35 +498,38 @@ 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, attrs.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. */
nix::Attr & a = (*v.attrs)[i->first]; unsigned int displ = 0;
mkThunk(a.value, env2, i->second.first); foreach (AttrDefs::iterator, i, attrs)
mkCopy(env2.values[displ++], a.value); if (i->second.inherited) {
a.pos = &i->second.second; /* !!! handle overrides? */
} Value * vAttr = state.lookupVar(&env, i->second.var);
env2.values[displ++] = vAttr;
/* The inherited attributes, on the other hand, are v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
evaluated in the original environment. */ } else {
foreach (list<Inherited>::iterator, i, inherited) { Value * vAttr;
nix::Attr & a = (*v.attrs)[i->first.name]; if (hasOverrides) {
Value * v2 = state.lookupVar(&env, i->first); vAttr = state.allocValue();
mkCopy(a.value, *v2); mkThunk(*vAttr, env2, i->second.e);
mkCopy(env2.values[displ++], *v2); } else
a.pos = &i->second; vAttr = state.maybeThunk(env2, i->second.e);
} env2.values[displ++] = vAttr;
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
evaluate it, and add the attributes in that set to the rec. evaluate it, and add the attributes in that set to the rec.
This allows overriding of recursive attributes, which is This allows overriding of recursive attributes, which is
@ -453,28 +538,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->second.value); state.forceAttrs(*vOverrides);
foreach (Bindings::iterator, i, *overrides->second.value.attrs) { foreach (Bindings::iterator, i, *vOverrides->attrs) {
nix::Attr & a = (*v.attrs)[i->first]; AttrDefs::iterator j = attrs.find(i->name);
mkCopy(a.value, i->second.value); if (j != attrs.end()) {
(*v.attrs)[j->second.displ] = *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)
mkThunk(a.value, 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];
mkCopy(a.value, *state.lookupVar(&env, i->first));
a.pos = &i->second;
}
} }
} }
@ -483,20 +567,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;
mkThunk(env2.values[displ++], 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);
mkCopy(env2.values[displ++], *state.lookupVar(&env, i->first));
state.eval(env2, body, v); state.eval(env2, body, v);
} }
@ -505,11 +587,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
void ExprList::eval(EvalState & state, Env & env, Value & v) void ExprList::eval(EvalState & state, Env & env, Value & v)
{ {
state.mkList(v, elems.size()); state.mkList(v, elems.size());
Value * vs = state.allocValues(v.list.length); for (unsigned int n = 0; n < v.list.length; ++n)
for (unsigned int n = 0; n < v.list.length; ++n) { v.list.elems[n] = state.maybeThunk(env, elems[n]);
v.list.elems[n] = &vs[n];
mkThunk(vs[n], env, elems[n]);
}
} }
@ -521,21 +600,26 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v)
} }
unsigned long nrLookups = 0;
unsigned long nrLookupSize = 0;
void ExprSelect::eval(EvalState & state, Env & env, Value & v) void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{ {
nrLookups++;
Value v2; Value v2;
state.evalAttrs(env, e, v2); state.evalAttrs(env, e, v2);
nrLookupSize += v2.attrs->size();
Bindings::iterator i = v2.attrs->find(name); Bindings::iterator i = v2.attrs->find(name);
if (i == v2.attrs->end()) if (i == v2.attrs->end())
throwEvalError("attribute `%1%' missing", name); throwEvalError("attribute `%1%' missing", name);
try { try {
state.forceValue(i->second.value); state.forceValue(*i->value);
} catch (Error & e) { } catch (Error & e) {
addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n", addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
name, *i->second.pos); name, *i->pos);
throw; throw;
} }
v = i->second.value; v = *i->value;
} }
@ -559,24 +643,27 @@ void ExprApp::eval(EvalState & state, Env & env, Value & v)
{ {
Value vFun; Value vFun;
state.eval(env, e1, vFun); state.eval(env, e1, vFun);
Value vArg; state.callFunction(vFun, *state.maybeThunk(env, e2), v);
mkThunk(vArg, env, e2); // !!! should this be on the heap?
state.callFunction(vFun, vArg, v);
} }
void EvalState::callFunction(Value & fun, Value & arg, Value & v) void EvalState::callFunction(Value & fun, Value & arg, Value & v)
{ {
if (fun.type == tPrimOp || fun.type == tPrimOpApp) { if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
unsigned int argsLeft =
fun.type == tPrimOp ? fun.primOp.arity : fun.primOpApp.argsLeft; /* Figure out the number of arguments still needed. */
unsigned int argsDone = 0;
Value * primOp = &fun;
while (primOp->type == tPrimOpApp) {
argsDone++;
primOp = primOp->primOpApp.left;
}
assert(primOp->type == tPrimOp);
unsigned int arity = primOp->primOp->arity;
unsigned int argsLeft = arity - argsDone;
if (argsLeft == 1) { if (argsLeft == 1) {
/* We have all the arguments, so call the primop. First /* We have all the arguments, so call the primop. */
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. */ /* Put all the arguments in an array. */
Value * vArgs[arity]; Value * vArgs[arity];
@ -587,19 +674,16 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
/* And call the primop. */ /* And call the primop. */
try { try {
primOp->primOp.fun(*this, vArgs, v); primOp->primOp->fun(*this, vArgs, v);
} catch (Error & e) { } catch (Error & e) {
addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp.name); addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp->name);
throw; throw;
} }
} else { } else {
Value * v2 = allocValues(2);
v2[0] = fun;
v2[1] = arg;
v.type = tPrimOpApp; v.type = tPrimOpApp;
v.primOpApp.left = &v2[0]; v.primOpApp.left = allocValue();
v.primOpApp.right = &v2[1]; *v.primOpApp.left = fun;
v.primOpApp.argsLeft = argsLeft - 1; v.primOpApp.right = &arg;
} }
return; return;
} }
@ -617,13 +701,13 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
unsigned int displ = 0; unsigned int displ = 0;
if (!fun.lambda.fun->matchAttrs) if (!fun.lambda.fun->matchAttrs)
env2.values[displ++] = arg; env2.values[displ++] = &arg;
else { else {
forceAttrs(arg); forceAttrs(arg);
if (!fun.lambda.fun->arg.empty()) if (!fun.lambda.fun->arg.empty())
env2.values[displ++] = arg; env2.values[displ++] = &arg;
/* For each formal argument, get the actual argument. If /* For each formal argument, get the actual argument. If
there is no matching actual argument but the formal there is no matching actual argument but the formal
@ -633,11 +717,11 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
Bindings::iterator j = arg.attrs->find(i->name); Bindings::iterator j = arg.attrs->find(i->name);
if (j == arg.attrs->end()) { if (j == arg.attrs->end()) {
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'", if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
fun.lambda.fun->pos, i->name); fun.lambda.fun->pos, i->name);
mkThunk(env2.values[displ++], env2, i->def); env2.values[displ++] = maybeThunk(env2, i->def);
} else { } else {
attrsUsed++; attrsUsed++;
mkCopy(env2.values[displ++], j->second.value); env2.values[displ++] = j->value;
} }
} }
@ -645,7 +729,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
argument (unless the attribute match specifies a `...'). argument (unless the attribute match specifies a `...').
TODO: show the names of the expected/unexpected TODO: show the names of the expected/unexpected
arguments. */ arguments. */
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size())
throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos); throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
} }
@ -658,7 +742,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
} }
void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res) void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
{ {
forceValue(fun); forceValue(fun);
@ -668,16 +752,18 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
} }
Value actualArgs; Value actualArgs;
mkAttrs(actualArgs); mkAttrs(actualArgs, fun.lambda.fun->formals->formals.size());
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) { foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
Bindings::const_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->second; 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);
} }
@ -688,7 +774,8 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
env2.up = &env; env2.up = &env;
env2.prevWith = prevWith; env2.prevWith = prevWith;
state.evalAttrs(env, attrs, env2.values[0]); env2.values[0] = state.allocValue();
state.evalAttrs(env, attrs, *env2.values[0]);
state.eval(env2, body, v); state.eval(env2, body, v);
} }
@ -754,16 +841,34 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
state.evalAttrs(env, e1, v1); state.evalAttrs(env, e1, v1);
state.evalAttrs(env, e2, v2); state.evalAttrs(env, e2, v2);
state.nrOpUpdates++;
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, v1.attrs->size() + v2.attrs->size());
foreach (Bindings::iterator, i, *v2.attrs) { /* Merge the attribute sets, preferring values from the second
Attr & a = (*v.attrs)[i->first]; set. Make sure to keep the resulting vector in sorted
mkCopy(a.value, i->second.value); order. */
a.pos = i->second.pos; 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();
} }
@ -826,10 +931,6 @@ void EvalState::forceValue(Value & v)
throw; throw;
} }
} }
else if (v.type == tCopy) {
forceValue(*v.val);
v = *v.val;
}
else if (v.type == tApp) else if (v.type == tApp)
callFunction(*v.app.left, *v.app.right, v); callFunction(*v.app.left, *v.app.right, v);
else if (v.type == tBlackhole) else if (v.type == tBlackhole)
@ -843,7 +944,7 @@ void EvalState::strictForceValue(Value & v)
if (v.type == tAttrs) { if (v.type == tAttrs) {
foreach (Bindings::iterator, i, *v.attrs) foreach (Bindings::iterator, i, *v.attrs)
strictForceValue(i->second.value); strictForceValue(*i->value);
} }
else if (v.type == tList) { else if (v.type == tList) {
@ -934,7 +1035,7 @@ bool EvalState::isDerivation(Value & v)
{ {
if (v.type != tAttrs) return false; if (v.type != tAttrs) return false;
Bindings::iterator i = v.attrs->find(sType); Bindings::iterator i = v.attrs->find(sType);
return i != v.attrs->end() && forceStringNoCtx(i->second.value) == "derivation"; return i != v.attrs->end() && forceStringNoCtx(*i->value) == "derivation";
} }
@ -978,7 +1079,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
Bindings::iterator i = v.attrs->find(sOutPath); Bindings::iterator i = v.attrs->find(sOutPath);
if (i == v.attrs->end()) if (i == v.attrs->end())
throwTypeError("cannot coerce an attribute set (except a derivation) to a string"); throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
return coerceToString(i->second.value, context, coerceMore, copyToStore); return coerceToString(*i->value, context, coerceMore, copyToStore);
} }
if (coerceMore) { if (coerceMore) {
@ -1064,9 +1165,9 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tAttrs: { case tAttrs: {
if (v1.attrs->size() != v2.attrs->size()) return false; if (v1.attrs->size() != v2.attrs->size()) return false;
Bindings::iterator i, j; Bindings::iterator i = v1.attrs->begin(), j = v2.attrs->begin();
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) for ( ; i != v1.attrs->end(); ++i, ++j)
if (i->first != j->first || !eqValues(i->second.value, j->second.value)) if (i->name != j->name || !eqValues(*i->value, *j->value))
return false; return false;
return true; return true;
} }
@ -1089,20 +1190,26 @@ void EvalState::printStats()
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0"; bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
Verbosity v = showStats ? lvlInfo : lvlDebug; Verbosity v = showStats ? lvlInfo : lvlDebug;
printMsg(v, "evaluation statistics:"); printMsg(v, "evaluation statistics:");
printMsg(v, format(" size of a value: %1%") % sizeof(Value));
printMsg(v, format(" expressions evaluated: %1%") % nrEvaluated); printMsg(v, format(" expressions evaluated: %1%") % nrEvaluated);
printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack)); printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack));
printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth); printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth);
printMsg(v, format(" stack space per eval() level: %1% bytes") printMsg(v, format(" stack space per eval() level: %1% bytes")
% ((&x - deepestStack) / (float) maxRecursionDepth)); % ((&x - deepestStack) / (float) maxRecursionDepth));
printMsg(v, format(" environments allocated: %1% (%2% bytes)") printMsg(v, format(" environments allocated: %1% (%2% bytes)")
% nrEnvs % (nrEnvs * sizeof(Env))); % nrEnvs % (nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *)));
printMsg(v, format(" values allocated in environments: %1% (%2% bytes)")
% nrValuesInEnvs % (nrValuesInEnvs * sizeof(Value)));
printMsg(v, format(" list elements: %1% (%2% bytes)") printMsg(v, format(" list elements: %1% (%2% bytes)")
% nrListElems % (nrListElems * sizeof(Value *))); % nrListElems % (nrListElems * sizeof(Value *)));
printMsg(v, format(" misc. values allocated: %1% (%2% bytes)") printMsg(v, format(" values allocated: %1% (%2% bytes)")
% nrValues % (nrValues * sizeof(Value))); % nrValues % (nrValues * sizeof(Value)));
printMsg(v, format(" attribute sets allocated: %1%") % nrAttrsets);
printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates);
printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
printMsg(v, format(" symbols in symbol table: %1%") % symbols.size()); printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());
printMsg(v, format(" number of thunks: %1%") % nrThunks);
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
printMsg(v, format(" attr lookup size: %1%") % nrLookupSize);
} }

View File

@ -7,6 +7,10 @@
#include <map> #include <map>
#if HAVE_BOEHMGC
#include <gc/gc_allocator.h>
#endif
namespace nix { namespace nix {
@ -16,7 +20,22 @@ struct Env;
struct Value; struct Value;
struct Attr; struct Attr;
typedef std::map<Symbol, Attr> Bindings;
/* Attribute sets are represented as a vector of attributes, sorted by
symbol (i.e. pointer to the attribute name in the symbol table). */
#if HAVE_BOEHMGC
typedef std::vector<Attr, gc_allocator<Attr> > BindingsBase;
#else
typedef std::vector<Attr> BindingsBase;
#endif
class Bindings : public BindingsBase
{
public:
iterator find(const Symbol & name);
void sort();
};
typedef enum { typedef enum {
@ -30,14 +49,23 @@ typedef enum {
tThunk, tThunk,
tApp, tApp,
tLambda, tLambda,
tCopy,
tBlackhole, tBlackhole,
tPrimOp, tPrimOp,
tPrimOpApp, tPrimOpApp,
} ValueType; } ValueType;
typedef void (* PrimOp) (EvalState & state, Value * * args, Value & v); typedef void (* PrimOpFun) (EvalState & state, Value * * args, Value & v);
struct PrimOp
{
PrimOpFun fun;
unsigned int arity;
Symbol name;
PrimOp(PrimOpFun fun, unsigned int arity, Symbol name)
: fun(fun), arity(arity), name(name) { }
};
struct Value struct Value
@ -90,15 +118,9 @@ struct Value
Env * env; Env * env;
ExprLambda * fun; ExprLambda * fun;
} lambda; } lambda;
Value * val; PrimOp * primOp;
struct {
PrimOp fun;
char * name;
unsigned int arity;
} primOp;
struct { struct {
Value * left, * right; Value * left, * right;
unsigned int argsLeft;
} primOpApp; } primOpApp;
}; };
}; };
@ -108,20 +130,36 @@ struct Env
{ {
Env * up; Env * up;
unsigned int prevWith; // nr of levels up to next `with' environment unsigned int prevWith; // nr of levels up to next `with' environment
Value values[0]; Value * values[0];
}; };
struct Attr struct Attr
{ {
Value value; Symbol name;
Value * value;
Pos * pos; Pos * pos;
Attr(Symbol name, Value * value, Pos * pos = &noPos)
: name(name), value(value), pos(pos) { };
Attr() : pos(&noPos) { }; Attr() : pos(&noPos) { };
bool operator < (const Attr & a) const
{
return name < a.name;
}
}; };
/* After overwriting an app node, be sure to clear pointers in the
Value to ensure that the target isn't kept alive unnecessarily. */
static inline void clearValue(Value & v)
{
v.app.right = 0;
}
static inline void mkInt(Value & v, int n) static inline void mkInt(Value & v, int n)
{ {
clearValue(v);
v.type = tInt; v.type = tInt;
v.integer = n; v.integer = n;
} }
@ -129,26 +167,12 @@ static inline void mkInt(Value & v, int n)
static inline void mkBool(Value & v, bool b) static inline void mkBool(Value & v, bool b)
{ {
clearValue(v);
v.type = tBool; v.type = tBool;
v.boolean = b; v.boolean = b;
} }
static inline void mkThunk(Value & v, Env & env, Expr * expr)
{
v.type = tThunk;
v.thunk.env = &env;
v.thunk.expr = expr;
}
static inline void mkCopy(Value & v, Value & src)
{
v.type = tCopy;
v.val = &src;
}
static inline void mkApp(Value & v, Value & left, Value & right) static inline void mkApp(Value & v, Value & left, Value & right)
{ {
v.type = tApp; v.type = tApp;
@ -268,7 +292,7 @@ private:
void addConstant(const string & name, Value & v); void addConstant(const string & name, Value & v);
void addPrimOp(const string & name, void addPrimOp(const string & name,
unsigned int arity, PrimOp primOp); unsigned int arity, PrimOpFun primOp);
Value * lookupVar(Env * env, const VarRef & var); Value * lookupVar(Env * env, const VarRef & var);
@ -286,18 +310,20 @@ public:
/* Automatically call a function for which each argument has a /* Automatically call a function for which each argument has a
default value or has a binding in the `args' map. */ default value or has a binding in the `args' map. */
void autoCallFunction(const Bindings & args, Value & fun, Value & res); void autoCallFunction(Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */ /* Allocation primitives. */
Value * allocValues(unsigned int count); Value * allocValue();
Env & allocEnv(unsigned int size); Env & allocEnv(unsigned int size);
void mkList(Value & v, unsigned int length); Value * allocAttr(Value & vAttrs, const Symbol & name);
void mkAttrs(Value & v);
void mkThunk_(Value & v, Expr * expr);
void cloneAttrs(Value & src, Value & dst);
void mkList(Value & v, unsigned int length);
void mkAttrs(Value & v, unsigned int expected);
void mkThunk_(Value & v, Expr * expr);
Value * maybeThunk(Env & env, Expr * expr);
/* Print statistics. */ /* Print statistics. */
void printStats(); void printStats();
@ -308,11 +334,15 @@ private:
unsigned long nrValues; unsigned long nrValues;
unsigned long nrListElems; unsigned long nrListElems;
unsigned long nrEvaluated; unsigned long nrEvaluated;
unsigned long nrAttrsets;
unsigned long nrOpUpdates;
unsigned long nrOpUpdateValuesCopied;
unsigned int recursionDepth; unsigned int recursionDepth;
unsigned int maxRecursionDepth; unsigned int maxRecursionDepth;
char * deepestStack; /* for measuring stack usage */ char * deepestStack; /* for measuring stack usage */
friend class RecursionCounter; friend class RecursionCounter;
friend class ExprOpUpdate;
}; };

View File

@ -10,7 +10,7 @@ string DrvInfo::queryDrvPath(EvalState & state) const
if (drvPath == "" && attrs) { if (drvPath == "" && attrs) {
Bindings::iterator i = attrs->find(state.sDrvPath); Bindings::iterator i = attrs->find(state.sDrvPath);
PathSet context; PathSet context;
(string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : ""; (string &) drvPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : "";
} }
return drvPath; return drvPath;
} }
@ -21,7 +21,7 @@ string DrvInfo::queryOutPath(EvalState & state) const
if (outPath == "" && attrs) { if (outPath == "" && attrs) {
Bindings::iterator i = attrs->find(state.sOutPath); Bindings::iterator i = attrs->find(state.sOutPath);
PathSet context; PathSet context;
(string &) outPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : ""; (string &) outPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : "";
} }
return outPath; return outPath;
} }
@ -36,23 +36,23 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
Bindings::iterator a = attrs->find(state.sMeta); Bindings::iterator a = attrs->find(state.sMeta);
if (a == attrs->end()) return meta; /* fine, empty meta information */ if (a == attrs->end()) return meta; /* fine, empty meta information */
state.forceAttrs(a->second.value); state.forceAttrs(*a->value);
foreach (Bindings::iterator, i, *a->second.value.attrs) { foreach (Bindings::iterator, i, *a->value->attrs) {
MetaValue value; MetaValue value;
state.forceValue(i->second.value); state.forceValue(*i->value);
if (i->second.value.type == tString) { if (i->value->type == tString) {
value.type = MetaValue::tpString; value.type = MetaValue::tpString;
value.stringValue = i->second.value.string.s; value.stringValue = i->value->string.s;
} else if (i->second.value.type == tInt) { } else if (i->value->type == tInt) {
value.type = MetaValue::tpInt; value.type = MetaValue::tpInt;
value.intValue = i->second.value.integer; value.intValue = i->value->integer;
} else if (i->second.value.type == tList) { } else if (i->value->type == tList) {
value.type = MetaValue::tpStrings; value.type = MetaValue::tpStrings;
for (unsigned int j = 0; j < i->second.value.list.length; ++j) for (unsigned int j = 0; j < i->value->list.length; ++j)
value.stringValues.push_back(state.forceStringNoCtx(*i->second.value.list.elems[j])); value.stringValues.push_back(state.forceStringNoCtx(*i->value->list.elems[j]));
} else continue; } else continue;
((MetaInfo &) meta)[i->first] = value; ((MetaInfo &) meta)[i->name] = value;
} }
return meta; return meta;
@ -99,13 +99,13 @@ static bool getDerivation(EvalState & state, Value & v,
Bindings::iterator i = v.attrs->find(state.sName); Bindings::iterator i = v.attrs->find(state.sName);
/* !!! We really would like to have a decent back trace here. */ /* !!! We really would like to have a decent back trace here. */
if (i == v.attrs->end()) throw TypeError("derivation name missing"); if (i == v.attrs->end()) throw TypeError("derivation name missing");
drv.name = state.forceStringNoCtx(i->second.value); drv.name = state.forceStringNoCtx(*i->value);
i = v.attrs->find(state.sSystem); Bindings::iterator i2 = v.attrs->find(state.sSystem);
if (i == v.attrs->end()) if (i2 == v.attrs->end())
drv.system = "unknown"; drv.system = "unknown";
else else
drv.system = state.forceStringNoCtx(i->second.value); drv.system = state.forceStringNoCtx(*i2->value);
drv.attrs = v.attrs; drv.attrs = v.attrs;
@ -138,7 +138,7 @@ static string addToPath(const string & s1, const string & s2)
static void getDerivations(EvalState & state, Value & vIn, static void getDerivations(EvalState & state, Value & vIn,
const string & pathPrefix, const Bindings & autoArgs, const string & pathPrefix, Bindings & autoArgs,
DrvInfos & drvs, Done & done) DrvInfos & drvs, Done & done)
{ {
Value v; Value v;
@ -163,12 +163,12 @@ static void getDerivations(EvalState & state, Value & vIn,
typedef std::map<string, Symbol> SortedSymbols; typedef std::map<string, Symbol> SortedSymbols;
SortedSymbols attrs; SortedSymbols attrs;
foreach (Bindings::iterator, i, *v.attrs) foreach (Bindings::iterator, i, *v.attrs)
attrs.insert(std::pair<string, Symbol>(i->first, i->first)); attrs.insert(std::pair<string, Symbol>(i->name, i->name));
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)) {
@ -178,7 +178,7 @@ static void getDerivations(EvalState & state, Value & vIn,
attribute. */ attribute. */
if (v2.type == tAttrs) { if (v2.type == tAttrs) {
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations")); Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
if (j != v2.attrs->end() && state.forceBool(j->second.value)) if (j != v2.attrs->end() && state.forceBool(*j->value))
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done); getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
} }
} }
@ -200,7 +200,7 @@ static void getDerivations(EvalState & state, Value & vIn,
void getDerivations(EvalState & state, Value & v, const string & pathPrefix, void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
const Bindings & autoArgs, DrvInfos & drvs) Bindings & autoArgs, DrvInfos & drvs)
{ {
Done done; Done done;
getDerivations(state, v, pathPrefix, autoArgs, drvs, done); getDerivations(state, v, pathPrefix, autoArgs, drvs, done);

View File

@ -70,7 +70,7 @@ typedef list<DrvInfo> DrvInfos;
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv); bool getDerivation(EvalState & state, Value & v, DrvInfo & drv);
void getDerivations(EvalState & state, Value & v, const string & pathPrefix, void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
const Bindings & autoArgs, DrvInfos & drvs); Bindings & autoArgs, DrvInfos & drvs);
} }

View File

@ -46,7 +46,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
} }
static Expr * unescapeStr(const char * s) static Expr * unescapeStr(SymbolTable & symbols, const char * s)
{ {
string t; string t;
char c; char c;
@ -66,7 +66,7 @@ static Expr * unescapeStr(const char * s)
} }
else t += c; else t += c;
} }
return new ExprString(t); return new ExprString(symbols.create(t));
} }
@ -119,7 +119,7 @@ inherit { return INHERIT; }
"$\"" will be consumed as part of a string, rather "$\"" will be consumed as part of a string, rather
than a "$" followed by the string terminator. than a "$" followed by the string terminator.
Disallow "$\"" for now. */ Disallow "$\"" for now. */
yylval->e = unescapeStr(yytext); yylval->e = unescapeStr(data->symbols, yytext);
return STR; return STR;
} }
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; } <STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
@ -140,7 +140,7 @@ inherit { return INHERIT; }
return IND_STR; return IND_STR;
} }
<IND_STRING>\'\'\\. { <IND_STRING>\'\'\\. {
yylval->e = unescapeStr(yytext + 2); yylval->e = unescapeStr(data->symbols, yytext + 2);
return IND_STR; return IND_STR;
} }
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; } <IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }

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)
foreach (ExprAttrs::Attrs::iterator, i, attrs) newEnv.vars[i->first] = i->second.displ = displ++;
newEnv.vars[i->first] = displ++;
foreach (AttrDefs::iterator, i, attrs)
foreach (list<Inherited>::iterator, i, inherited) { if (i->second.inherited) i->second.var.bind(env);
newEnv.vars[i->first.name] = displ++; else i->second.e->bindVars(newEnv);
i->first.bind(env);
}
foreach (ExprAttrs::Attrs::iterator, i, attrs)
i->second.first->bindVars(newEnv);
} }
else { else
foreach (ExprAttrs::Attrs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
i->second.first->bindVars(env); if (i->second.inherited) i->second.var.bind(env);
else i->second.e->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)
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) newEnv.vars[i->first] = i->second.displ = displ++;
newEnv.vars[i->first] = displ++;
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
foreach (list<ExprAttrs::Inherited>::iterator, i, attrs->inherited) { if (i->second.inherited) i->second.var.bind(env);
newEnv.vars[i->first.name] = displ++; else i->second.e->bindVars(newEnv);
i->first.bind(env);
}
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
i->second.first->bindVars(newEnv);
body->bindVars(newEnv); body->bindVars(newEnv);
} }

View File

@ -65,8 +65,8 @@ struct ExprInt : Expr
struct ExprString : Expr struct ExprString : Expr
{ {
string s; Symbol s;
ExprString(const string & s) : s(s) { }; ExprString(const Symbol & s) : s(s) { };
COMMON_METHODS COMMON_METHODS
}; };
@ -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,12 +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
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

@ -7,19 +7,44 @@
%parse-param { yyscan_t scanner } %parse-param { yyscan_t scanner }
%parse-param { ParseData * data } %parse-param { ParseData * data }
%lex-param { yyscan_t scanner } %lex-param { yyscan_t scanner }
%lex-param { ParseData * data }
%code requires {
%{
/* Newer versions of Bison copy the declarations below to #ifndef BISON_HEADER
parser-tab.hh, which sucks bigtime since lexer.l doesn't want that #define BISON_HEADER
stuff. So allow it to be excluded. */
#ifndef BISON_HEADER_HACK
#define BISON_HEADER_HACK
#include "util.hh" #include "util.hh"
#include "nixexpr.hh" #include "nixexpr.hh"
namespace nix {
struct ParseData
{
SymbolTable & symbols;
Expr * result;
Path basePath;
Path path;
string error;
Symbol sLetBody;
ParseData(SymbolTable & symbols)
: symbols(symbols)
, sLetBody(symbols.create("<let-body>"))
{ };
};
}
#define YY_DECL int yylex \
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
#endif
}
%{
#include "parser-tab.hh" #include "parser-tab.hh"
#include "lexer-tab.hh" #include "lexer-tab.hh"
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4 #define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
@ -28,27 +53,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
YY_DECL;
using namespace nix; using namespace nix;
namespace nix { namespace nix {
struct ParseData
{
SymbolTable & symbols;
Expr * result;
Path basePath;
Path path;
string error;
Symbol sLetBody;
ParseData(SymbolTable & symbols)
: symbols(symbols)
, sLetBody(symbols.create("<let-body>"))
{ };
};
static string showAttrPath(const vector<Symbol> & attrPath) static string showAttrPath(const vector<Symbol> & attrPath)
{ {
@ -82,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);
attrs = attrs2; if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.pos);
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;
} }
} }
@ -113,9 +124,9 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
} }
static Expr * stripIndentation(vector<Expr *> & es) static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
{ {
if (es.empty()) return new ExprString(""); if (es.empty()) return new ExprString(symbols.create(""));
/* Figure out the minimum indentation. Note that by design /* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So whitespace-only final lines are not taken into account. (So
@ -195,7 +206,7 @@ static Expr * stripIndentation(vector<Expr *> & es)
s2 = string(s2, 0, p + 1); s2 = string(s2, 0, p + 1);
} }
es2->push_back(new ExprString(s2)); es2->push_back(new ExprString(symbols.create(s2)));
} }
return new ExprConcatStrings(es2); return new ExprConcatStrings(es2);
@ -224,9 +235,6 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
} }
#endif
%} %}
%union { %union {
@ -337,15 +345,15 @@ expr_simple
| INT { $$ = new ExprInt($1); } | INT { $$ = new ExprInt($1); }
| '"' string_parts '"' { | '"' string_parts '"' {
/* For efficiency, and to simplify parse trees a bit. */ /* For efficiency, and to simplify parse trees a bit. */
if ($2->empty()) $$ = new ExprString(""); if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
else if ($2->size() == 1) $$ = $2->front(); else if ($2->size() == 1) $$ = $2->front();
else $$ = new ExprConcatStrings($2); else $$ = new ExprConcatStrings($2);
} }
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(*$2); $$ = stripIndentation(data->symbols, *$2);
} }
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); } | PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
| URI { $$ = new ExprString($1); } | URI { $$ = new ExprString(data->symbols.create($1)); }
| '(' expr ')' { $$ = $2; } | '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared /* Let expressions `let {..., body = ...}' are just desugared
into `(rec {..., body = ...}).body'. */ into `(rec {..., body = ...}).body'. */
@ -375,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

@ -119,24 +119,24 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
args[0]->attrs->find(state.symbols.create("startSet")); args[0]->attrs->find(state.symbols.create("startSet"));
if (startSet == args[0]->attrs->end()) if (startSet == args[0]->attrs->end())
throw EvalError("attribute `startSet' required"); throw EvalError("attribute `startSet' required");
state.forceList(startSet->second.value); state.forceList(*startSet->value);
list<Value *> workSet; list<Value *> workSet;
for (unsigned int n = 0; n < startSet->second.value.list.length; ++n) for (unsigned int n = 0; n < startSet->value->list.length; ++n)
workSet.push_back(startSet->second.value.list.elems[n]); workSet.push_back(startSet->value->list.elems[n]);
/* Get the operator. */ /* Get the operator. */
Bindings::iterator op = Bindings::iterator op =
args[0]->attrs->find(state.symbols.create("operator")); args[0]->attrs->find(state.symbols.create("operator"));
if (op == args[0]->attrs->end()) if (op == args[0]->attrs->end())
throw EvalError("attribute `operator' required"); throw EvalError("attribute `operator' required");
state.forceValue(op->second.value); state.forceValue(*op->value);
/* Construct the closure by applying the operator to element of /* Construct the closure by applying the operator to element of
`workSet', adding the result to `workSet', continuing until `workSet', adding the result to `workSet', continuing until
no new elements are found. */ no new elements are found. */
list<Value> res; list<Value> res;
set<Value, CompareValues> doneKeys; set<Value, CompareValues> doneKeys; // !!! use Value *?
while (!workSet.empty()) { while (!workSet.empty()) {
Value * e = *(workSet.begin()); Value * e = *(workSet.begin());
workSet.pop_front(); workSet.pop_front();
@ -147,15 +147,15 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
e->attrs->find(state.symbols.create("key")); e->attrs->find(state.symbols.create("key"));
if (key == e->attrs->end()) if (key == e->attrs->end())
throw EvalError("attribute `key' required"); throw EvalError("attribute `key' required");
state.forceValue(key->second.value); state.forceValue(*key->value);
if (doneKeys.find(key->second.value) != doneKeys.end()) continue; if (doneKeys.find(*key->value) != doneKeys.end()) continue;
doneKeys.insert(key->second.value); doneKeys.insert(*key->value);
res.push_back(*e); res.push_back(*e);
/* Call the `operator' function with `e' as argument. */ /* Call the `operator' function with `e' as argument. */
Value call; Value call;
mkApp(call, op->second.value, *e); mkApp(call, *op->value, *e);
state.forceList(call); state.forceList(call);
/* Add the values returned by the operator to the work set. */ /* Add the values returned by the operator to the work set. */
@ -167,13 +167,9 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
/* Create the result list. */ /* Create the result list. */
state.mkList(v, res.size()); state.mkList(v, res.size());
Value * vs = state.allocValues(res.size());
unsigned int n = 0; unsigned int n = 0;
foreach (list<Value>::iterator, i, res) { foreach (list<Value>::iterator, i, res)
v.list.elems[n] = &vs[n]; *(v.list.elems[n++] = state.allocValue()) = *i;
vs[n++] = *i;
}
} }
@ -210,15 +206,16 @@ static void prim_addErrorContext(EvalState & state, Value * * args, Value & v)
* else => {success=false; value=false;} */ * else => {success=false; value=false;} */
static void prim_tryEval(EvalState & state, Value * * args, Value & v) static void prim_tryEval(EvalState & state, Value * * args, Value & v)
{ {
state.mkAttrs(v); state.mkAttrs(v, 2);
try { try {
state.forceValue(*args[0]); state.forceValue(*args[0]);
(*v.attrs)[state.symbols.create("value")].value = *args[0]; v.attrs->push_back(Attr(state.symbols.create("value"), args[0]));
mkBool((*v.attrs)[state.symbols.create("success")].value, true); mkBool(*state.allocAttr(v, state.symbols.create("success")), true);
} catch (AssertionError & e) { } catch (AssertionError & e) {
mkBool((*v.attrs)[state.symbols.create("value")].value, false); mkBool(*state.allocAttr(v, state.symbols.create("value")), false);
mkBool((*v.attrs)[state.symbols.create("success")].value, false); mkBool(*state.allocAttr(v, state.symbols.create("success")), false);
} }
v.attrs->sort();
} }
@ -324,9 +321,9 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
if (attr == args[0]->attrs->end()) if (attr == args[0]->attrs->end())
throw EvalError("required attribute `name' missing"); throw EvalError("required attribute `name' missing");
string drvName; string drvName;
Pos & posDrvName(*attr->second.pos); Pos & posDrvName(*attr->pos);
try { try {
drvName = state.forceStringNoCtx(attr->second.value); drvName = state.forceStringNoCtx(*attr->value);
} catch (Error & e) { } catch (Error & e) {
e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") % posDrvName); e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") % posDrvName);
throw; throw;
@ -341,7 +338,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
bool outputHashRecursive = false; bool outputHashRecursive = false;
foreach (Bindings::iterator, i, *args[0]->attrs) { foreach (Bindings::iterator, i, *args[0]->attrs) {
string key = i->first; string key = i->name;
startNest(nest, lvlVomit, format("processing attribute `%1%'") % key); startNest(nest, lvlVomit, format("processing attribute `%1%'") % key);
try { try {
@ -349,9 +346,9 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
/* The `args' attribute is special: it supplies the /* The `args' attribute is special: it supplies the
command-line arguments to the builder. */ command-line arguments to the builder. */
if (key == "args") { if (key == "args") {
state.forceList(i->second.value); state.forceList(*i->value);
for (unsigned int n = 0; n < i->second.value.list.length; ++n) { for (unsigned int n = 0; n < i->value->list.length; ++n) {
string s = state.coerceToString(*i->second.value.list.elems[n], context, true); string s = state.coerceToString(*i->value->list.elems[n], context, true);
drv.args.push_back(s); drv.args.push_back(s);
} }
} }
@ -359,11 +356,11 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
/* All other attributes are passed to the builder through /* All other attributes are passed to the builder through
the environment. */ the environment. */
else { else {
string s = state.coerceToString(i->second.value, context, true); string s = state.coerceToString(*i->value, context, true);
drv.env[key] = s; drv.env[key] = s;
if (key == "builder") drv.builder = s; if (key == "builder") drv.builder = s;
else if (i->first == state.sSystem) drv.platform = s; else if (i->name == state.sSystem) drv.platform = s;
else if (i->first == state.sName) drvName = s; else if (i->name == state.sName) drvName = s;
else if (key == "outputHash") outputHash = s; else if (key == "outputHash") outputHash = s;
else if (key == "outputHashAlgo") outputHashAlgo = s; else if (key == "outputHashAlgo") outputHashAlgo = s;
else if (key == "outputHashMode") { else if (key == "outputHashMode") {
@ -375,7 +372,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
} catch (Error & e) { } catch (Error & e) {
e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n") e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n")
% key % *i->second.pos); % key % *i->pos);
e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n") e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n")
% drvName % posDrvName); % drvName % posDrvName);
throw; throw;
@ -487,9 +484,10 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
state.drvHashes[drvPath] = hashDerivationModulo(state, drv); state.drvHashes[drvPath] = hashDerivationModulo(state, drv);
/* !!! assumes a single output */ /* !!! assumes a single output */
state.mkAttrs(v); state.mkAttrs(v, 2);
mkString((*v.attrs)[state.sOutPath].value, outPath, singleton<PathSet>(drvPath)); mkString(*state.allocAttr(v, state.sOutPath), outPath, singleton<PathSet>(drvPath));
mkString((*v.attrs)[state.sDrvPath].value, drvPath, singleton<PathSet>("=" + drvPath)); mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
v.attrs->sort();
} }
@ -689,17 +687,14 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v)
state.forceAttrs(*args[0]); state.forceAttrs(*args[0]);
state.mkList(v, args[0]->attrs->size()); state.mkList(v, args[0]->attrs->size());
Value * vs = state.allocValues(v.list.length);
StringSet names; StringSet names;
foreach (Bindings::iterator, i, *args[0]->attrs) foreach (Bindings::iterator, i, *args[0]->attrs)
names.insert(i->first); names.insert(i->name);
unsigned int n = 0; unsigned int n = 0;
foreach (StringSet::iterator, i, names) { foreach (StringSet::iterator, i, names)
v.list.elems[n] = &vs[n]; mkString(*(v.list.elems[n++] = state.allocValue()), *i);
mkString(vs[n++], *i);
}
} }
@ -713,8 +708,8 @@ static void prim_getAttr(EvalState & state, Value * * args, Value & v)
if (i == args[1]->attrs->end()) if (i == args[1]->attrs->end())
throw EvalError(format("attribute `%1%' missing") % attr); throw EvalError(format("attribute `%1%' missing") % attr);
// !!! add to stack trace? // !!! add to stack trace?
state.forceValue(i->second.value); state.forceValue(*i->value);
v = i->second.value; v = *i->value;
} }
@ -740,11 +735,20 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v)
state.forceAttrs(*args[0]); state.forceAttrs(*args[0]);
state.forceList(*args[1]); state.forceList(*args[1]);
state.cloneAttrs(*args[0], v); /* Get the attribute names to be removed. */
std::set<Symbol> names;
for (unsigned int i = 0; i < args[1]->list.length; ++i) { for (unsigned int i = 0; i < args[1]->list.length; ++i) {
state.forceStringNoCtx(*args[1]->list.elems[i]); state.forceStringNoCtx(*args[1]->list.elems[i]);
v.attrs->erase(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. Note that we don't need
to sort v.attrs because it's a subset of an already sorted
vector. */
state.mkAttrs(v, args[0]->attrs->size());
foreach (Bindings::iterator, i, *args[0]->attrs) {
if (names.find(i->name) == names.end())
v.attrs->push_back(*i);
} }
} }
@ -757,7 +761,9 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
{ {
state.forceList(*args[0]); state.forceList(*args[0]);
state.mkAttrs(v); state.mkAttrs(v, args[0]->list.length);
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]);
@ -766,16 +772,21 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
Bindings::iterator j = v2.attrs->find(state.sName); Bindings::iterator j = v2.attrs->find(state.sName);
if (j == v2.attrs->end()) if (j == v2.attrs->end())
throw TypeError("`name' attribute missing in a call to `listToAttrs'"); throw TypeError("`name' attribute missing in a call to `listToAttrs'");
string name = state.forceStringNoCtx(j->second.value); string name = state.forceStringNoCtx(*j->value);
j = v2.attrs->find(state.symbols.create("value")); Bindings::iterator j2 = v2.attrs->find(state.symbols.create("value"));
if (j == 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);
mkCopy(a.value, j->second.value); if (seen.find(sym) == seen.end()) {
a.pos = j->second.pos; v.attrs->push_back(Attr(sym, j2->value, j2->pos));
seen.insert(sym);
}
/* !!! Throw an error if `name' already exists? */
} }
v.attrs->sort();
} }
@ -787,15 +798,12 @@ static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v)
state.forceAttrs(*args[0]); state.forceAttrs(*args[0]);
state.forceAttrs(*args[1]); state.forceAttrs(*args[1]);
state.mkAttrs(v); state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
foreach (Bindings::iterator, i, *args[0]->attrs) { foreach (Bindings::iterator, i, *args[0]->attrs) {
Bindings::iterator j = args[1]->attrs->find(i->first); Bindings::iterator j = args[1]->attrs->find(i->name);
if (j != args[1]->attrs->end()) { if (j != args[1]->attrs->end())
Attr & a = (*v.attrs)[j->first]; v.attrs->push_back(*j);
mkCopy(a.value, j->second.value);
a.pos = j->second.pos;
}
} }
} }
@ -819,12 +827,16 @@ static void prim_functionArgs(EvalState & state, Value * * args, Value & v)
if (args[0]->type != tLambda) if (args[0]->type != tLambda)
throw TypeError("`functionArgs' requires a function"); throw TypeError("`functionArgs' requires a function");
state.mkAttrs(v); if (!args[0]->lambda.fun->matchAttrs) {
state.mkAttrs(v, 0);
if (!args[0]->lambda.fun->matchAttrs) return; return;
}
state.mkAttrs(v, args[0]->lambda.fun->formals->formals.size());
foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals) foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals)
mkBool((*v.attrs)[i->name].value, i->def); // !!! should optimise booleans (allocate only once)
mkBool(*state.allocAttr(v, i->name), i->def);
v.attrs->sort();
} }
@ -872,12 +884,10 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
state.forceList(*args[1]); state.forceList(*args[1]);
state.mkList(v, args[1]->list.length); state.mkList(v, args[1]->list.length);
Value * vs = state.allocValues(v.list.length);
for (unsigned int n = 0; n < v.list.length; ++n) { for (unsigned int n = 0; n < v.list.length; ++n)
v.list.elems[n] = &vs[n]; mkApp(*(v.list.elems[n] = state.allocValue()),
mkApp(vs[n], *args[0], *args[1]->list.elems[n]); *args[0], *args[1]->list.elems[n]);
}
} }
@ -1006,9 +1016,10 @@ static void prim_parseDrvName(EvalState & state, Value * * args, Value & v)
{ {
string name = state.forceStringNoCtx(*args[0]); string name = state.forceStringNoCtx(*args[0]);
DrvName parsed(name); DrvName parsed(name);
state.mkAttrs(v); state.mkAttrs(v, 2);
mkString((*v.attrs)[state.sName].value, parsed.name); mkString(*state.allocAttr(v, state.sName), parsed.name);
mkString((*v.attrs)[state.symbols.create("version")].value, parsed.version); mkString(*state.allocAttr(v, state.symbols.create("version")), parsed.version);
v.attrs->sort();
} }
@ -1033,7 +1044,7 @@ void EvalState::createBaseEnv()
Value v; Value v;
/* `builtins' must be first! */ /* `builtins' must be first! */
mkAttrs(v); mkAttrs(v, 128);
addConstant("builtins", v); addConstant("builtins", v);
mkBool(v, true); mkBool(v, true);
@ -1072,7 +1083,7 @@ void EvalState::createBaseEnv()
/* Add a wrapper around the derivation primop that computes the /* Add a wrapper around the derivation primop that computes the
`drvPath' and `outPath' attributes lazily. */ `drvPath' and `outPath' attributes lazily. */
string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }"; string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }";
mkThunk(v, baseEnv, parseExprFromString(*this, s, "/")); mkThunk_(v, parseExprFromString(*this, s, "/"));
addConstant("derivation", v); addConstant("derivation", v);
// Paths // Paths
@ -1121,7 +1132,11 @@ 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

@ -28,6 +28,8 @@ private:
friend class SymbolTable; friend class SymbolTable;
public: public:
Symbol() : s(0) { };
bool operator == (const Symbol & s2) const bool operator == (const Symbol & s2) const
{ {
return s == s2.s; return s == s2.s;

View File

@ -34,10 +34,10 @@ static void showAttrs(EvalState & state, bool strict, bool location,
StringSet names; StringSet names;
foreach (Bindings::iterator, i, attrs) foreach (Bindings::iterator, i, attrs)
names.insert(i->first); 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;
@ -45,7 +45,7 @@ static void showAttrs(EvalState & state, bool strict, bool location,
XMLOpenElement _(doc, "attr", xmlAttrs); XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location, printValueAsXML(state, strict, location,
a.value, doc, context, drvsSeen); *a.value, doc, context, drvsSeen);
} }
} }
@ -90,16 +90,16 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
Path drvPath; Path drvPath;
a = v.attrs->find(state.sDrvPath); a = v.attrs->find(state.sDrvPath);
if (a != v.attrs->end()) { if (a != v.attrs->end()) {
if (strict) state.forceValue(a->second.value); if (strict) state.forceValue(*a->value);
if (a->second.value.type == tString) if (a->value->type == tString)
xmlAttrs["drvPath"] = drvPath = a->second.value.string.s; xmlAttrs["drvPath"] = drvPath = a->value->string.s;
} }
a = v.attrs->find(state.sOutPath); a = v.attrs->find(state.sOutPath);
if (a != v.attrs->end()) { if (a != v.attrs->end()) {
if (strict) state.forceValue(a->second.value); if (strict) state.forceValue(*a->value);
if (a->second.value.type == tString) if (a->value->type == tString)
xmlAttrs["outPath"] = a->second.value.string.s; xmlAttrs["outPath"] = a->value->string.s;
} }
XMLOpenElement _(doc, "derivation", xmlAttrs); XMLOpenElement _(doc, "derivation", xmlAttrs);

View File

@ -2,7 +2,7 @@ pkglib_LTLIBRARIES = libmain.la
libmain_la_SOURCES = shared.cc libmain_la_SOURCES = shared.cc
libmain_la_LIBADD = ../libstore/libstore.la libmain_la_LIBADD = ../libstore/libstore.la @boehmgc_lib@
pkginclude_HEADERS = shared.hh pkginclude_HEADERS = shared.hh

View File

@ -13,6 +13,10 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#if HAVE_BOEHMGC
#include <gc/gc.h>
#endif
namespace nix { namespace nix {
@ -314,6 +318,14 @@ static void setuidInit()
} }
/* Called when the Boehm GC runs out of memory. */
static void * oomHandler(size_t requested)
{
/* Convert this to a proper C++ exception. */
throw std::bad_alloc();
}
} }
@ -335,6 +347,26 @@ int main(int argc, char * * argv)
std::ios::sync_with_stdio(false); std::ios::sync_with_stdio(false);
#if HAVE_BOEHMGC
/* Initialise the Boehm garbage collector. This isn't necessary
on most platforms, but for portability we do it anyway. */
GC_INIT();
GC_oom_fn = oomHandler;
/* Set the initial heap size to something fairly big (384 MiB) so
that in most cases we don't need to garbage collect at all.
(Collection has a fairly significant overhead, some.) The heap
size can be overriden through libgc's GC_INITIAL_HEAP_SIZE
environment variable. We should probably also provide a
nix.conf setting for this. Note that GC_expand_hp() causes a
lot of virtual, but not physical (resident) memory to be
allocated. This might be a problem on systems that don't
overcommit. */
if (!getenv("GC_INITIAL_HEAP_SIZE"))
GC_expand_hp(384 * 1024 * 1024);
#endif
try { try {
try { try {
initAndRun(argc, argv); initAndRun(argc, argv);

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,14 +154,14 @@ 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;
} }
static void loadDerivations(EvalState & state, Path nixExprPath, static void loadDerivations(EvalState & state, Path nixExprPath,
string systemFilter, const Bindings & autoArgs, string systemFilter, Bindings & autoArgs,
const string & pathPrefix, DrvInfos & elems) const string & pathPrefix, DrvInfos & elems)
{ {
Value v; Value v;
@ -321,7 +321,7 @@ static bool isPath(const string & s)
static void queryInstSources(EvalState & state, static void queryInstSources(EvalState & state,
const InstallSourceInfo & instSource, const Strings & args, InstallSourceInfo & instSource, const Strings & args,
DrvInfos & elems, bool newestOnly) DrvInfos & elems, bool newestOnly)
{ {
InstallSourceType type = instSource.type; InstallSourceType type = instSource.type;

View File

@ -25,7 +25,8 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
if (pathExists(manifestFile)) { if (pathExists(manifestFile)) {
Value v; Value v;
state.eval(parseExprFromFile(state, manifestFile), v); state.eval(parseExprFromFile(state, manifestFile), v);
getDerivations(state, v, "", Bindings(), elems); Bindings bindings;
getDerivations(state, v, "", bindings, elems);
} else if (pathExists(oldManifestFile)) } else if (pathExists(oldManifestFile))
readLegacyManifest(oldManifestFile, elems); readLegacyManifest(oldManifestFile, elems);
@ -58,23 +59,24 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
the meta attributes. */ the meta attributes. */
Path drvPath = keepDerivations ? i->queryDrvPath(state) : ""; Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
Value & v(*state.allocValues(1)); Value & v(*state.allocValue());
manifest.list.elems[n++] = &v; manifest.list.elems[n++] = &v;
state.mkAttrs(v); state.mkAttrs(v, 8);
mkString((*v.attrs)[state.sType].value, "derivation"); mkString(*state.allocAttr(v, state.sType), "derivation");
mkString((*v.attrs)[state.sName].value, i->name); mkString(*state.allocAttr(v, state.sName), i->name);
mkString((*v.attrs)[state.sSystem].value, i->system); mkString(*state.allocAttr(v, state.sSystem), i->system);
mkString((*v.attrs)[state.sOutPath].value, i->queryOutPath(state)); mkString(*state.allocAttr(v, state.sOutPath), i->queryOutPath(state));
if (drvPath != "") if (drvPath != "")
mkString((*v.attrs)[state.sDrvPath].value, i->queryDrvPath(state)); mkString(*state.allocAttr(v, state.sDrvPath), i->queryDrvPath(state));
state.mkAttrs((*v.attrs)[state.sMeta].value); Value & vMeta = *state.allocAttr(v, state.sMeta);
state.mkAttrs(vMeta, 16);
MetaInfo meta = i->queryMetaInfo(state); MetaInfo meta = i->queryMetaInfo(state);
foreach (MetaInfo::const_iterator, j, meta) { foreach (MetaInfo::const_iterator, j, meta) {
Value & v2((*(*v.attrs)[state.sMeta].value.attrs)[state.symbols.create(j->first)].value); 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;
@ -82,7 +84,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.mkList(v2, j->second.stringValues.size()); state.mkList(v2, j->second.stringValues.size());
unsigned int m = 0; unsigned int m = 0;
foreach (Strings::const_iterator, k, j->second.stringValues) { foreach (Strings::const_iterator, k, j->second.stringValues) {
v2.list.elems[m] = state.allocValues(1); v2.list.elems[m] = state.allocValue();
mkString(*v2.list.elems[m++], *k); mkString(*v2.list.elems[m++], *k);
} }
break; break;
@ -91,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));
@ -113,11 +118,12 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* Construct a Nix expression that calls the user environment /* Construct a Nix expression that calls the user environment
builder with the manifest as argument. */ builder with the manifest as argument. */
Value args, topLevel; Value args, topLevel;
state.mkAttrs(args); state.mkAttrs(args, 3);
mkString((*args.attrs)[state.sSystem].value, thisSystem); mkString(*state.allocAttr(args, state.sSystem), thisSystem);
mkString((*args.attrs)[state.symbols.create("manifest")].value, 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

@ -38,7 +38,7 @@ static bool indirectRoot = false;
void processExpr(EvalState & state, const Strings & attrPaths, void processExpr(EvalState & state, const Strings & attrPaths,
bool parseOnly, bool strict, const Bindings & autoArgs, bool parseOnly, bool strict, Bindings & autoArgs,
bool evalOnly, bool xmlOutput, bool location, Expr * e) bool evalOnly, bool xmlOutput, bool location, Expr * e)
{ {
if (parseOnly) if (parseOnly)

View File

@ -1 +1 @@
"AA" "AAbar"

View File

@ -7,4 +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")) ];
in concat (map (x: x.a) r.result) x = builtins.listToAttrs [ (asi "foo" "bar") (asi "foo" "bla") ];
in concat (map (x: x.a) r.result) + x.foo

View File

@ -0,0 +1 @@
2

View File

@ -0,0 +1,9 @@
let
overrides = { a = 2; };
in (rec {
__overrides = overrides;
x = a;
a = 1;
}).x