* Merge the GC branch.
This commit is contained in:
		
						commit
						4aced7f8d0
					
				
					 30 changed files with 681 additions and 416 deletions
				
			
		|  | @ -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 | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								configure.ac
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								configure.ac
									
										
									
									
									
								
							|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 evaluator’s 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> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 evaluator’s 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> | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|         ''; |         ''; | ||||||
|       }; |       }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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) { | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
| 
 | 
 | ||||||
|      |      | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
| 
 | 
 | ||||||
|   |   | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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; } | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -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; } | ||||||
|  |  | ||||||
|  | @ -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(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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. */ | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| "AA" | "AAbar" | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-okay-overrides.exp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-okay-overrides.exp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | 2 | ||||||
							
								
								
									
										9
									
								
								tests/lang/eval-okay-overrides.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/lang/eval-okay-overrides.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | let | ||||||
|  | 
 | ||||||
|  |   overrides = { a = 2; }; | ||||||
|  | 
 | ||||||
|  | in (rec { | ||||||
|  |   __overrides = overrides; | ||||||
|  |   x = a; | ||||||
|  |   a = 1; | ||||||
|  | }).x | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue