2003-10-30 16:48:26 +00:00
|
|
|
#include "eval.hh"
|
|
|
|
#include "parser.hh"
|
2006-09-04 21:06:23 +00:00
|
|
|
#include "hash.hh"
|
|
|
|
#include "util.hh"
|
2006-11-30 17:43:04 +00:00
|
|
|
#include "store-api.hh"
|
2006-10-16 15:55:34 +00:00
|
|
|
#include "derivations.hh"
|
2006-12-01 20:51:18 +00:00
|
|
|
#include "globals.hh"
|
2004-02-04 16:03:29 +00:00
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
#include <cstring>
|
|
|
|
|
2010-10-22 13:39:15 +00:00
|
|
|
#if HAVE_BOEHMGC
|
|
|
|
|
2010-10-20 11:38:30 +00:00
|
|
|
#include <gc/gc.h>
|
|
|
|
#include <gc/gc_cpp.h>
|
|
|
|
|
2010-10-24 00:41:29 +00:00
|
|
|
#define NEW new (UseGC)
|
2010-10-23 22:58:24 +00:00
|
|
|
|
2010-10-22 13:39:15 +00:00
|
|
|
#else
|
|
|
|
|
|
|
|
#define GC_STRDUP strdup
|
|
|
|
#define GC_MALLOC malloc
|
|
|
|
|
2010-10-23 22:58:24 +00:00
|
|
|
#define NEW new
|
|
|
|
|
2010-10-22 13:39:15 +00:00
|
|
|
#endif
|
|
|
|
|
2004-02-04 16:03:29 +00:00
|
|
|
|
2007-02-27 19:10:45 +00:00
|
|
|
#define LocalNoInline(f) static f __attribute__((noinline)); f
|
|
|
|
#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
|
|
|
|
|
|
|
|
|
2006-09-04 21:06:23 +00:00
|
|
|
namespace nix {
|
2010-10-24 00:41:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
Bindings::iterator Bindings::find(const Symbol & name)
|
|
|
|
{
|
2010-10-24 19:52:33 +00:00
|
|
|
Attr key(name, 0);
|
|
|
|
iterator i = lower_bound(begin(), end(), key);
|
|
|
|
if (i != end() && i->name == name) return i;
|
|
|
|
return end();
|
2010-10-24 00:41:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 19:52:33 +00:00
|
|
|
void Bindings::sort()
|
2010-10-24 00:41:29 +00:00
|
|
|
{
|
2010-10-24 19:52:33 +00:00
|
|
|
std::sort(begin(), end());
|
2010-10-24 00:41:29 +00:00
|
|
|
}
|
2006-09-04 21:06:23 +00:00
|
|
|
|
|
|
|
|
2010-05-18 10:36:37 +00:00
|
|
|
std::ostream & operator << (std::ostream & str, const Value & v)
|
2003-10-30 16:48:26 +00:00
|
|
|
{
|
2010-03-29 14:37:56 +00:00
|
|
|
switch (v.type) {
|
|
|
|
case tInt:
|
|
|
|
str << v.integer;
|
|
|
|
break;
|
|
|
|
case tBool:
|
|
|
|
str << (v.boolean ? "true" : "false");
|
|
|
|
break;
|
|
|
|
case tString:
|
2010-04-08 11:41:19 +00:00
|
|
|
str << "\"";
|
|
|
|
for (const char * i = v.string.s; *i; i++)
|
|
|
|
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
|
|
|
else if (*i == '\n') str << "\\n";
|
|
|
|
else if (*i == '\r') str << "\\r";
|
|
|
|
else if (*i == '\t') str << "\\t";
|
|
|
|
else str << *i;
|
|
|
|
str << "\"";
|
2010-03-29 14:37:56 +00:00
|
|
|
break;
|
2010-03-30 09:22:33 +00:00
|
|
|
case tPath:
|
|
|
|
str << v.path; // !!! escaping?
|
|
|
|
break;
|
2010-03-29 14:37:56 +00:00
|
|
|
case tNull:
|
2010-08-27 12:10:56 +00:00
|
|
|
str << "null";
|
2010-03-29 14:37:56 +00:00
|
|
|
break;
|
2010-05-12 12:15:49 +00:00
|
|
|
case tAttrs: {
|
2010-03-29 14:37:56 +00:00
|
|
|
str << "{ ";
|
2010-05-12 12:15:49 +00:00
|
|
|
typedef std::map<string, Value *> Sorted;
|
|
|
|
Sorted sorted;
|
2010-04-14 22:59:39 +00:00
|
|
|
foreach (Bindings::iterator, i, *v.attrs)
|
2010-10-24 00:41:29 +00:00
|
|
|
sorted[i->name] = i->value;
|
2010-05-12 12:15:49 +00:00
|
|
|
foreach (Sorted::iterator, i, sorted)
|
|
|
|
str << i->first << " = " << *i->second << "; ";
|
2010-03-29 14:37:56 +00:00
|
|
|
str << "}";
|
|
|
|
break;
|
2010-05-12 12:15:49 +00:00
|
|
|
}
|
2010-03-29 14:37:56 +00:00
|
|
|
case tList:
|
|
|
|
str << "[ ";
|
|
|
|
for (unsigned int n = 0; n < v.list.length; ++n)
|
2010-04-15 00:37:36 +00:00
|
|
|
str << *v.list.elems[n] << " ";
|
2010-03-29 14:37:56 +00:00
|
|
|
str << "]";
|
|
|
|
break;
|
|
|
|
case tThunk:
|
2010-06-02 09:43:04 +00:00
|
|
|
case tApp:
|
2010-03-29 14:37:56 +00:00
|
|
|
str << "<CODE>";
|
|
|
|
break;
|
|
|
|
case tLambda:
|
|
|
|
str << "<LAMBDA>";
|
|
|
|
break;
|
|
|
|
case tPrimOp:
|
|
|
|
str << "<PRIMOP>";
|
|
|
|
break;
|
|
|
|
case tPrimOpApp:
|
|
|
|
str << "<PRIMOP-APP>";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw Error("invalid value");
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
2004-02-04 16:03:29 +00:00
|
|
|
|
2004-10-26 22:54:26 +00:00
|
|
|
|
2010-04-21 15:57:11 +00:00
|
|
|
string showType(const Value & v)
|
2010-03-29 14:37:56 +00:00
|
|
|
{
|
|
|
|
switch (v.type) {
|
|
|
|
case tInt: return "an integer";
|
|
|
|
case tBool: return "a boolean";
|
2010-04-01 10:55:36 +00:00
|
|
|
case tString: return "a string";
|
|
|
|
case tPath: return "a path";
|
2010-04-21 15:57:11 +00:00
|
|
|
case tNull: return "null";
|
2010-03-29 14:37:56 +00:00
|
|
|
case tAttrs: return "an attribute set";
|
|
|
|
case tList: return "a list";
|
2010-04-21 15:57:11 +00:00
|
|
|
case tThunk: return "a thunk";
|
|
|
|
case tApp: return "a function application";
|
2010-04-01 10:55:36 +00:00
|
|
|
case tLambda: return "a function";
|
2010-04-21 15:57:11 +00:00
|
|
|
case tBlackhole: return "a black hole";
|
2010-04-01 10:55:36 +00:00
|
|
|
case tPrimOp: return "a built-in function";
|
2010-03-29 14:37:56 +00:00
|
|
|
case tPrimOpApp: return "a partially applied built-in function";
|
|
|
|
}
|
2010-04-21 15:57:11 +00:00
|
|
|
abort();
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
2009-05-12 11:06:24 +00:00
|
|
|
|
2010-04-13 12:25:42 +00:00
|
|
|
EvalState::EvalState()
|
|
|
|
: sWith(symbols.create("<with>"))
|
|
|
|
, sOutPath(symbols.create("outPath"))
|
|
|
|
, sDrvPath(symbols.create("drvPath"))
|
|
|
|
, sType(symbols.create("type"))
|
|
|
|
, sMeta(symbols.create("meta"))
|
|
|
|
, sName(symbols.create("name"))
|
2010-04-21 15:08:58 +00:00
|
|
|
, sSystem(symbols.create("system"))
|
2010-05-15 08:10:12 +00:00
|
|
|
, sOverrides(symbols.create("__overrides"))
|
2010-04-14 14:42:32 +00:00
|
|
|
, baseEnv(allocEnv(128))
|
2010-04-14 22:59:39 +00:00
|
|
|
, baseEnvDispl(0)
|
|
|
|
, staticBaseEnv(false, 0)
|
2010-03-29 14:37:56 +00:00
|
|
|
{
|
2010-04-15 00:37:36 +00:00
|
|
|
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
|
2010-04-14 23:48:46 +00:00
|
|
|
nrEvaluated = recursionDepth = maxRecursionDepth = 0;
|
2010-10-20 15:48:00 +00:00
|
|
|
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
2010-04-09 12:00:49 +00:00
|
|
|
deepestStack = (char *) -1;
|
2010-03-29 14:37:56 +00:00
|
|
|
|
|
|
|
createBaseEnv();
|
|
|
|
|
2009-05-12 11:06:24 +00:00
|
|
|
allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
|
2011-02-09 22:59:50 +00:00
|
|
|
|
|
|
|
#if HAVE_BOEHMGC
|
|
|
|
static bool gcInitialised = true;
|
|
|
|
if (gcInitialised) {
|
|
|
|
/* 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.) 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);
|
|
|
|
gcInitialised = true;
|
|
|
|
}
|
|
|
|
#endif
|
2004-02-04 16:03:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-09 12:00:49 +00:00
|
|
|
EvalState::~EvalState()
|
|
|
|
{
|
|
|
|
assert(recursionDepth == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-30 14:39:27 +00:00
|
|
|
void EvalState::addConstant(const string & name, Value & v)
|
|
|
|
{
|
2010-10-22 14:47:42 +00:00
|
|
|
Value * v2 = allocValue();
|
|
|
|
*v2 = v;
|
2010-04-14 22:59:39 +00:00
|
|
|
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
2010-10-22 15:51:52 +00:00
|
|
|
baseEnv.values[baseEnvDispl++] = v2;
|
2010-03-30 14:39:27 +00:00
|
|
|
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
|
2010-10-24 00:41:29 +00:00
|
|
|
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v2));
|
2010-03-30 14:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-04 10:59:20 +00:00
|
|
|
void EvalState::addPrimOp(const string & name,
|
2010-10-23 20:07:47 +00:00
|
|
|
unsigned int arity, PrimOpFun primOp)
|
2004-02-04 16:03:29 +00:00
|
|
|
{
|
2010-10-22 14:47:42 +00:00
|
|
|
Value * v = allocValue();
|
2010-04-21 15:08:58 +00:00
|
|
|
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
|
2010-10-24 19:52:33 +00:00
|
|
|
Symbol sym = symbols.create(name2);
|
2010-10-22 14:47:42 +00:00
|
|
|
v->type = tPrimOp;
|
2010-10-23 22:58:24 +00:00
|
|
|
v->primOp = NEW PrimOp(primOp, arity, sym);
|
2010-10-24 19:52:33 +00:00
|
|
|
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
2010-10-22 15:51:52 +00:00
|
|
|
baseEnv.values[baseEnvDispl++] = v;
|
2010-10-24 19:52:33 +00:00
|
|
|
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
2003-10-31 17:09:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-02-27 19:10:45 +00:00
|
|
|
/* Every "format" object (even temporary) takes up a few hundred bytes
|
|
|
|
of stack space, which is a real killer in the recursive
|
|
|
|
evaluator. So here are some helper functions for throwing
|
|
|
|
exceptions. */
|
|
|
|
|
|
|
|
LocalNoInlineNoReturn(void throwEvalError(const char * s))
|
|
|
|
{
|
|
|
|
throw EvalError(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
|
|
|
{
|
|
|
|
throw EvalError(format(s) % s2);
|
|
|
|
}
|
|
|
|
|
2010-04-09 12:00:49 +00:00
|
|
|
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
|
|
|
|
{
|
|
|
|
throw EvalError(format(s) % s2 % s3);
|
|
|
|
}
|
|
|
|
|
2008-07-24 14:52:25 +00:00
|
|
|
LocalNoInlineNoReturn(void throwTypeError(const char * s))
|
|
|
|
{
|
|
|
|
throw TypeError(s);
|
|
|
|
}
|
|
|
|
|
2007-02-27 19:10:45 +00:00
|
|
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
|
|
|
|
{
|
|
|
|
throw TypeError(format(s) % s2);
|
|
|
|
}
|
|
|
|
|
2010-04-12 23:33:23 +00:00
|
|
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2))
|
|
|
|
{
|
|
|
|
throw TypeError(format(s) % pos % s2);
|
|
|
|
}
|
|
|
|
|
|
|
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
|
|
|
|
{
|
|
|
|
throw TypeError(format(s) % pos);
|
|
|
|
}
|
|
|
|
|
2010-04-12 21:21:24 +00:00
|
|
|
LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
|
2010-04-09 12:00:49 +00:00
|
|
|
{
|
2010-04-12 21:21:24 +00:00
|
|
|
throw AssertionError(format(s) % pos);
|
2010-04-09 12:00:49 +00:00
|
|
|
}
|
|
|
|
|
2007-02-27 19:10:45 +00:00
|
|
|
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
|
|
|
|
{
|
|
|
|
e.addPrefix(format(s) % s2);
|
|
|
|
}
|
|
|
|
|
2010-04-12 23:33:23 +00:00
|
|
|
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos))
|
|
|
|
{
|
|
|
|
e.addPrefix(format(s) % pos);
|
|
|
|
}
|
|
|
|
|
2010-05-07 12:11:05 +00:00
|
|
|
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos))
|
|
|
|
{
|
|
|
|
e.addPrefix(format(s) % s2 % pos);
|
|
|
|
}
|
|
|
|
|
2007-02-27 19:10:45 +00:00
|
|
|
|
2010-03-30 18:05:54 +00:00
|
|
|
void mkString(Value & v, const char * s)
|
|
|
|
{
|
|
|
|
v.type = tString;
|
2010-10-22 13:39:15 +00:00
|
|
|
v.string.s = GC_STRDUP(s);
|
2010-03-30 18:05:54 +00:00
|
|
|
v.string.context = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void mkString(Value & v, const string & s, const PathSet & context)
|
|
|
|
{
|
|
|
|
mkString(v, s.c_str());
|
2010-03-31 19:52:29 +00:00
|
|
|
if (!context.empty()) {
|
2010-04-01 09:55:57 +00:00
|
|
|
unsigned int n = 0;
|
2010-10-28 12:50:01 +00:00
|
|
|
v.string.context = (const char * *)
|
|
|
|
GC_MALLOC((context.size() + 1) * sizeof(char *));
|
2010-10-28 12:29:40 +00:00
|
|
|
foreach (PathSet::const_iterator, i, context)
|
2010-10-22 13:39:15 +00:00
|
|
|
v.string.context[n++] = GC_STRDUP(i->c_str());
|
2010-03-31 19:52:29 +00:00
|
|
|
v.string.context[n] = 0;
|
|
|
|
}
|
2010-03-30 18:05:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-23 21:11:59 +00:00
|
|
|
void mkString(Value & v, const Symbol & s)
|
|
|
|
{
|
|
|
|
v.type = tString;
|
|
|
|
v.string.s = ((string) s).c_str();
|
|
|
|
v.string.context = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-30 18:05:54 +00:00
|
|
|
void mkPath(Value & v, const char * s)
|
|
|
|
{
|
2010-10-23 20:07:47 +00:00
|
|
|
clearValue(v);
|
2010-03-30 18:05:54 +00:00
|
|
|
v.type = tPath;
|
2010-10-22 13:39:15 +00:00
|
|
|
v.path = GC_STRDUP(s);
|
2010-03-30 18:05:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 15:14:23 +00:00
|
|
|
Value * EvalState::lookupVar(Env * env, const VarRef & var)
|
2010-03-29 14:37:56 +00:00
|
|
|
{
|
2010-04-14 15:14:23 +00:00
|
|
|
for (unsigned int l = var.level; l; --l, env = env->up) ;
|
|
|
|
|
|
|
|
if (var.fromWith) {
|
2010-04-22 15:08:09 +00:00
|
|
|
while (1) {
|
2010-10-22 15:51:52 +00:00
|
|
|
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
|
|
|
if (j != env->values[0]->attrs->end())
|
2010-10-24 00:41:29 +00:00
|
|
|
return j->value;
|
2010-04-22 15:08:09 +00:00
|
|
|
if (env->prevWith == 0)
|
|
|
|
throwEvalError("undefined variable `%1%'", var.name);
|
|
|
|
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
|
|
|
}
|
2010-04-14 15:14:23 +00:00
|
|
|
} else
|
2010-10-22 15:51:52 +00:00
|
|
|
return env->values[var.displ];
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-22 14:47:42 +00:00
|
|
|
Value * EvalState::allocValue()
|
|
|
|
{
|
|
|
|
nrValues++;
|
2010-10-28 12:50:01 +00:00
|
|
|
return (Value *) GC_MALLOC(sizeof(Value));
|
2010-10-22 14:47:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:42:32 +00:00
|
|
|
Env & EvalState::allocEnv(unsigned int size)
|
2010-03-29 14:37:56 +00:00
|
|
|
{
|
|
|
|
nrEnvs++;
|
2010-04-14 23:48:46 +00:00
|
|
|
nrValuesInEnvs += size;
|
2010-10-22 15:51:52 +00:00
|
|
|
Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
|
2010-10-24 14:20:02 +00:00
|
|
|
|
|
|
|
/* Clear the values because maybeThunk() expects this. */
|
|
|
|
for (unsigned i = 0; i < size; ++i)
|
|
|
|
env->values[i] = 0;
|
|
|
|
|
2010-04-14 14:42:32 +00:00
|
|
|
return *env;
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-22 14:47:42 +00:00
|
|
|
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
|
|
|
{
|
2010-10-24 00:41:29 +00:00
|
|
|
Value * v = allocValue();
|
|
|
|
vAttrs.attrs->push_back(Attr(name, v));
|
|
|
|
return v;
|
2010-10-22 14:47:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-30 14:39:27 +00:00
|
|
|
void EvalState::mkList(Value & v, unsigned int length)
|
|
|
|
{
|
|
|
|
v.type = tList;
|
|
|
|
v.list.length = length;
|
2010-10-28 12:50:01 +00:00
|
|
|
v.list.elems = (Value * *) GC_MALLOC(length * sizeof(Value *));
|
2010-04-15 00:37:36 +00:00
|
|
|
nrListElems += length;
|
2010-03-30 14:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 20:09:37 +00:00
|
|
|
void EvalState::mkAttrs(Value & v, unsigned int expected)
|
2010-03-30 22:39:48 +00:00
|
|
|
{
|
2010-10-23 20:07:47 +00:00
|
|
|
clearValue(v);
|
2010-03-30 22:39:48 +00:00
|
|
|
v.type = tAttrs;
|
2010-10-23 22:58:24 +00:00
|
|
|
v.attrs = NEW Bindings;
|
2010-10-24 20:09:37 +00:00
|
|
|
v.attrs->reserve(expected);
|
2010-10-20 15:48:00 +00:00
|
|
|
nrAttrsets++;
|
2010-03-30 22:39:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 14:20:02 +00:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void EvalState::mkThunk_(Value & v, Expr * expr)
|
2010-04-07 15:47:06 +00:00
|
|
|
{
|
|
|
|
mkThunk(v, baseEnv, expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 14:20:02 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
ExprVar * var;
|
|
|
|
/* Ignore variables from `withs' because they can throw an
|
|
|
|
exception. */
|
|
|
|
if ((var = dynamic_cast<ExprVar *>(expr))) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-30 09:22:33 +00:00
|
|
|
void EvalState::evalFile(const Path & path, Value & v)
|
|
|
|
{
|
|
|
|
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
|
2010-03-31 16:14:32 +00:00
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
Expr * e = parseTrees[path];
|
2010-03-31 16:14:32 +00:00
|
|
|
|
|
|
|
if (!e) {
|
2010-04-13 12:25:42 +00:00
|
|
|
e = parseExprFromFile(*this, path);
|
2010-04-12 18:30:11 +00:00
|
|
|
parseTrees[path] = e;
|
2010-03-31 16:14:32 +00:00
|
|
|
}
|
|
|
|
|
2010-03-30 09:22:33 +00:00
|
|
|
try {
|
|
|
|
eval(e, v);
|
|
|
|
} catch (Error & e) {
|
2010-04-09 12:00:49 +00:00
|
|
|
addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
|
2010-03-30 09:22:33 +00:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-09 12:00:49 +00:00
|
|
|
struct RecursionCounter
|
|
|
|
{
|
|
|
|
EvalState & state;
|
|
|
|
RecursionCounter(EvalState & state) : state(state)
|
|
|
|
{
|
|
|
|
state.recursionDepth++;
|
|
|
|
if (state.recursionDepth > state.maxRecursionDepth)
|
|
|
|
state.maxRecursionDepth = state.recursionDepth;
|
|
|
|
}
|
|
|
|
~RecursionCounter()
|
|
|
|
{
|
|
|
|
state.recursionDepth--;
|
|
|
|
}
|
|
|
|
};
|
2010-03-29 14:37:56 +00:00
|
|
|
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void EvalState::eval(Env & env, Expr * e, Value & v)
|
2010-03-29 14:37:56 +00:00
|
|
|
{
|
|
|
|
/* When changing this function, make sure that you don't cause a
|
|
|
|
(large) increase in stack consumption! */
|
2010-04-09 12:00:49 +00:00
|
|
|
|
|
|
|
/* !!! Disable this eventually. */
|
|
|
|
RecursionCounter r(*this);
|
2010-03-29 14:37:56 +00:00
|
|
|
char x;
|
|
|
|
if (&x < deepestStack) deepestStack = &x;
|
|
|
|
|
2010-04-14 23:25:05 +00:00
|
|
|
//debug(format("eval: %1%") % *e);
|
2010-03-29 14:37:56 +00:00
|
|
|
|
2010-04-07 13:55:46 +00:00
|
|
|
checkInterrupt();
|
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
nrEvaluated++;
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
e->eval(*this, env, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EvalState::eval(Expr * e, Value & v)
|
|
|
|
{
|
|
|
|
eval(baseEnv, e, v);
|
|
|
|
}
|
2010-03-29 14:37:56 +00:00
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
|
|
|
|
bool EvalState::evalBool(Env & env, Expr * e)
|
|
|
|
{
|
|
|
|
Value v;
|
|
|
|
eval(env, e, v);
|
|
|
|
if (v.type != tBool)
|
|
|
|
throwTypeError("value is %1% while a Boolean was expected", showType(v));
|
|
|
|
return v.boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-16 15:13:47 +00:00
|
|
|
void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
|
|
|
|
{
|
|
|
|
eval(env, e, v);
|
|
|
|
if (v.type != tAttrs)
|
|
|
|
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 22:03:27 +00:00
|
|
|
void Expr::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void ExprInt::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
mkInt(v, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprString::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
2010-10-23 21:11:59 +00:00
|
|
|
mkString(v, s);
|
2010-04-12 18:30:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprPath::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
mkPath(v, s.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
2010-10-24 20:09:37 +00:00
|
|
|
state.mkAttrs(v, attrs.size());
|
2010-04-14 23:25:05 +00:00
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
if (recursive) {
|
|
|
|
/* Create a new environment that contains the attributes in
|
|
|
|
this `rec'. */
|
2010-10-24 19:52:33 +00:00
|
|
|
Env & env2(state.allocEnv(attrs.size()));
|
2010-04-12 18:30:11 +00:00
|
|
|
env2.up = &env;
|
|
|
|
|
2010-10-24 19:52:33 +00:00
|
|
|
AttrDefs::iterator overrides = attrs.find(state.sOverrides);
|
|
|
|
bool hasOverrides = overrides != attrs.end();
|
|
|
|
|
|
|
|
/* The recursive attributes are evaluated in the new
|
|
|
|
environment, while the inherited attributes are evaluated
|
|
|
|
in the original environment. */
|
2010-04-14 14:42:32 +00:00
|
|
|
unsigned int displ = 0;
|
2010-10-24 19:52:33 +00:00
|
|
|
foreach (AttrDefs::iterator, i, attrs)
|
|
|
|
if (i->second.inherited) {
|
|
|
|
/* !!! handle overrides? */
|
|
|
|
Value * vAttr = state.lookupVar(&env, i->second.var);
|
|
|
|
env2.values[displ++] = vAttr;
|
|
|
|
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
|
|
|
} else {
|
|
|
|
Value * vAttr;
|
|
|
|
if (hasOverrides) {
|
|
|
|
vAttr = state.allocValue();
|
|
|
|
mkThunk(*vAttr, env2, i->second.e);
|
|
|
|
} else
|
|
|
|
vAttr = state.maybeThunk(env2, i->second.e);
|
|
|
|
env2.values[displ++] = vAttr;
|
|
|
|
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
|
|
|
}
|
2010-04-14 14:42:32 +00:00
|
|
|
|
2010-05-15 08:10:12 +00:00
|
|
|
/* If the rec contains an attribute called `__overrides', then
|
|
|
|
evaluate it, and add the attributes in that set to the rec.
|
|
|
|
This allows overriding of recursive attributes, which is
|
|
|
|
otherwise not possible. (You can use the // operator to
|
|
|
|
replace an attribute, but other attributes in the rec will
|
|
|
|
still reference the original value, because that value has
|
|
|
|
been substituted into the bodies of the other attributes.
|
|
|
|
Hence we need __overrides.) */
|
2010-10-24 19:52:33 +00:00
|
|
|
if (hasOverrides) {
|
|
|
|
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
|
|
|
state.forceAttrs(*vOverrides);
|
|
|
|
foreach (Bindings::iterator, i, *vOverrides->attrs) {
|
|
|
|
AttrDefs::iterator j = attrs.find(i->name);
|
|
|
|
if (j != attrs.end()) {
|
|
|
|
(*v.attrs)[j->second.displ] = *i;
|
|
|
|
env2.values[j->second.displ] = i->value;
|
|
|
|
} else
|
|
|
|
v.attrs->push_back(*i);
|
2010-05-15 08:10:12 +00:00
|
|
|
}
|
2010-10-24 19:52:33 +00:00
|
|
|
v.attrs->sort();
|
2010-05-15 08:10:12 +00:00
|
|
|
}
|
2010-04-12 18:30:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
2010-10-24 19:52:33 +00:00
|
|
|
foreach (AttrDefs::iterator, i, attrs)
|
|
|
|
if (i->second.inherited)
|
|
|
|
v.attrs->push_back(Attr(i->first, state.lookupVar(&env, i->second.var), &i->second.pos));
|
|
|
|
else
|
|
|
|
v.attrs->push_back(Attr(i->first, state.maybeThunk(env, i->second.e), &i->second.pos));
|
2010-04-12 18:30:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-13 13:42:25 +00:00
|
|
|
void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
/* Create a new environment that contains the attributes in this
|
|
|
|
`let'. */
|
2010-10-24 19:52:33 +00:00
|
|
|
Env & env2(state.allocEnv(attrs->attrs.size()));
|
2010-04-13 13:42:25 +00:00
|
|
|
env2.up = &env;
|
2010-04-14 14:42:32 +00:00
|
|
|
|
2010-10-24 19:52:33 +00:00
|
|
|
/* The recursive attributes are evaluated in the new environment,
|
|
|
|
while the inherited attributes are evaluated in the original
|
2010-04-13 13:42:25 +00:00
|
|
|
environment. */
|
2010-10-24 19:52:33 +00:00
|
|
|
unsigned int displ = 0;
|
|
|
|
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
|
|
|
if (i->second.inherited)
|
|
|
|
env2.values[displ++] = state.lookupVar(&env, i->second.var);
|
|
|
|
else
|
|
|
|
env2.values[displ++] = state.maybeThunk(env2, i->second.e);
|
2010-04-13 13:42:25 +00:00
|
|
|
|
|
|
|
state.eval(env2, body, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
state.mkList(v, elems.size());
|
2010-10-23 18:18:07 +00:00
|
|
|
for (unsigned int n = 0; n < v.list.length; ++n)
|
2010-10-24 14:20:02 +00:00
|
|
|
v.list.elems[n] = state.maybeThunk(env, elems[n]);
|
2010-04-12 18:30:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
2010-04-14 15:14:23 +00:00
|
|
|
Value * v2 = state.lookupVar(&env, info);
|
|
|
|
state.forceValue(*v2);
|
|
|
|
v = *v2;
|
2010-04-12 18:30:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 14:20:02 +00:00
|
|
|
unsigned long nrLookups = 0;
|
|
|
|
unsigned long nrLookupSize = 0;
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
2010-10-24 14:20:02 +00:00
|
|
|
nrLookups++;
|
2010-04-12 18:30:11 +00:00
|
|
|
Value v2;
|
2010-04-16 15:13:47 +00:00
|
|
|
state.evalAttrs(env, e, v2);
|
2010-10-24 14:20:02 +00:00
|
|
|
nrLookupSize += v2.attrs->size();
|
2010-04-12 18:30:11 +00:00
|
|
|
Bindings::iterator i = v2.attrs->find(name);
|
|
|
|
if (i == v2.attrs->end())
|
|
|
|
throwEvalError("attribute `%1%' missing", name);
|
|
|
|
try {
|
2010-10-24 00:41:29 +00:00
|
|
|
state.forceValue(*i->value);
|
2010-04-12 18:30:11 +00:00
|
|
|
} catch (Error & e) {
|
2010-05-07 12:11:05 +00:00
|
|
|
addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
|
2010-10-24 00:41:29 +00:00
|
|
|
name, *i->pos);
|
2010-04-12 18:30:11 +00:00
|
|
|
throw;
|
|
|
|
}
|
2010-10-24 00:41:29 +00:00
|
|
|
v = *i->value;
|
2010-04-12 18:30:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 21:21:24 +00:00
|
|
|
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
Value vAttrs;
|
2010-04-16 15:13:47 +00:00
|
|
|
state.evalAttrs(env, e, vAttrs);
|
2010-04-12 21:21:24 +00:00
|
|
|
mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void ExprLambda::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
v.type = tLambda;
|
|
|
|
v.lambda.env = &env;
|
|
|
|
v.lambda.fun = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprApp::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
Value vFun;
|
|
|
|
state.eval(env, e1, vFun);
|
2010-10-24 14:20:02 +00:00
|
|
|
state.callFunction(vFun, *state.maybeThunk(env, e2), v);
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-30 13:47:59 +00:00
|
|
|
void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
|
|
|
{
|
|
|
|
if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
|
2010-10-23 20:07:47 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2010-03-30 13:47:59 +00:00
|
|
|
if (argsLeft == 1) {
|
2010-10-23 20:07:47 +00:00
|
|
|
/* We have all the arguments, so call the primop. */
|
2010-03-30 13:47:59 +00:00
|
|
|
|
|
|
|
/* Put all the arguments in an array. */
|
|
|
|
Value * vArgs[arity];
|
|
|
|
unsigned int n = arity - 1;
|
|
|
|
vArgs[n--] = &arg;
|
|
|
|
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left)
|
|
|
|
vArgs[n--] = arg->primOpApp.right;
|
|
|
|
|
|
|
|
/* And call the primop. */
|
2010-04-21 15:08:58 +00:00
|
|
|
try {
|
2010-10-23 20:07:47 +00:00
|
|
|
primOp->primOp->fun(*this, vArgs, v);
|
2010-04-21 15:08:58 +00:00
|
|
|
} catch (Error & e) {
|
2010-10-23 20:07:47 +00:00
|
|
|
addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp->name);
|
2010-04-21 15:08:58 +00:00
|
|
|
throw;
|
|
|
|
}
|
2010-03-30 13:47:59 +00:00
|
|
|
} else {
|
|
|
|
v.type = tPrimOpApp;
|
2010-10-23 18:18:07 +00:00
|
|
|
v.primOpApp.left = allocValue();
|
|
|
|
*v.primOpApp.left = fun;
|
|
|
|
v.primOpApp.right = &arg;
|
2010-03-30 13:47:59 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fun.type != tLambda)
|
|
|
|
throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
|
|
|
|
showType(fun));
|
|
|
|
|
2010-04-14 14:42:32 +00:00
|
|
|
unsigned int size =
|
|
|
|
(fun.lambda.fun->arg.empty() ? 0 : 1) +
|
|
|
|
(fun.lambda.fun->matchAttrs ? fun.lambda.fun->formals->formals.size() : 0);
|
|
|
|
Env & env2(allocEnv(size));
|
2010-03-30 13:47:59 +00:00
|
|
|
env2.up = fun.lambda.env;
|
|
|
|
|
2010-04-14 14:42:32 +00:00
|
|
|
unsigned int displ = 0;
|
|
|
|
|
|
|
|
if (!fun.lambda.fun->matchAttrs)
|
2010-10-22 15:51:52 +00:00
|
|
|
env2.values[displ++] = &arg;
|
2010-03-30 13:47:59 +00:00
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
else {
|
2010-03-30 13:47:59 +00:00
|
|
|
forceAttrs(arg);
|
|
|
|
|
2010-04-14 14:42:32 +00:00
|
|
|
if (!fun.lambda.fun->arg.empty())
|
2010-10-22 15:51:52 +00:00
|
|
|
env2.values[displ++] = &arg;
|
2010-03-30 13:47:59 +00:00
|
|
|
|
|
|
|
/* For each formal argument, get the actual argument. If
|
|
|
|
there is no matching actual argument but the formal
|
|
|
|
argument has a default, use the default. */
|
|
|
|
unsigned int attrsUsed = 0;
|
2010-04-12 18:30:11 +00:00
|
|
|
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
|
|
|
Bindings::iterator j = arg.attrs->find(i->name);
|
2010-03-30 13:47:59 +00:00
|
|
|
if (j == arg.attrs->end()) {
|
2010-04-12 23:33:23 +00:00
|
|
|
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
|
2010-10-22 15:51:52 +00:00
|
|
|
fun.lambda.fun->pos, i->name);
|
2010-10-24 14:20:02 +00:00
|
|
|
env2.values[displ++] = maybeThunk(env2, i->def);
|
2010-03-30 13:47:59 +00:00
|
|
|
} else {
|
|
|
|
attrsUsed++;
|
2010-10-24 00:41:29 +00:00
|
|
|
env2.values[displ++] = j->value;
|
2010-03-30 13:47:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that each actual argument is listed as a formal
|
|
|
|
argument (unless the attribute match specifies a `...').
|
|
|
|
TODO: show the names of the expected/unexpected
|
|
|
|
arguments. */
|
2010-10-24 19:52:33 +00:00
|
|
|
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size())
|
2010-04-12 23:33:23 +00:00
|
|
|
throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
|
2010-03-30 13:47:59 +00:00
|
|
|
}
|
|
|
|
|
2010-04-12 23:33:23 +00:00
|
|
|
try {
|
|
|
|
eval(env2, fun.lambda.fun->body, v);
|
|
|
|
} catch (Error & e) {
|
|
|
|
addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
|
|
|
|
throw;
|
|
|
|
}
|
2010-03-30 13:47:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-22 14:47:42 +00:00
|
|
|
void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
2010-04-07 15:47:06 +00:00
|
|
|
{
|
|
|
|
forceValue(fun);
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
if (fun.type != tLambda || !fun.lambda.fun->matchAttrs) {
|
2010-04-07 15:47:06 +00:00
|
|
|
res = fun;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Value actualArgs;
|
2010-10-24 20:09:37 +00:00
|
|
|
mkAttrs(actualArgs, fun.lambda.fun->formals->formals.size());
|
2010-04-12 18:30:11 +00:00
|
|
|
|
|
|
|
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
2010-10-22 14:47:42 +00:00
|
|
|
Bindings::iterator j = args.find(i->name);
|
2010-04-07 15:47:06 +00:00
|
|
|
if (j != args.end())
|
2010-10-24 19:52:33 +00:00
|
|
|
actualArgs.attrs->push_back(*j);
|
2010-04-12 18:30:11 +00:00
|
|
|
else if (!i->def)
|
|
|
|
throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name);
|
2010-04-07 15:47:06 +00:00
|
|
|
}
|
|
|
|
|
2010-10-24 19:52:33 +00:00
|
|
|
actualArgs.attrs->sort();
|
|
|
|
|
2010-04-07 15:47:06 +00:00
|
|
|
callFunction(fun, actualArgs, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
2010-03-29 14:37:56 +00:00
|
|
|
{
|
2010-04-14 15:01:04 +00:00
|
|
|
Env & env2(state.allocEnv(1));
|
2010-04-12 18:30:11 +00:00
|
|
|
env2.up = &env;
|
2010-04-22 15:08:09 +00:00
|
|
|
env2.prevWith = prevWith;
|
2010-04-12 18:30:11 +00:00
|
|
|
|
2010-10-22 15:51:52 +00:00
|
|
|
env2.values[0] = state.allocValue();
|
|
|
|
state.evalAttrs(env, attrs, *env2.values[0]);
|
2010-04-14 15:01:04 +00:00
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
state.eval(env2, body, v);
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
2010-03-29 14:37:56 +00:00
|
|
|
{
|
2010-04-12 18:30:11 +00:00
|
|
|
state.eval(env, state.evalBool(env, cond) ? then : else_, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 21:21:24 +00:00
|
|
|
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
if (!state.evalBool(env, cond))
|
|
|
|
throwAssertionError("assertion failed at %1%", pos);
|
|
|
|
state.eval(env, body, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
mkBool(v, !state.evalBool(env, e));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 18:30:11 +00:00
|
|
|
void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
Value v1; state.eval(env, e1, v1);
|
|
|
|
Value v2; state.eval(env, e2, v2);
|
|
|
|
mkBool(v, state.eqValues(v1, v2));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
Value v1; state.eval(env, e1, v1);
|
|
|
|
Value v2; state.eval(env, e2, v2);
|
|
|
|
mkBool(v, !state.eqValues(v1, v2));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
mkBool(v, state.evalBool(env, e1) && state.evalBool(env, e2));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
mkBool(v, state.evalBool(env, e1) || state.evalBool(env, e2));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
mkBool(v, !state.evalBool(env, e1) || state.evalBool(env, e2));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
2010-08-02 16:31:05 +00:00
|
|
|
Value v1, v2;
|
|
|
|
state.evalAttrs(env, e1, v1);
|
2010-04-16 15:13:47 +00:00
|
|
|
state.evalAttrs(env, e2, v2);
|
2010-08-02 16:31:05 +00:00
|
|
|
|
2010-10-20 15:48:00 +00:00
|
|
|
state.nrOpUpdates++;
|
|
|
|
|
2010-08-02 16:31:05 +00:00
|
|
|
if (v1.attrs->size() == 0) { v = v2; return; }
|
|
|
|
if (v2.attrs->size() == 0) { v = v1; return; }
|
|
|
|
|
2010-10-24 20:09:37 +00:00
|
|
|
state.mkAttrs(v, v1.attrs->size() + v2.attrs->size());
|
2010-10-24 19:52:33 +00:00
|
|
|
|
|
|
|
/* Merge the attribute sets, preferring values from the second
|
|
|
|
set. Make sure to keep the resulting vector in sorted
|
|
|
|
order. */
|
|
|
|
Bindings::iterator i = v1.attrs->begin();
|
|
|
|
Bindings::iterator j = v2.attrs->begin();
|
2010-08-02 16:31:05 +00:00
|
|
|
|
2010-10-24 19:52:33 +00:00
|
|
|
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++);
|
|
|
|
}
|
2010-10-20 15:48:00 +00:00
|
|
|
|
2010-10-24 19:52:33 +00:00
|
|
|
while (i != v1.attrs->end()) v.attrs->push_back(*i++);
|
|
|
|
while (j != v2.attrs->end()) v.attrs->push_back(*j++);
|
|
|
|
|
2010-10-20 15:48:00 +00:00
|
|
|
state.nrOpUpdateValuesCopied += v.attrs->size();
|
2010-04-12 18:30:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
Value v1; state.eval(env, e1, v1);
|
|
|
|
state.forceList(v1);
|
|
|
|
Value v2; state.eval(env, e2, v2);
|
|
|
|
state.forceList(v2);
|
|
|
|
state.mkList(v, v1.list.length + v2.list.length);
|
|
|
|
for (unsigned int n = 0; n < v1.list.length; ++n)
|
|
|
|
v.list.elems[n] = v1.list.elems[n];
|
|
|
|
for (unsigned int n = 0; n < v2.list.length; ++n)
|
|
|
|
v.list.elems[n + v1.list.length] = v2.list.elems[n];
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 21:21:24 +00:00
|
|
|
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
|
|
|
{
|
|
|
|
PathSet context;
|
|
|
|
std::ostringstream s;
|
|
|
|
|
|
|
|
bool first = true, isPath = false;
|
|
|
|
Value vStr;
|
|
|
|
|
|
|
|
foreach (vector<Expr *>::iterator, i, *es) {
|
|
|
|
state.eval(env, *i, vStr);
|
|
|
|
|
|
|
|
/* If the first element is a path, then the result will also
|
|
|
|
be a path, we don't copy anything (yet - that's done later,
|
|
|
|
since paths are copied when they are used in a derivation),
|
|
|
|
and none of the strings are allowed to have contexts. */
|
|
|
|
if (first) {
|
|
|
|
isPath = vStr.type == tPath;
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
s << state.coerceToString(vStr, context, false, !isPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isPath && !context.empty())
|
|
|
|
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
|
|
|
|
|
|
|
|
if (isPath)
|
|
|
|
mkPath(v, s.str().c_str());
|
|
|
|
else
|
|
|
|
mkString(v, s.str(), context);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
void EvalState::forceValue(Value & v)
|
|
|
|
{
|
|
|
|
if (v.type == tThunk) {
|
2010-04-08 11:25:14 +00:00
|
|
|
ValueType saved = v.type;
|
|
|
|
try {
|
|
|
|
v.type = tBlackhole;
|
|
|
|
eval(*v.thunk.env, v.thunk.expr, v);
|
|
|
|
} catch (Error & e) {
|
|
|
|
v.type = saved;
|
|
|
|
throw;
|
|
|
|
}
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
2010-03-30 13:47:59 +00:00
|
|
|
else if (v.type == tApp)
|
|
|
|
callFunction(*v.app.left, *v.app.right, v);
|
2010-03-29 14:37:56 +00:00
|
|
|
else if (v.type == tBlackhole)
|
2010-04-09 12:00:49 +00:00
|
|
|
throwEvalError("infinite recursion encountered");
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-07 13:55:46 +00:00
|
|
|
void EvalState::strictForceValue(Value & v)
|
|
|
|
{
|
|
|
|
forceValue(v);
|
|
|
|
|
|
|
|
if (v.type == tAttrs) {
|
|
|
|
foreach (Bindings::iterator, i, *v.attrs)
|
2010-10-24 00:41:29 +00:00
|
|
|
strictForceValue(*i->value);
|
2010-04-07 13:55:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (v.type == tList) {
|
|
|
|
for (unsigned int n = 0; n < v.list.length; ++n)
|
2010-04-15 00:37:36 +00:00
|
|
|
strictForceValue(*v.list.elems[n]);
|
2010-04-07 13:55:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
int EvalState::forceInt(Value & v)
|
|
|
|
{
|
|
|
|
forceValue(v);
|
|
|
|
if (v.type != tInt)
|
2010-04-09 12:00:49 +00:00
|
|
|
throwTypeError("value is %1% while an integer was expected", showType(v));
|
2010-03-29 14:37:56 +00:00
|
|
|
return v.integer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-31 15:38:03 +00:00
|
|
|
bool EvalState::forceBool(Value & v)
|
|
|
|
{
|
|
|
|
forceValue(v);
|
|
|
|
if (v.type != tBool)
|
2010-04-09 12:00:49 +00:00
|
|
|
throwTypeError("value is %1% while a Boolean was expected", showType(v));
|
2010-03-31 15:38:03 +00:00
|
|
|
return v.boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
void EvalState::forceAttrs(Value & v)
|
|
|
|
{
|
|
|
|
forceValue(v);
|
|
|
|
if (v.type != tAttrs)
|
2010-04-09 12:00:49 +00:00
|
|
|
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EvalState::forceList(Value & v)
|
|
|
|
{
|
|
|
|
forceValue(v);
|
|
|
|
if (v.type != tList)
|
2010-04-09 12:00:49 +00:00
|
|
|
throwTypeError("value is %1% while a list was expected", showType(v));
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-30 13:47:59 +00:00
|
|
|
void EvalState::forceFunction(Value & v)
|
|
|
|
{
|
|
|
|
forceValue(v);
|
|
|
|
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp)
|
2010-04-09 12:00:49 +00:00
|
|
|
throwTypeError("value is %1% while a function was expected", showType(v));
|
2010-03-30 13:47:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-31 15:38:03 +00:00
|
|
|
string EvalState::forceString(Value & v)
|
2010-03-30 18:05:54 +00:00
|
|
|
{
|
|
|
|
forceValue(v);
|
|
|
|
if (v.type != tString)
|
2010-04-09 12:00:49 +00:00
|
|
|
throwTypeError("value is %1% while a string was expected", showType(v));
|
2010-03-31 15:38:03 +00:00
|
|
|
return string(v.string.s);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-10 10:29:50 +00:00
|
|
|
void copyContext(const Value & v, PathSet & context)
|
2010-03-31 19:52:29 +00:00
|
|
|
{
|
2010-04-09 12:00:49 +00:00
|
|
|
if (v.string.context)
|
2010-03-31 19:52:29 +00:00
|
|
|
for (const char * * p = v.string.context; *p; ++p)
|
|
|
|
context.insert(*p);
|
2010-06-10 10:29:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string EvalState::forceString(Value & v, PathSet & context)
|
|
|
|
{
|
|
|
|
string s = forceString(v);
|
|
|
|
copyContext(v, context);
|
2010-03-31 19:52:29 +00:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-31 15:38:03 +00:00
|
|
|
string EvalState::forceStringNoCtx(Value & v)
|
|
|
|
{
|
|
|
|
string s = forceString(v);
|
2010-03-30 18:05:54 +00:00
|
|
|
if (v.string.context)
|
2010-04-09 12:00:49 +00:00
|
|
|
throwEvalError("the string `%1%' is not allowed to refer to a store path (such as `%2%')",
|
|
|
|
v.string.s, v.string.context[0]);
|
2010-03-31 15:38:03 +00:00
|
|
|
return s;
|
2010-03-30 18:05:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-07 13:55:46 +00:00
|
|
|
bool EvalState::isDerivation(Value & v)
|
|
|
|
{
|
|
|
|
if (v.type != tAttrs) return false;
|
2010-04-13 12:25:42 +00:00
|
|
|
Bindings::iterator i = v.attrs->find(sType);
|
2010-10-24 00:41:29 +00:00
|
|
|
return i != v.attrs->end() && forceStringNoCtx(*i->value) == "derivation";
|
2010-04-07 13:55:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-30 09:22:33 +00:00
|
|
|
string EvalState::coerceToString(Value & v, PathSet & context,
|
|
|
|
bool coerceMore, bool copyToStore)
|
|
|
|
{
|
|
|
|
forceValue(v);
|
|
|
|
|
|
|
|
string s;
|
|
|
|
|
2010-03-31 19:52:29 +00:00
|
|
|
if (v.type == tString) {
|
2010-06-10 10:29:50 +00:00
|
|
|
copyContext(v, context);
|
2010-03-31 19:52:29 +00:00
|
|
|
return v.string.s;
|
|
|
|
}
|
2010-03-30 09:22:33 +00:00
|
|
|
|
|
|
|
if (v.type == tPath) {
|
|
|
|
Path path(canonPath(v.path));
|
|
|
|
|
|
|
|
if (!copyToStore) return path;
|
|
|
|
|
2010-04-07 13:55:46 +00:00
|
|
|
if (nix::isDerivation(path))
|
2010-04-09 12:00:49 +00:00
|
|
|
throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
|
2010-03-30 09:22:33 +00:00
|
|
|
|
|
|
|
Path dstPath;
|
|
|
|
if (srcToStore[path] != "")
|
|
|
|
dstPath = srcToStore[path];
|
|
|
|
else {
|
|
|
|
dstPath = readOnlyMode
|
|
|
|
? computeStorePathForPath(path).first
|
|
|
|
: store->addToStore(path);
|
|
|
|
srcToStore[path] = dstPath;
|
|
|
|
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
|
|
|
|
% path % dstPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
context.insert(dstPath);
|
|
|
|
return dstPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v.type == tAttrs) {
|
2010-04-13 12:25:42 +00:00
|
|
|
Bindings::iterator i = v.attrs->find(sOutPath);
|
2010-03-30 09:22:33 +00:00
|
|
|
if (i == v.attrs->end())
|
|
|
|
throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
|
2010-10-24 00:41:29 +00:00
|
|
|
return coerceToString(*i->value, context, coerceMore, copyToStore);
|
2010-03-30 09:22:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (coerceMore) {
|
|
|
|
|
|
|
|
/* Note that `false' is represented as an empty string for
|
|
|
|
shell scripting convenience, just like `null'. */
|
|
|
|
if (v.type == tBool && v.boolean) return "1";
|
|
|
|
if (v.type == tBool && !v.boolean) return "";
|
|
|
|
if (v.type == tInt) return int2String(v.integer);
|
|
|
|
if (v.type == tNull) return "";
|
|
|
|
|
|
|
|
if (v.type == tList) {
|
|
|
|
string result;
|
|
|
|
for (unsigned int n = 0; n < v.list.length; ++n) {
|
2010-04-15 00:37:36 +00:00
|
|
|
result += coerceToString(*v.list.elems[n],
|
2010-03-30 09:22:33 +00:00
|
|
|
context, coerceMore, copyToStore);
|
2010-04-01 14:35:03 +00:00
|
|
|
if (n < v.list.length - 1
|
|
|
|
/* !!! not quite correct */
|
2010-04-15 00:37:36 +00:00
|
|
|
&& (v.list.elems[n]->type != tList || v.list.elems[n]->list.length != 0))
|
2010-04-01 14:35:03 +00:00
|
|
|
result += " ";
|
2010-03-30 09:22:33 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throwTypeError("cannot coerce %1% to a string", showType(v));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Path EvalState::coerceToPath(Value & v, PathSet & context)
|
|
|
|
{
|
|
|
|
string path = coerceToString(v, context, false, false);
|
|
|
|
if (path == "" || path[0] != '/')
|
2010-04-09 12:00:49 +00:00
|
|
|
throwEvalError("string `%1%' doesn't represent an absolute path", path);
|
2010-03-30 09:22:33 +00:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
bool EvalState::eqValues(Value & v1, Value & v2)
|
|
|
|
{
|
|
|
|
forceValue(v1);
|
|
|
|
forceValue(v2);
|
|
|
|
|
2010-04-12 09:50:20 +00:00
|
|
|
/* !!! Hack to support some old broken code that relies on pointer
|
|
|
|
equality tests between attribute sets. (Specifically,
|
|
|
|
builderDefs calls uniqList on a list of attribute sets.) Will
|
|
|
|
remove this eventually. */
|
|
|
|
if (&v1 == &v2) return true;
|
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
if (v1.type != v2.type) return false;
|
2010-04-12 09:50:20 +00:00
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
switch (v1.type) {
|
|
|
|
|
|
|
|
case tInt:
|
|
|
|
return v1.integer == v2.integer;
|
|
|
|
|
|
|
|
case tBool:
|
|
|
|
return v1.boolean == v2.boolean;
|
|
|
|
|
2010-04-22 09:54:11 +00:00
|
|
|
case tString: {
|
|
|
|
/* Compare both the string and its context. */
|
|
|
|
if (strcmp(v1.string.s, v2.string.s) != 0) return false;
|
|
|
|
const char * * p = v1.string.context, * * q = v2.string.context;
|
|
|
|
if (!p && !q) return true;
|
|
|
|
if (!p || !q) return false;
|
|
|
|
for ( ; *p && *q; ++p, ++q)
|
|
|
|
if (strcmp(*p, *q) != 0) return false;
|
|
|
|
if (*p || *q) return false;
|
|
|
|
return true;
|
|
|
|
}
|
2010-03-29 14:37:56 +00:00
|
|
|
|
2010-03-31 20:09:20 +00:00
|
|
|
case tPath:
|
|
|
|
return strcmp(v1.path, v2.path) == 0;
|
|
|
|
|
2010-03-31 09:54:12 +00:00
|
|
|
case tNull:
|
|
|
|
return true;
|
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
case tList:
|
2010-04-16 13:51:01 +00:00
|
|
|
if (v1.list.length != v2.list.length) return false;
|
2010-03-29 14:37:56 +00:00
|
|
|
for (unsigned int n = 0; n < v1.list.length; ++n)
|
2010-04-15 00:37:36 +00:00
|
|
|
if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false;
|
2010-03-29 14:37:56 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
case tAttrs: {
|
2010-04-16 13:51:01 +00:00
|
|
|
if (v1.attrs->size() != v2.attrs->size()) return false;
|
2010-10-22 14:47:42 +00:00
|
|
|
Bindings::iterator i = v1.attrs->begin(), j = v2.attrs->begin();
|
|
|
|
for ( ; i != v1.attrs->end(); ++i, ++j)
|
2010-10-24 00:41:29 +00:00
|
|
|
if (i->name != j->name || !eqValues(*i->value, *j->value))
|
2010-05-07 12:11:05 +00:00
|
|
|
return false;
|
2010-03-29 14:37:56 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-04-01 10:55:36 +00:00
|
|
|
/* Functions are incomparable. */
|
|
|
|
case tLambda:
|
|
|
|
case tPrimOp:
|
|
|
|
case tPrimOpApp:
|
|
|
|
return false;
|
|
|
|
|
2010-03-29 14:37:56 +00:00
|
|
|
default:
|
2010-04-09 12:00:49 +00:00
|
|
|
throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
|
2010-03-29 14:37:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-30 15:18:20 +00:00
|
|
|
void EvalState::printStats()
|
2003-10-31 17:09:31 +00:00
|
|
|
{
|
2007-02-27 17:28:51 +00:00
|
|
|
char x;
|
2006-05-08 10:00:37 +00:00
|
|
|
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
|
2010-04-09 12:00:49 +00:00
|
|
|
Verbosity v = showStats ? lvlInfo : lvlDebug;
|
|
|
|
printMsg(v, "evaluation statistics:");
|
2010-10-20 11:38:30 +00:00
|
|
|
printMsg(v, format(" size of a value: %1%") % sizeof(Value));
|
2010-04-09 12:00:49 +00:00
|
|
|
printMsg(v, format(" expressions evaluated: %1%") % nrEvaluated);
|
|
|
|
printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack));
|
|
|
|
printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth);
|
2010-04-16 13:44:02 +00:00
|
|
|
printMsg(v, format(" stack space per eval() level: %1% bytes")
|
|
|
|
% ((&x - deepestStack) / (float) maxRecursionDepth));
|
2010-04-14 23:48:46 +00:00
|
|
|
printMsg(v, format(" environments allocated: %1% (%2% bytes)")
|
2010-10-23 22:58:24 +00:00
|
|
|
% nrEnvs % (nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *)));
|
2010-04-15 00:37:36 +00:00
|
|
|
printMsg(v, format(" list elements: %1% (%2% bytes)")
|
|
|
|
% nrListElems % (nrListElems * sizeof(Value *)));
|
2010-10-23 22:58:24 +00:00
|
|
|
printMsg(v, format(" values allocated: %1% (%2% bytes)")
|
2010-04-14 23:48:46 +00:00
|
|
|
% nrValues % (nrValues * sizeof(Value)));
|
2010-10-20 15:48:00 +00:00
|
|
|
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);
|
2010-04-13 14:34:11 +00:00
|
|
|
printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());
|
2010-10-24 14:20:02 +00:00
|
|
|
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);
|
2003-10-31 17:09:31 +00:00
|
|
|
}
|
2006-09-04 21:06:23 +00:00
|
|
|
|
2010-04-07 13:55:46 +00:00
|
|
|
|
2006-09-04 21:06:23 +00:00
|
|
|
}
|