1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-15 15:02:42 +01:00

Checkpoint

This commit is contained in:
Eelco Dolstra 2019-04-22 23:25:47 +02:00
parent 4237414f4d
commit 7c716b4c49
27 changed files with 596 additions and 440 deletions

View file

@ -32,7 +32,7 @@ static Strings parseAttrPath(const string & s)
} }
Value * findAlongAttrPath(EvalState & state, const string & attrPath, Ptr<Value> findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings & autoArgs, Value & vIn) Bindings & autoArgs, Value & vIn)
{ {
Strings tokens = parseAttrPath(attrPath); Strings tokens = parseAttrPath(attrPath);
@ -40,7 +40,7 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
Error attrError = Error attrError =
Error(format("attribute selection path '%1%' does not match expression") % attrPath); Error(format("attribute selection path '%1%' does not match expression") % attrPath);
Value * v = &vIn; Ptr<Value> v(&vIn);
for (auto & attr : tokens) { for (auto & attr : tokens) {
@ -50,7 +50,7 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
if (string2Int(attr, attrIndex)) apType = apIndex; if (string2Int(attr, attrIndex)) apType = apIndex;
/* Evaluate the expression. */ /* Evaluate the expression. */
Value * vNew = state.allocValue(); auto vNew = state.allocValue();
state.autoCallFunction(autoArgs, *v, *vNew); state.autoCallFunction(autoArgs, *v, *vNew);
v = vNew; v = vNew;
state.forceValue(*v); state.forceValue(*v);
@ -71,7 +71,7 @@ Value * 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 % attrPath); throw Error(format("attribute '%1%' in selection path '%2%' not found") % attr % attrPath);
v = &*a->value; v = a->value;
} }
else if (apType == apIndex) { else if (apType == apIndex) {

View file

@ -7,7 +7,7 @@
namespace nix { namespace nix {
Value * findAlongAttrPath(EvalState & state, const string & attrPath, Ptr<Value> findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings & autoArgs, Value & vIn); Bindings & autoArgs, Value & vIn);
} }

View file

@ -20,14 +20,15 @@ Ptr<Bindings> Bindings::allocBindings(size_t capacity)
void EvalState::mkAttrs(Value & v, size_t capacity) void EvalState::mkAttrs(Value & v, size_t capacity)
{ {
v.type = tAttrs;
if (capacity == 0) { if (capacity == 0) {
v.attrs = emptyBindings; v.attrs = emptyBindings;
return; v.type = tAttrs;
} } else {
v.attrs = Bindings::allocBindings(capacity); v.attrs = Bindings::allocBindings(capacity);
v.type = tAttrs;
nrAttrsets++; nrAttrsets++;
nrAttrsInAttrsets += capacity; nrAttrsInAttrsets += capacity;
}
} }
@ -36,7 +37,7 @@ void EvalState::mkAttrs(Value & v, size_t capacity)
this attribute. */ this attribute. */
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name) Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
{ {
Value * v = allocValue(); auto v = allocValue();
vAttrs.attrs->push_back(Attr(name, v)); vAttrs.attrs->push_back(Attr(name, v));
return v; return v;
} }

View file

@ -42,11 +42,11 @@ private:
size_t size_; size_t size_;
Attr attrs[0]; Attr attrs[0];
public: Bindings(size_t capacity) : Object(tBindings, capacity), size_(0) {}
// FIXME: make private
Bindings(size_t capacity) : Object(tBindings, capacity), size_(0) { }
Bindings(const Bindings & bindings) = delete; Bindings(const Bindings & bindings) = delete;
public:
size_t size() const { return size_; } size_t size() const { return size_; }
bool empty() const { return !size_; } bool empty() const { return !size_; }
@ -56,6 +56,7 @@ public:
void push_back(const Attr & attr) void push_back(const Attr & attr)
{ {
assert(size_ < capacity()); assert(size_ < capacity());
gc.assertObject(attr.value);
attrs[size_++] = attr; attrs[size_++] = attr;
} }

View file

@ -28,9 +28,9 @@ MixEvalArgs::MixEvalArgs()
.handler([&](std::string s) { searchPath.push_back(s); }); .handler([&](std::string s) { searchPath.push_back(s); });
} }
Bindings * MixEvalArgs::getAutoArgs(EvalState & state) Ptr<Bindings> MixEvalArgs::getAutoArgs(EvalState & state)
{ {
Bindings * res = Bindings::allocBindings(autoArgs.size()); auto res = Bindings::allocBindings(autoArgs.size());
for (auto & i : autoArgs) { for (auto & i : autoArgs) {
Value * v = state.allocValue(); Value * v = state.allocValue();
if (i.second[0] == 'E') if (i.second[0] == 'E')

View file

@ -8,11 +8,14 @@ class Store;
class EvalState; class EvalState;
class Bindings; class Bindings;
template<class T>
struct Ptr;
struct MixEvalArgs : virtual Args struct MixEvalArgs : virtual Args
{ {
MixEvalArgs(); MixEvalArgs();
Bindings * getAutoArgs(EvalState & state); Ptr<Bindings> getAutoArgs(EvalState & state);
Strings searchPath; Strings searchPath;

View file

@ -27,21 +27,27 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v, const
void EvalState::forceValue(Value & v, const Pos & pos) void EvalState::forceValue(Value & v, const Pos & pos)
{ {
if (v.type == tThunk) { if (v.type == tThunk) {
Env * env = v.thunk.env; // FIXME: this is necessary because some values (like vList2)
// are created non-atomically.
Ptr<Env> env(v.thunk.env);
Expr * expr = v.thunk.expr; Expr * expr = v.thunk.expr;
try { try {
v.type = tBlackhole; v.type = tBlackhole;
//checkInterrupt(); //checkInterrupt();
expr->eval(*this, *env, v); expr->eval(*this, *env, v);
} catch (...) { } catch (...) {
v.type = tThunk;
v.thunk.env = env; v.thunk.env = env;
v.thunk.expr = expr; v.thunk.expr = expr;
v.type = tThunk;
throw; throw;
} }
} }
else if (v.type == tApp) else if (v.type == tApp) {
// FIXME: idem.
Ptr<Value> left(v.app.left);
Ptr<Value> right(v.app.right);
callFunction(*v.app.left, *v.app.right, v, noPos); callFunction(*v.app.left, *v.app.right, v, noPos);
}
else if (v.type == tBlackhole) else if (v.type == tBlackhole)
throwEvalError("infinite recursion encountered, at %1%", pos); throwEvalError("infinite recursion encountered, at %1%", pos);
} }

View file

@ -166,10 +166,10 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
if (name.symbol.set()) { if (name.symbol.set()) {
return name.symbol; return name.symbol;
} else { } else {
Value nameValue; Root<Value> nameValue;
name.expr->eval(state, env, nameValue); name.expr->eval(state, env, nameValue);
state.forceStringNoCtx(nameValue); state.forceStringNoCtx(nameValue);
return state.symbols.create(nameValue.string.s); return state.symbols.create(nameValue->string.s);
} }
} }
@ -384,7 +384,7 @@ Path EvalState::toRealPath(const Path & path, const PathSet & context)
Value * EvalState::addConstant(const string & name, Value & v) Value * EvalState::addConstant(const string & name, Value & v)
{ {
Value * v2 = allocValue(); auto v2 = allocValue();
*v2 = v; *v2 = v;
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv->values[baseEnvDispl++] = v2; baseEnv->values[baseEnvDispl++] = v2;
@ -398,11 +398,11 @@ Value * EvalState::addPrimOp(const string & name,
size_t arity, PrimOpFun primOp) size_t arity, PrimOpFun primOp)
{ {
if (arity == 0) { if (arity == 0) {
Value v; Root<Value> v;
primOp(*this, noPos, nullptr, v); primOp(*this, noPos, nullptr, v);
return addConstant(name, v); return addConstant(name, v);
} }
Value * v = allocValue(); auto v = allocValue();
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
Symbol sym = symbols.create(name2); Symbol sym = symbols.create(name2);
v->type = tPrimOp; v->type = tPrimOp;
@ -523,12 +523,18 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
{ {
for (size_t l = var.level; l; --l, env = env->up) ; for (size_t l = var.level; l; --l, env = env->up) ;
if (!var.fromWith) return env->values[var.displ]; assert(((Object *) env)->type == tEnv);
if (!var.fromWith) {
auto v = env->values[var.displ];
if (v) gc.assertObject(v);
return v;
}
while (1) { while (1) {
if (env->type == Env::HasWithExpr) { if (env->type == Env::HasWithExpr) {
if (noEval) return 0; if (noEval) return 0;
Value * v = allocValue(); auto v = allocValue();
evalAttrs(*env->up, (Expr *) env->values[0], *v); evalAttrs(*env->up, (Expr *) env->values[0], *v);
env->values[0] = v; env->values[0] = v;
env->type = Env::HasWithAttrs; env->type = Env::HasWithAttrs;
@ -536,6 +542,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
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()) {
if (countCalls && j->pos) attrSelects[*j->pos]++; if (countCalls && j->pos) attrSelects[*j->pos]++;
gc.assertObject(j->value);
return j->value; return j->value;
} }
if (!env->prevWith) if (!env->prevWith)
@ -559,12 +566,8 @@ Ptr<Env> EvalState::allocEnv(size_t size)
nrEnvs++; nrEnvs++;
nrValuesInEnvs += size; nrValuesInEnvs += size;
auto env = gc.alloc<Env>(Env::wordsFor(size), size);
// FIXME return gc.alloc<Env>(Env::wordsFor(size), size);
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
return env;
} }
@ -572,14 +575,17 @@ void EvalState::mkList(Value & v, size_t size)
{ {
if (size == 0) if (size == 0)
v.type = tList0; v.type = tList0;
else if (size == 1) else if (size == 1) {
v.smallList[0] = nullptr;
v.type = tList1; v.type = tList1;
else if (size == 2) } else if (size == 2) {
v.smallList[0] = nullptr;
v.smallList[1] = nullptr;
v.type = tList2; v.type = tList2;
else { } else {
v.type = tListN;
v.bigList = gc.alloc<PtrList<Value>>( v.bigList = gc.alloc<PtrList<Value>>(
PtrList<Value>::wordsFor(size), tValueList, size); PtrList<Value>::wordsFor(size), tValueList, size);
v.type = tListN;
} }
nrListElems += size; nrListElems += size;
} }
@ -616,12 +622,12 @@ void EvalState::mkPos(Value & v, Pos * pos)
/* Create a thunk for the delayed computation of the given expression /* Create a thunk for the delayed computation of the given expression
in the given environment. But if the expression is a variable, in the given environment. But if the expression is a variable or a
then look it up right away. This significantly reduces the number constant, then look it up right away. This significantly reduces
of thunks allocated. */ the number of thunks allocated. */
Value * Expr::maybeThunk(EvalState & state, Env & env) Ptr<Value> Expr::maybeThunk(EvalState & state, Env & env)
{ {
Value * v = state.allocValue(); auto v = state.allocValue();
mkThunk(*v, env, this); mkThunk(*v, env, this);
return v; return v;
} }
@ -629,9 +635,9 @@ Value * Expr::maybeThunk(EvalState & state, Env & env)
unsigned long nrAvoided = 0; unsigned long nrAvoided = 0;
Value * ExprVar::maybeThunk(EvalState & state, Env & env) Ptr<Value> ExprVar::maybeThunk(EvalState & state, Env & env)
{ {
Value * v = state.lookupVar(&env, *this, true); auto v = state.lookupVar(&env, *this, true);
/* The value might not be initialised in the environment yet. /* The value might not be initialised in the environment yet.
In that case, ignore it. */ In that case, ignore it. */
if (v) { nrAvoided++; return v; } if (v) { nrAvoided++; return v; }
@ -639,28 +645,28 @@ Value * ExprVar::maybeThunk(EvalState & state, Env & env)
} }
Value * ExprString::maybeThunk(EvalState & state, Env & env) Ptr<Value> ExprString::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; nrAvoided++;
return &*v; return v;
} }
Value * ExprInt::maybeThunk(EvalState & state, Env & env) Ptr<Value> ExprInt::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; nrAvoided++;
return &*v; return v;
} }
Value * ExprFloat::maybeThunk(EvalState & state, Env & env) Ptr<Value> ExprFloat::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; nrAvoided++;
return &*v; return v;
} }
Value * ExprPath::maybeThunk(EvalState & state, Env & env) Ptr<Value> ExprPath::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; nrAvoided++;
return &*v; return v;
} }
@ -723,21 +729,21 @@ void EvalState::eval(Expr * e, Value & v)
inline bool EvalState::evalBool(Env & env, Expr * e) inline bool EvalState::evalBool(Env & env, Expr * e)
{ {
Value v; Root<Value> v;
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type != tBool) if (v->type != tBool)
throwTypeError("value is %1% while a Boolean was expected", v); throwTypeError("value is %1% while a Boolean was expected", v);
return v.boolean; return v->boolean;
} }
inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos) inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
{ {
Value v; Root<Value> v;
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type != tBool) if (v->type != tBool)
throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos); throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
return v.boolean; return v->boolean;
} }
@ -781,14 +787,14 @@ 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, attrs.size() + dynamicAttrs.size()); state.mkAttrs(v, attrs.size() + dynamicAttrs.size());
Env *dynamicEnv = &env; Env * dynamicEnv = &env;
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())); auto env2 = state.allocEnv(attrs.size());
env2.up = &env; env2->up = &env;
dynamicEnv = &env2; dynamicEnv = &*env2;
AttrDefs::iterator overrides = attrs.find(state.sOverrides); AttrDefs::iterator overrides = attrs.find(state.sOverrides);
bool hasOverrides = overrides != attrs.end(); bool hasOverrides = overrides != attrs.end();
@ -798,13 +804,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
in the original environment. */ in the original environment. */
size_t displ = 0; size_t displ = 0;
for (auto & i : attrs) { for (auto & i : attrs) {
Value * vAttr; Ptr<Value> vAttr; // FIXME: Ptr unnecessary?
if (hasOverrides && !i.second.inherited) { if (hasOverrides && !i.second.inherited) {
vAttr = state.allocValue(); vAttr = state.allocValue(); // FIXME
mkThunk(*vAttr, env2, i.second.e); mkThunk(*vAttr, env2, i.second.e);
} else } else
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
env2.values[displ++] = vAttr; env2->values[displ++] = vAttr;
v.attrs->push_back(Attr(i.first, vAttr, &i.second.pos)); v.attrs->push_back(Attr(i.first, vAttr, &i.second.pos));
} }
@ -826,7 +832,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
AttrDefs::iterator j = attrs.find(i.name); AttrDefs::iterator j = attrs.find(i.name);
if (j != attrs.end()) { if (j != attrs.end()) {
(*newBnds)[j->second.displ] = i; (*newBnds)[j->second.displ] = i;
env2.values[j->second.displ] = i.value; env2->values[j->second.displ] = i.value;
} else } else
newBnds->push_back(i); newBnds->push_back(i);
} }
@ -841,13 +847,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
/* Dynamic attrs apply *after* rec and __overrides. */ /* Dynamic attrs apply *after* rec and __overrides. */
for (auto & i : dynamicAttrs) { for (auto & i : dynamicAttrs) {
Value nameVal; Root<Value> nameVal;
i.nameExpr->eval(state, *dynamicEnv, nameVal); i.nameExpr->eval(state, *dynamicEnv, nameVal);
state.forceValue(nameVal, i.pos); state.forceValue(nameVal, i.pos);
if (nameVal.type == tNull) if (nameVal->type == tNull)
continue; continue;
state.forceStringNoCtx(nameVal); state.forceStringNoCtx(nameVal);
Symbol nameSym = state.symbols.create(nameVal.string.s); Symbol nameSym = state.symbols.create(nameVal->string.s);
Bindings::iterator j = v.attrs->find(nameSym); Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end()) if (j != v.attrs->end())
throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%", nameSym, i.pos, *j->pos); throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%", nameSym, i.pos, *j->pos);
@ -864,15 +870,15 @@ 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())); auto env2 = state.allocEnv(attrs->attrs.size());
env2.up = &env; env2->up = &env;
/* The recursive attributes are evaluated in the new environment, /* The recursive attributes are evaluated in the new environment,
while the inherited attributes are evaluated in the original while the inherited attributes are evaluated in the original
environment. */ environment. */
size_t displ = 0; size_t displ = 0;
for (auto & i : attrs->attrs) for (auto & i : attrs->attrs)
env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); env2->values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
body->eval(state, env2, v); body->eval(state, env2, v);
} }
@ -915,9 +921,9 @@ unsigned long nrLookups = 0;
void ExprSelect::eval(EvalState & state, Env & env, Value & v) void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{ {
Value vTmp; Root<Value> vTmp;
Pos * pos2 = 0; Pos * pos2 = 0;
Value * vAttrs = &vTmp; Value * vAttrs = &*vTmp;
e->eval(state, env, vTmp); e->eval(state, env, vTmp);
@ -960,8 +966,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
{ {
Value vTmp; Root<Value> vTmp;
Value * vAttrs = &vTmp; Value * vAttrs = &*vTmp;
e->eval(state, env, vTmp); e->eval(state, env, vTmp);
@ -994,7 +1000,7 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
void ExprApp::eval(EvalState & state, Env & env, Value & v) void ExprApp::eval(EvalState & state, Env & env, Value & v)
{ {
/* FIXME: vFun prevents GCC from doing tail call optimisation. */ /* FIXME: vFun prevents GCC from doing tail call optimisation. */
Value vFun; Root<Value> vFun;
e1->eval(state, env, vFun); e1->eval(state, env, vFun);
state.callFunction(vFun, *(e2->maybeThunk(state, env)), v, pos); state.callFunction(vFun, *(e2->maybeThunk(state, env)), v, pos);
} }
@ -1028,11 +1034,11 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
if (countCalls) primOpCalls[primOp->primOp->name]++; if (countCalls) primOpCalls[primOp->primOp->name]++;
primOp->primOp->fun(*this, pos, vArgs, v); primOp->primOp->fun(*this, pos, vArgs, v);
} else { } else {
Value * fun2 = allocValue(); auto fun2 = allocValue();
*fun2 = fun; *fun2 = fun;
v.type = tPrimOpApp;
v.app.left = fun2; v.app.left = fun2;
v.app.right = &arg; v.app.right = &arg;
v.type = tPrimOpApp;
} }
} }
@ -1053,10 +1059,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
* but for functors we may keep a reference, so heap-allocate * but for functors we may keep a reference, so heap-allocate
* a copy and use that instead. * a copy and use that instead.
*/ */
auto & fun2 = *allocValue(); auto fun2 = allocValue();
fun2 = fun; *fun2 = fun;
/* !!! Should we use the attr pos here? */ /* !!! Should we use the attr pos here? */
Value v2; Root<Value> v2;
callFunction(*found->value, fun2, v2, pos); callFunction(*found->value, fun2, v2, pos);
return callFunction(v2, arg, v, pos); return callFunction(v2, arg, v, pos);
} }
@ -1070,19 +1076,19 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
auto size = auto size =
(lambda.arg.empty() ? 0 : 1) + (lambda.arg.empty() ? 0 : 1) +
(lambda.matchAttrs ? lambda.formals->formals.size() : 0); (lambda.matchAttrs ? lambda.formals->formals.size() : 0);
Env & env2(allocEnv(size)); auto env2 = allocEnv(size);
env2.up = fun.lambda.env; env2->up = fun.lambda.env;
size_t displ = 0; size_t displ = 0;
if (!lambda.matchAttrs) if (!lambda.matchAttrs)
env2.values[displ++] = &arg; env2->values[displ++] = &arg;
else { else {
forceAttrs(arg, pos); forceAttrs(arg, pos);
if (!lambda.arg.empty()) if (!lambda.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
@ -1093,10 +1099,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
if (j == arg.attrs->end()) { if (j == arg.attrs->end()) {
if (!i.def) throwTypeError("%1% called without required argument '%2%', at %3%", if (!i.def) throwTypeError("%1% called without required argument '%2%', at %3%",
lambda, i.name, pos); lambda, i.name, pos);
env2.values[displ++] = i.def->maybeThunk(*this, env2); env2->values[displ++] = i.def->maybeThunk(*this, env2);
} else { } else {
attrsUsed++; attrsUsed++;
env2.values[displ++] = j->value; env2->values[displ++] = j->value;
} }
} }
@ -1144,7 +1150,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
if (fun.type == tAttrs) { if (fun.type == tAttrs) {
auto found = fun.attrs->find(sFunctor); auto found = fun.attrs->find(sFunctor);
if (found != fun.attrs->end()) { if (found != fun.attrs->end()) {
Value * v = allocValue(); auto v = allocValue();
callFunction(*found->value, fun, *v, noPos); callFunction(*found->value, fun, *v, noPos);
forceValue(*v); forceValue(*v);
return autoCallFunction(args, *v, res); return autoCallFunction(args, *v, res);
@ -1156,7 +1162,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
return; return;
} }
Value * actualArgs = allocValue(); auto actualArgs = allocValue();
mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size()); mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
for (auto & i : fun.lambda.fun->formals->formals) { for (auto & i : fun.lambda.fun->formals->formals) {
@ -1175,11 +1181,11 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
void ExprWith::eval(EvalState & state, Env & env, Value & v) void ExprWith::eval(EvalState & state, Env & env, Value & v)
{ {
Env & env2(state.allocEnv(1)); auto env2 = state.allocEnv(1);
env2.up = &env; env2->up = &env;
env2.prevWith = prevWith; env2->prevWith = prevWith;
env2.type = Env::HasWithExpr; env2->type = Env::HasWithExpr;
env2.values[0] = (Value *) attrs; env2->values[0] = (Value *) attrs;
body->eval(state, env2, v); body->eval(state, env2, v);
} }
@ -1207,16 +1213,16 @@ void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
void ExprOpEq::eval(EvalState & state, Env & env, Value & v) void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
{ {
Value v1; e1->eval(state, env, v1); Root<Value> v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2); Root<Value> v2; e2->eval(state, env, v2);
mkBool(v, state.eqValues(v1, v2)); mkBool(v, state.eqValues(v1, v2));
} }
void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
{ {
Value v1; e1->eval(state, env, v1); Root<Value> v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2); Root<Value> v2; e2->eval(state, env, v2);
mkBool(v, !state.eqValues(v1, v2)); mkBool(v, !state.eqValues(v1, v2));
} }
@ -1241,23 +1247,24 @@ void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
{ {
Value v1, v2; Root<Value> v1;
Root<Value> v2;
state.evalAttrs(env, e1, v1); state.evalAttrs(env, e1, v1);
state.evalAttrs(env, e2, v2); state.evalAttrs(env, e2, v2);
state.nrOpUpdates++; 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.mkAttrs(v, v1.attrs->size() + v2.attrs->size()); state.mkAttrs(v, v1->attrs->size() + v2->attrs->size());
/* Merge the sets, preferring values from the second set. Make /* Merge the sets, preferring values from the second set. Make
sure to keep the resulting vector in sorted order. */ sure to keep the resulting vector in sorted order. */
Bindings::iterator i = v1.attrs->begin(); Bindings::iterator i = v1->attrs->begin();
Bindings::iterator j = v2.attrs->begin(); Bindings::iterator j = v2->attrs->begin();
while (i != v1.attrs->end() && j != v2.attrs->end()) { while (i != v1->attrs->end() && j != v2->attrs->end()) {
if (i->name == j->name) { if (i->name == j->name) {
v.attrs->push_back(*j); v.attrs->push_back(*j);
++i; ++j; ++i; ++j;
@ -1268,8 +1275,8 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
v.attrs->push_back(*j++); v.attrs->push_back(*j++);
} }
while (i != v1.attrs->end()) v.attrs->push_back(*i++); while (i != v1->attrs->end()) v.attrs->push_back(*i++);
while (j != v2.attrs->end()) v.attrs->push_back(*j++); while (j != v2->attrs->end()) v.attrs->push_back(*j++);
state.nrOpUpdateValuesCopied += v.attrs->size(); state.nrOpUpdateValuesCopied += v.attrs->size();
} }
@ -1277,9 +1284,9 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
{ {
Value v1; e1->eval(state, env, v1); Root<Value> v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2); Root<Value> v2; e2->eval(state, env, v2);
Value * lists[2] = { &v1, &v2 }; Value * lists[2] = { &*v1, &*v2 };
state.concatLists(v, 2, lists, pos); state.concatLists(v, 2, lists, pos);
} }
@ -1324,7 +1331,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
Tag firstType = tString; Tag firstType = tString;
for (auto & i : *es) { for (auto & i : *es) {
Value vTmp; Root<Value> vTmp;
i->eval(state, env, vTmp); i->eval(state, env, vTmp);
/* If the first element is a path, then the result will also /* If the first element is a path, then the result will also
@ -1332,25 +1339,25 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
since paths are copied when they are used in a derivation), since paths are copied when they are used in a derivation),
and none of the strings are allowed to have contexts. */ and none of the strings are allowed to have contexts. */
if (first) { if (first) {
firstType = vTmp.type; firstType = vTmp->type;
first = false; first = false;
} }
if (firstType == tInt) { if (firstType == tInt) {
if (vTmp.type == tInt) { if (vTmp->type == tInt) {
n += vTmp.integer; n += vTmp->integer;
} else if (vTmp.type == tFloat) { } else if (vTmp->type == tFloat) {
// Upgrade the type from int to float; // Upgrade the type from int to float;
firstType = tFloat; firstType = tFloat;
nf = n; nf = n;
nf += vTmp.fpoint; nf += vTmp->fpoint;
} else } else
throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos); throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos);
} else if (firstType == tFloat) { } else if (firstType == tFloat) {
if (vTmp.type == tInt) { if (vTmp->type == tInt) {
nf += vTmp.integer; nf += vTmp->integer;
} else if (vTmp.type == tFloat) { } else if (vTmp->type == tFloat) {
nf += vTmp.fpoint; nf += vTmp->fpoint;
} else } else
throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp), pos); throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp), pos);
} else } else
@ -1527,7 +1534,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.type == tAttrs) { if (v.type == tAttrs) {
auto i = v.attrs->find(sToString); auto i = v.attrs->find(sToString);
if (i != v.attrs->end()) { if (i != v.attrs->end()) {
Value v1; Root<Value> v1;
callFunction(*i->value, v, v1, pos); callFunction(*i->value, v, v1, pos);
return coerceToString(pos, v1, context, coerceMore, copyToStore); return coerceToString(pos, v1, context, coerceMore, copyToStore);
} }

View file

@ -42,7 +42,16 @@ struct Env : Object
enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2; // FIXME: fold into type? enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2; // FIXME: fold into type?
Value * values[0]; Value * values[0];
Env(unsigned short size) : Object(tEnv, 0), size(size) {} private:
Env(unsigned short size) : Object(tEnv, 0), size(size) {
for (auto i = 0; i < size; i++)
values[i] = nullptr;
}
friend class GC;
public:
Size words() const Size words() const
{ {

View file

@ -18,6 +18,15 @@ GC::GC()
backSentinel->prev = frontSentinel; backSentinel->prev = frontSentinel;
backSentinel->next = nullptr; backSentinel->next = nullptr;
frontRootSentinel = (Root<Object> *) malloc(sizeof(Root<Object>));
backRootSentinel = (Root<Object> *) malloc(sizeof(Root<Object>));
frontRootSentinel->prev = nullptr;
frontRootSentinel->next = backRootSentinel;
backRootSentinel->prev = frontRootSentinel;
backRootSentinel->next = nullptr;
} }
GC::~GC() GC::~GC()
@ -26,9 +35,18 @@ GC::~GC()
for (Ptr<Object> * p = frontSentinel->next; p != backSentinel; p = p->next) for (Ptr<Object> * p = frontSentinel->next; p != backSentinel; p = p->next)
n++; n++;
if (n) if (n)
warn("%d GC roots still exist on exit", n); warn("%d GC root pointers still exist on exit", n);
n = 0;
for (Root<Object> * p = frontRootSentinel->next; p != backRootSentinel; p = p->next)
n++;
if (n)
warn("%d GC root objects still exist on exit", n);
assert(!frontSentinel->prev); assert(!frontSentinel->prev);
assert(!backSentinel->next); assert(!backSentinel->next);
assert(!frontRootSentinel->prev);
assert(!backRootSentinel->next);
} }
void GC::gc() void GC::gc()
@ -37,23 +55,10 @@ void GC::gc()
std::stack<Object *> stack; std::stack<Object *> stack;
for (Ptr<Object> * p = frontSentinel->next; p != backSentinel; p = p->next) {
if (!p->value) continue;
stack.push(p->value);
while (!stack.empty()) {
auto obj = stack.top();
stack.pop();
// FIXME: ensure this gets inlined. // FIXME: ensure this gets inlined.
auto push = [&](Object * p) { if (p) { /* FIXME */ assert(isObject(p)); stack.push(p); } }; auto push = [&](Object * p) { if (p) { assertObject(p); stack.push(p); } };
//printError("MARK %x", obj); auto pushPointers = [&](Object * obj) {
if (!obj->isMarked()) {
marked++;
obj->mark();
switch (obj->type) { switch (obj->type) {
case tFree: case tFree:
@ -116,6 +121,7 @@ void GC::gc()
break; break;
case tThunk: case tThunk:
case tBlackhole:
push(((Value *) obj)->thunk.env); push(((Value *) obj)->thunk.env);
break; break;
@ -129,10 +135,6 @@ void GC::gc()
push(((Value *) obj)->lambda.env); push(((Value *) obj)->lambda.env);
break; break;
case tBlackhole:
// FIXME
break;
case tPrimOp: case tPrimOp:
// FIXME: GC primops? // FIXME: GC primops?
break; break;
@ -141,8 +143,32 @@ void GC::gc()
printError("don't know how to traverse object at %x (tag %d)", obj, obj->type); printError("don't know how to traverse object at %x (tag %d)", obj, obj->type);
abort(); abort();
} }
};
auto processStack = [&]() {
while (!stack.empty()) {
auto obj = stack.top();
stack.pop();
//printError("MARK %x", obj);
if (!obj->isMarked()) {
marked++;
obj->mark();
pushPointers(obj);
} }
} }
};
for (Root<Object> * p = frontRootSentinel->next; p != backRootSentinel; p = p->next) {
pushPointers(&p->value);
processStack();
}
for (Ptr<Object> * p = frontSentinel->next; p != backSentinel; p = p->next) {
if (!p->value) continue;
stack.push(p->value);
processStack();
} }
Size totalObjectsFreed = 0; Size totalObjectsFreed = 0;
@ -162,7 +188,7 @@ void GC::gc()
}); });
} }
printError("freed %d bytes in %d dead objects, keeping %d objects", debug("freed %d bytes in %d dead objects, keeping %d objects",
totalWordsFreed * WORD_SIZE, totalObjectsFreed, marked); totalWordsFreed * WORD_SIZE, totalObjectsFreed, marked);
} }
@ -224,6 +250,7 @@ std::pair<Size, Size> GC::Arena::freeUnmarked()
}; };
if (tag == tFree) { if (tag == tFree) {
//debug("FREE %x %d", obj, obj->getMisc());
if (curFree) { if (curFree) {
// Merge this object into the previous free // Merge this object into the previous free
// object. // object.
@ -239,10 +266,13 @@ std::pair<Size, Size> GC::Arena::freeUnmarked()
if (obj->isMarked()) { if (obj->isMarked()) {
// Unmark to prepare for the next GC run. // Unmark to prepare for the next GC run.
//debug("KEEP OBJECT %x %d %d", obj, obj->type, objSize);
curFree = nullptr; curFree = nullptr;
obj->unmark(); obj->unmark();
} else { } else {
//printError("FREE %x %d %d", obj, obj->type, objSize); //debug("FREE OBJECT %x %d %d", obj, obj->type, objSize);
for (Size i = 0; i < objSize; ++i)
((Word *) obj)[i] = 0xdeadc0dedeadbeefULL;
objectsFreed += 1; objectsFreed += 1;
wordsFreed += objSize; wordsFreed += objSize;
if (curFree) { if (curFree) {
@ -279,9 +309,18 @@ bool GC::isObject(void * p)
return false; return false;
} }
GC::ArenaList::ArenaList() void GC::assertObject(void * p)
: nextSize(1024)
{ {
if (!isObject(p)) {
printError("object %p is not an object", p);
abort();
}
}
GC::ArenaList::ArenaList()
{
static Size initialHeapSize = std::stol(getEnv("GC_INITIAL_HEAP_SIZE", "1000000")) / WORD_SIZE;
nextSize = initialHeapSize;
} }
} }

View file

@ -104,94 +104,6 @@ struct PtrList : Object
static Size wordsFor(Size size) { return 1 + size; } static Size wordsFor(Size size) { return 1 + size; }
}; };
template<class T>
struct Ptr
{
Ptr * prev = nullptr, * next = nullptr;
T * value = nullptr;
Ptr() { }
Ptr(Ptr * next, T * value) : next(next), value(value)
{
assert(value);
assert(next == next->prev->next);
prev = next->prev;
next->prev = this;
prev->next = this;
}
Ptr(const Ptr & p)
{
if (p.value) {
auto & p2 = const_cast<Ptr &>(p);
value = p2.value;
next = &p2;
prev = p2.prev;
prev->next = this;
p2.prev = this;
}
}
Ptr(Ptr && p)
{
*this = std::move(p);
}
Ptr & operator =(Ptr && p)
{
reset();
if (p.value) {
value = p.value;
next = p.next;
prev = p.prev;
p.value = nullptr;
prev->next = this;
next->prev = this;
}
return *this;
}
Ptr & operator =(const Ptr & p)
{
throw Error("NOT IMPLEMENTED = PTR &");
}
~Ptr()
{
reset();
}
void reset()
{
if (value) {
assert(next);
assert(prev);
assert(next->prev == this);
next->prev = prev;
assert(prev->next == this);
prev->next = next;
value = nullptr;
}
}
T * operator ->()
{
return value;
}
operator T * ()
{
return value;
}
operator T & ()
{
assert(value);
return *value;
}
};
struct Free : Object struct Free : Object
{ {
Free * next; Free * next;
@ -204,6 +116,12 @@ struct Free : Object
void setSize(Size size) { assert(size >= 1); setMisc(size); } void setSize(Size size) { assert(size >= 1); setMisc(size); }
}; };
template<class T>
struct Ptr;
template<class T>
struct Root;
struct GC struct GC
{ {
@ -212,6 +130,15 @@ private:
Ptr<Object> * frontSentinel; Ptr<Object> * frontSentinel;
Ptr<Object> * backSentinel; Ptr<Object> * backSentinel;
Root<Object> * frontRootSentinel;
Root<Object> * backRootSentinel;
template<class T>
friend class Ptr;
template<class T>
friend class Root;
struct Arena struct Arena
{ {
Size size; // in words Size size; // in words
@ -317,18 +244,17 @@ public:
auto raw = arena.alloc(size); auto raw = arena.alloc(size);
if (raw) { if (raw) {
auto obj = new (raw) T(args...); auto obj = new (raw) T(args...);
//printError("ALLOC %x", obj); return obj;
return Ptr<T>((Ptr<T> *) frontSentinel->next, obj);
} }
} }
if (i == 0) { if (i == 0) {
printError("allocation of %d bytes failed, GCing...", size * WORD_SIZE); debug("allocation of %d bytes failed, GCing...", size * WORD_SIZE);
gc(); gc();
} else { } else {
Size arenaSize = std::max(arenaList.nextSize, size); Size arenaSize = std::max(arenaList.nextSize, size);
arenaList.nextSize = arenaSize * 2; // FIXME: overflow arenaList.nextSize = arenaSize * 1.5; // FIXME: overflow
printError("allocating arena of %d bytes", arenaSize * WORD_SIZE); debug("allocating arena of %d bytes", arenaSize * WORD_SIZE);
arenaList.arenas.emplace_back(arenaSize); arenaList.arenas.emplace_back(arenaSize);
} }
} }
@ -339,8 +265,159 @@ public:
void gc(); void gc();
bool isObject(void * p); bool isObject(void * p);
void assertObject(void * p);
}; };
extern GC gc; extern GC gc;
template<class T>
struct Ptr
{
private:
friend class GC;
Ptr * prev = nullptr, * next = nullptr;
T * value = nullptr;
void link()
{
prev = (Ptr *) gc.frontSentinel;
next = prev->next;
next->prev = this;
prev->next = this;
}
public:
Ptr() {
link();
}
Ptr(T * value) : value(value)
{
link();
}
Ptr(const Ptr & p)
{
auto & p2 = const_cast<Ptr &>(p);
value = p2.value;
next = &p2;
prev = p2.prev;
prev->next = this;
p2.prev = this;
}
Ptr(Ptr && p)
{
link();
value = p.value;
p.value = nullptr;
}
Ptr & operator =(const Ptr & p)
{
value = p.value;
return *this;
}
Ptr & operator =(Ptr && p)
{
value = p.value;
p.value = nullptr;
return *this;
}
Ptr & operator =(T * v)
{
value = v;
return *this;
}
~Ptr()
{
assert(next);
assert(prev);
assert(next->prev == this);
next->prev = prev;
assert(prev->next == this);
prev->next = next;
}
T * operator ->()
{
return value;
}
T * operator ->() const // FIXME
{
return value;
}
operator T * ()
{
return value;
}
operator T & ()
{
assert(value);
return *value;
}
operator bool() const
{
return value != nullptr;
}
};
template<class T>
struct Root
{
Root * prev = nullptr, * next = nullptr;
T value;
template<typename... Args>
Root(const Args & ... args)
: value{args... }
{
prev = (Root *) gc.frontRootSentinel;
next = prev->next;
next->prev = this;
prev->next = this;
}
Root(const Root & p) = delete;
Root(Root && p) = delete;
Root & operator =(const T & v) { value = v; return *this; }
~Root()
{
assert(next);
assert(prev);
assert(next->prev == this);
next->prev = prev;
assert(prev->next == this);
prev->next = next;
}
T * operator ->()
{
return &value;
}
operator T * ()
{
return &value;
}
operator T & ()
{
return value;
}
};
} }

View file

@ -115,7 +115,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
return outputs; return outputs;
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */ /* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
const Value * outTI = queryMeta("outputsToInstall"); const auto outTI = queryMeta("outputsToInstall");
if (!outTI) return outputs; if (!outTI) return outputs;
const auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'"); const auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
/* ^ this shows during `nix-env -i` right under the bad derivation */ /* ^ this shows during `nix-env -i` right under the bad derivation */
@ -194,7 +194,7 @@ Value * DrvInfo::queryMeta(const string & name)
string DrvInfo::queryMetaString(const string & name) string DrvInfo::queryMetaString(const string & name)
{ {
Value * v = queryMeta(name); auto v = queryMeta(name);
if (!v || v->type != tString) return ""; if (!v || v->type != tString) return "";
return v->string.s; return v->string.s;
} }
@ -202,7 +202,7 @@ string DrvInfo::queryMetaString(const string & name)
NixInt DrvInfo::queryMetaInt(const string & name, NixInt def) NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
{ {
Value * v = queryMeta(name); auto v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type == tInt) return v->integer; if (v->type == tInt) return v->integer;
if (v->type == tString) { if (v->type == tString) {
@ -216,7 +216,7 @@ NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def) NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
{ {
Value * v = queryMeta(name); auto v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type == tFloat) return v->fpoint; if (v->type == tFloat) return v->fpoint;
if (v->type == tString) { if (v->type == tString) {
@ -231,7 +231,7 @@ NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
bool DrvInfo::queryMetaBool(const string & name, bool def) bool DrvInfo::queryMetaBool(const string & name, bool def)
{ {
Value * v = queryMeta(name); auto v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type == tBool) return v->boolean; if (v->type == tBool) return v->boolean;
if (v->type == tString) { if (v->type == tString) {
@ -247,7 +247,7 @@ bool DrvInfo::queryMetaBool(const string & name, bool def)
void DrvInfo::setMeta(const string & name, Value * v) void DrvInfo::setMeta(const string & name, Value * v)
{ {
getMeta(); getMeta();
Bindings * old = meta; Ptr<Bindings> old = meta;
meta = Bindings::allocBindings(1 + (old ? old->size() : 0)); meta = Bindings::allocBindings(1 + (old ? old->size() : 0));
Symbol sym = state->symbols.create(name); Symbol sym = state->symbols.create(name);
if (old) if (old)
@ -260,6 +260,7 @@ void DrvInfo::setMeta(const string & name, Value * v)
/* Cache for already considered attrsets. */ /* Cache for already considered attrsets. */
// FIXME: Use Ptr?
typedef set<Bindings *> Done; typedef set<Bindings *> Done;
@ -320,24 +321,24 @@ static void getDerivations(EvalState & state, Value & vIn,
DrvInfos & drvs, Done & done, DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures) bool ignoreAssertionFailures)
{ {
Value v; Root<Value> v;
state.autoCallFunction(autoArgs, vIn, v); state.autoCallFunction(autoArgs, vIn, v);
/* Process the expression. */ /* Process the expression. */
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ; if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
else if (v.type == tAttrs) { else if (v->type == tAttrs) {
/* !!! undocumented hackery to support combining channels in /* !!! undocumented hackery to support combining channels in
nix-env.cc. */ nix-env.cc. */
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end(); bool combineChannels = v->attrs->find(state.symbols.create("_combineChannels")) != v->attrs->end();
/* Consider the attributes in sorted order to get more /* Consider the attributes in sorted order to get more
deterministic behaviour in nix-env operations (e.g. when deterministic behaviour in nix-env operations (e.g. when
there are names clashes between derivations, the derivation there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take bound to the attribute with the "lower" name should take
precedence). */ precedence). */
for (auto & i : v.attrs->lexicographicOrder()) { for (auto & i : v->attrs->lexicographicOrder()) {
debug("evaluating attribute '%1%'", i->name); debug("evaluating attribute '%1%'", i->name);
if (!std::regex_match(std::string(i->name), attrRegex)) if (!std::regex_match(std::string(i->name), attrRegex))
continue; continue;
@ -357,11 +358,11 @@ static void getDerivations(EvalState & state, Value & vIn,
} }
} }
else if (v.isList()) { else if (v->isList()) {
for (unsigned int n = 0; n < v.listSize(); ++n) { for (unsigned int n = 0; n < v->listSize(); ++n) {
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures)) if (getDerivation(state, *v->listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, *v->listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
} }
} }

View file

@ -26,7 +26,8 @@ private:
bool failed = false; // set if we get an AssertionError bool failed = false; // set if we get an AssertionError
Bindings * attrs = nullptr, * meta = nullptr; Ptr<Bindings> attrs;
Ptr<Bindings> meta;
Bindings * getMeta(); Bindings * getMeta();

View file

@ -78,7 +78,7 @@ struct Expr
virtual void show(std::ostream & str) const; virtual void show(std::ostream & str) const;
virtual void bindVars(const StaticEnv & env); virtual void bindVars(const StaticEnv & env);
virtual void eval(EvalState & state, Env & env, Value & v); virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env); virtual Ptr<Value> maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol & name); virtual void setName(Symbol & name);
}; };
@ -98,7 +98,7 @@ struct ExprInt : Expr
mkInt(v, n); mkInt(v, n);
}; };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Ptr<Value> maybeThunk(EvalState & state, Env & env) override;
}; };
struct ExprFloat : Expr struct ExprFloat : Expr
@ -110,7 +110,7 @@ struct ExprFloat : Expr
mkFloat(v, nf); mkFloat(v, nf);
}; };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Ptr<Value> maybeThunk(EvalState & state, Env & env) override;
}; };
struct ExprString : Expr struct ExprString : Expr
@ -122,7 +122,7 @@ struct ExprString : Expr
mkString(v, s); mkString(v, s);
}; };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Ptr<Value> maybeThunk(EvalState & state, Env & env) override;
}; };
/* Temporary class used during parsing of indented strings. */ /* Temporary class used during parsing of indented strings. */
@ -141,7 +141,7 @@ struct ExprPath : Expr
mkPathNoCopy(v, this->s.c_str()); mkPathNoCopy(v, this->s.c_str());
}; };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Ptr<Value> maybeThunk(EvalState & state, Env & env) override;
}; };
struct ExprVar : Expr struct ExprVar : Expr
@ -165,7 +165,7 @@ struct ExprVar : Expr
ExprVar(const Symbol & name) : name(name) { }; ExprVar(const Symbol & name) : name(name) { };
ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { }; ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Ptr<Value> maybeThunk(EvalState & state, Env & env) override;
}; };
struct ExprSelect : Expr struct ExprSelect : Expr

View file

@ -102,14 +102,13 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
if (state.store->isStorePath(path) && state.store->isValidPath(path) && isDerivation(path)) { if (state.store->isStorePath(path) && state.store->isValidPath(path) && isDerivation(path)) {
Derivation drv = readDerivation(realPath); Derivation drv = readDerivation(realPath);
Value & w = *state.allocValue(); auto w = state.allocValue();
state.mkAttrs(w, 3 + drv.outputs.size()); state.mkAttrs(w, 3 + drv.outputs.size());
Value * v2 = state.allocAttr(w, state.sDrvPath); auto v2 = state.allocAttr(w, state.sDrvPath);
mkString(*v2, path, {"=" + path}); mkString(*v2, path, {"=" + path});
v2 = state.allocAttr(w, state.sName); v2 = state.allocAttr(w, state.sName);
mkString(*v2, drv.env["name"]); mkString(*v2, drv.env["name"]);
Value * outputsVal = auto outputsVal = state.allocAttr(w, state.symbols.create("outputs"));
state.allocAttr(w, state.symbols.create("outputs"));
state.mkList(*outputsVal, drv.outputs.size()); state.mkList(*outputsVal, drv.outputs.size());
unsigned int outputs_index = 0; unsigned int outputs_index = 0;
@ -119,8 +118,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
outputsVal->listElems()[outputs_index] = state.allocValue(); outputsVal->listElems()[outputs_index] = state.allocValue();
mkString(*(outputsVal->listElems()[outputs_index++]), o.first); mkString(*(outputsVal->listElems()[outputs_index++]), o.first);
} }
w.attrs->sort(); w->attrs->sort();
Value fun; Root<Value> fun;
state.evalFile(settings.nixDataDir + "/nix/corepkgs/imported-drv-to-derivation.nix", fun); state.evalFile(settings.nixDataDir + "/nix/corepkgs/imported-drv-to-derivation.nix", fun);
state.forceFunction(fun, pos); state.forceFunction(fun, pos);
mkApp(v, fun, w); mkApp(v, fun, w);
@ -355,7 +354,6 @@ typedef list<Ptr<Value>> ValueList;
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
#if 0
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
/* Get the start set. */ /* Get the start set. */
@ -381,10 +379,10 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
no new elements are found. */ no new elements are found. */
ValueList res; ValueList res;
// `doneKeys' doesn't need to be a GC root, because its values are // `doneKeys' doesn't need to be a GC root, because its values are
// reachable from res. // reachable from res. FIXME: dubious.
set<Value *, CompareValues> doneKeys; set<Value *, CompareValues> doneKeys;
while (!workSet.empty()) { while (!workSet.empty()) {
Value * e = *(workSet.begin()); auto e = *(workSet.begin());
workSet.pop_front(); workSet.pop_front();
state.forceAttrs(*e, pos); state.forceAttrs(*e, pos);
@ -395,19 +393,19 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
throw EvalError(format("attribute 'key' required, at %1%") % pos); throw EvalError(format("attribute 'key' required, at %1%") % pos);
state.forceValue(*key->value); state.forceValue(*key->value);
if (doneKeys.find(key->value) != doneKeys.end()) continue; if (doneKeys.count(key->value)) continue;
doneKeys.insert(key->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; Root<Value> call;
mkApp(call, *op->value, *e); mkApp(call, *op->value, *e);
state.forceList(call, pos); state.forceList(call, pos);
/* Add the values returned by the operator to the work set. */ /* Add the values returned by the operator to the work set. */
for (unsigned int n = 0; n < call.listSize(); ++n) { for (unsigned int n = 0; n < call->listSize(); ++n) {
state.forceValue(*call.listElems()[n]); state.forceValue(*call->listElems()[n]);
workSet.push_back(call.listElems()[n]); workSet.push_back(call->listElems()[n]);
} }
} }
@ -416,8 +414,6 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
unsigned int n = 0; unsigned int n = 0;
for (auto & i : res) for (auto & i : res)
v.listElems()[n++] = i; v.listElems()[n++] = i;
#endif
abort();
} }
@ -940,7 +936,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
state.mkAttrs(v, entries.size()); state.mkAttrs(v, entries.size());
for (auto & ent : entries) { for (auto & ent : entries) {
Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name)); auto ent_val = state.allocAttr(v, state.symbols.create(ent.name));
if (ent.type == DT_UNKNOWN) if (ent.type == DT_UNKNOWN)
ent.type = getFileType(path + "/" + ent.name); ent.type = getFileType(path + "/" + ent.name);
mkStringNoCopy(*ent_val, mkStringNoCopy(*ent_val,
@ -1030,20 +1026,20 @@ static void addPath(EvalState & state, const Pos & pos, const string & name, con
/* Call the filter function. The first argument is the path, /* Call the filter function. The first argument is the path,
the second is a string indicating the type of the file. */ the second is a string indicating the type of the file. */
Value arg1; auto arg1 = state.allocValue();
mkString(arg1, path); mkString(arg1, path);
Value fun2; Root<Value> fun2;
state.callFunction(*filterFun, arg1, fun2, noPos); state.callFunction(*filterFun, arg1, fun2, noPos);
Value arg2; auto arg2 = state.allocValue();
mkString(arg2, mkString(arg2,
S_ISREG(st.st_mode) ? "regular" : S_ISREG(st.st_mode) ? "regular" :
S_ISDIR(st.st_mode) ? "directory" : S_ISDIR(st.st_mode) ? "directory" :
S_ISLNK(st.st_mode) ? "symlink" : S_ISLNK(st.st_mode) ? "symlink" :
"unknown" /* not supported, will fail! */); "unknown" /* not supported, will fail! */);
Value res; Root<Value> res;
state.callFunction(fun2, arg2, res, noPos); state.callFunction(fun2, arg2, res, noPos);
return state.forceBool(res, pos); return state.forceBool(res, pos);
@ -1355,8 +1351,8 @@ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Va
state.mkAttrs(v, args[1]->attrs->size()); state.mkAttrs(v, args[1]->attrs->size());
for (auto & i : *args[1]->attrs) { for (auto & i : *args[1]->attrs) {
Value * vName = state.allocValue(); auto vName = state.allocValue();
Value * vFun2 = state.allocValue(); auto vFun2 = state.allocValue();
mkString(*vName, i.name); mkString(*vName, i.name);
mkApp(*vFun2, *args[0], *vName); mkApp(*vFun2, *args[0], *vName);
mkApp(*state.allocAttr(v, i.name), *vFun2, *i.value); mkApp(*state.allocAttr(v, i.name), *vFun2, *i.value);
@ -1443,7 +1439,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
bool same = true; bool same = true;
for (unsigned int n = 0; n < args[1]->listSize(); ++n) { for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
Value res; Root<Value> res;
state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos); state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos);
if (state.forceBool(res, pos)) if (state.forceBool(res, pos))
vs[k++] = args[1]->listElems()[n]; vs[k++] = args[1]->listElems()[n];
@ -1498,12 +1494,12 @@ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args,
state.forceList(*args[2], pos); state.forceList(*args[2], pos);
if (args[2]->listSize()) { if (args[2]->listSize()) {
Value * vCur = args[1]; Ptr<Value> vCur = args[1];
for (unsigned int n = 0; n < args[2]->listSize(); ++n) { for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
Value vTmp; Root<Value> vTmp;
state.callFunction(*args[0], *vCur, vTmp, pos); state.callFunction(*args[0], *vCur, vTmp, pos);
vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue(); vCur = n == args[2]->listSize() - 1 ? Ptr(&v) : state.allocValue();
state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos); state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos);
} }
state.forceValue(v); state.forceValue(v);
@ -1519,7 +1515,7 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
Value vTmp; Root<Value> vTmp;
for (unsigned int n = 0; n < args[1]->listSize(); ++n) { for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos); state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos);
bool res = state.forceBool(vTmp, pos); bool res = state.forceBool(vTmp, pos);
@ -1555,7 +1551,7 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
state.mkList(v, len); state.mkList(v, len);
for (unsigned int n = 0; n < (unsigned int) len; ++n) { for (unsigned int n = 0; n < (unsigned int) len; ++n) {
Value * arg = state.allocValue(); auto arg = state.allocValue();
mkInt(*arg, n); mkInt(*arg, n);
mkApp(*(v.listElems()[n] = state.allocValue()), *args[0], *arg); mkApp(*(v.listElems()[n] = state.allocValue()), *args[0], *arg);
} }
@ -1584,7 +1580,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
if (args[0]->type == tPrimOp && args[0]->primOp->fun == prim_lessThan) if (args[0]->type == tPrimOp && args[0]->primOp->fun == prim_lessThan)
return CompareValues()(a, b); return CompareValues()(a, b);
Value vTmp1, vTmp2; Root<Value> vTmp1, vTmp2;
state.callFunction(*args[0], *a, vTmp1, pos); state.callFunction(*args[0], *a, vTmp1, pos);
state.callFunction(vTmp1, *b, vTmp2, pos); state.callFunction(vTmp1, *b, vTmp2, pos);
return state.forceBool(vTmp2, pos); return state.forceBool(vTmp2, pos);
@ -1610,7 +1606,7 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V
for (unsigned int n = 0; n < len; ++n) { for (unsigned int n = 0; n < len; ++n) {
auto vElem = args[1]->listElems()[n]; auto vElem = args[1]->listElems()[n];
state.forceValue(*vElem); state.forceValue(*vElem);
Value res; Root<Value> res;
state.callFunction(*args[0], *vElem, res, pos); state.callFunction(*args[0], *vElem, res, pos);
if (state.forceBool(res, pos)) if (state.forceBool(res, pos))
right.push_back(vElem); right.push_back(vElem);
@ -1620,13 +1616,13 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V
state.mkAttrs(v, 2); state.mkAttrs(v, 2);
Value * vRight = state.allocAttr(v, state.sRight); auto vRight = state.allocAttr(v, state.sRight);
auto rsize = right.size(); auto rsize = right.size();
state.mkList(*vRight, rsize); state.mkList(*vRight, rsize);
if (rsize) if (rsize)
memcpy(vRight->listElems(), right.data(), sizeof(Value *) * rsize); memcpy(vRight->listElems(), right.data(), sizeof(Value *) * rsize);
Value * vWrong = state.allocAttr(v, state.sWrong); auto vWrong = state.allocAttr(v, state.sWrong);
auto wsize = wrong.size(); auto wsize = wrong.size();
state.mkList(*vWrong, wsize); state.mkList(*vWrong, wsize);
if (wsize) if (wsize)
@ -1644,22 +1640,23 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
auto nrLists = args[1]->listSize(); auto nrLists = args[1]->listSize();
Value lists[nrLists]; // FIXME: Root<>[] is inefficient
Root<Value> lists[nrLists];
size_t len = 0; size_t len = 0;
for (unsigned int n = 0; n < nrLists; ++n) { for (unsigned int n = 0; n < nrLists; ++n) {
Value * vElem = args[1]->listElems()[n]; Value * vElem = args[1]->listElems()[n];
state.callFunction(*args[0], *vElem, lists[n], pos); state.callFunction(*args[0], *vElem, lists[n], pos);
state.forceList(lists[n], pos); state.forceList(lists[n], pos);
len += lists[n].listSize(); len += lists[n]->listSize();
} }
state.mkList(v, len); state.mkList(v, len);
auto out = v.listElems(); auto out = v.listElems();
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) { for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
auto l = lists[n].listSize(); auto l = lists[n]->listSize();
if (l) if (l)
memcpy(out + pos, lists[n].listElems(), l * sizeof(Value *)); memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *));
pos += l; pos += l;
} }
} }
@ -2130,7 +2127,7 @@ void EvalState::createBaseEnv()
auto vThrow = addPrimOp("throw", 1, prim_throw); auto vThrow = addPrimOp("throw", 1, prim_throw);
auto addPurityError = [&](const std::string & name) { auto addPurityError = [&](const std::string & name) {
Value * v2 = allocValue(); auto v2 = allocValue();
mkString(*v2, fmt("'%s' is not allowed in pure evaluation mode", name)); mkString(*v2, fmt("'%s' is not allowed in pure evaluation mode", name));
mkApp(v, *vThrow, *v2); mkApp(v, *vThrow, *v2);
addConstant(name, v); addConstant(name, v);
@ -2161,7 +2158,7 @@ void EvalState::createBaseEnv()
// Miscellaneous // Miscellaneous
auto vScopedImport = addPrimOp("scopedImport", 2, prim_scopedImport); auto vScopedImport = addPrimOp("scopedImport", 2, prim_scopedImport);
Value * v2 = allocValue(); auto v2 = allocValue();
mkAttrs(*v2, 0); mkAttrs(*v2, 0);
mkApp(v, *vScopedImport, *v2); mkApp(v, *vScopedImport, *v2);
forceValue(v); forceValue(v);

View file

@ -28,7 +28,8 @@ typedef double NixFloat;
class ExternalValueBase class ExternalValueBase
{ {
friend std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); friend std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
protected:
protected:
/* Print out the value */ /* Print out the value */
virtual std::ostream & print(std::ostream & str) const = 0; virtual std::ostream & print(std::ostream & str) const = 0;
@ -121,7 +122,14 @@ struct Value : Object
NixFloat fpoint; NixFloat fpoint;
}; };
Value() : Object(tBlackhole, 0) { } private:
Value() : Object(tNull, 0) {}
friend class GC;
template<typename T> friend class Root;
public:
bool isList() const bool isList() const
{ {

View file

@ -245,7 +245,7 @@ static void _main(int argc, char * * argv)
auto state = std::make_unique<EvalState>(myArgs.searchPath, store); auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
state->repair = repair; state->repair = repair;
Bindings & autoArgs = *myArgs.getAutoArgs(*state); auto autoArgs = myArgs.getAutoArgs(*state);
if (packages) { if (packages) {
std::ostringstream joined; std::ostringstream joined;
@ -293,13 +293,13 @@ static void _main(int argc, char * * argv)
if (attrPaths.empty()) attrPaths = {""}; if (attrPaths.empty()) attrPaths = {""};
for (auto e : exprs) { for (auto e : exprs) {
Value vRoot; Root<Value> vRoot;
state->eval(e, vRoot); state->eval(e, vRoot);
for (auto & i : attrPaths) { for (auto & i : attrPaths) {
Value & v(*findAlongAttrPath(*state, i, autoArgs, vRoot)); auto v = findAlongAttrPath(*state, i, *autoArgs, vRoot);
state->forceValue(v); state->forceValue(v);
getDerivations(*state, v, "", autoArgs, drvs, false); getDerivations(*state, v, "", *autoArgs, drvs, false);
} }
} }
@ -339,7 +339,7 @@ static void _main(int argc, char * * argv)
try { try {
auto expr = state->parseExprFromString("(import <nixpkgs> {}).bashInteractive", absPath(".")); auto expr = state->parseExprFromString("(import <nixpkgs> {}).bashInteractive", absPath("."));
Value v; Root<Value> v;
state->eval(expr, v); state->eval(expr, v);
auto drv = getDerivation(*state, v, false); auto drv = getDerivation(*state, v, false);

View file

@ -46,7 +46,7 @@ struct InstallSourceInfo
Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */ Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */
Path profile; /* for srcProfile */ Path profile; /* for srcProfile */
string systemFilter; /* for srcNixExprDrvs */ string systemFilter; /* for srcNixExprDrvs */
Bindings * autoArgs; Ptr<Bindings> autoArgs;
}; };
@ -131,7 +131,7 @@ static void getAllExprs(EvalState & state,
attrs.insert(attrName); attrs.insert(attrName);
/* Load the expression on demand. */ /* Load the expression on demand. */
Value & vFun = state.getBuiltin("import"); Value & vFun = state.getBuiltin("import");
Value & vArg(*state.allocValue()); auto vArg = state.allocValue();
mkString(vArg, path2); mkString(vArg, path2);
if (v.attrs->size() == v.attrs->capacity()) if (v.attrs->size() == v.attrs->capacity())
throw Error(format("too many Nix expressions in directory '%1%'") % path); throw Error(format("too many Nix expressions in directory '%1%'") % path);
@ -176,10 +176,10 @@ static void loadDerivations(EvalState & state, Path nixExprPath,
string systemFilter, Bindings & autoArgs, string systemFilter, Bindings & autoArgs,
const string & pathPrefix, DrvInfos & elems) const string & pathPrefix, DrvInfos & elems)
{ {
Value vRoot; auto vRoot = state.allocValue();
loadSourceExpr(state, nixExprPath, vRoot); loadSourceExpr(state, nixExprPath, vRoot);
Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot)); auto v = findAlongAttrPath(state, pathPrefix, autoArgs, vRoot);
getDerivations(state, v, pathPrefix, autoArgs, elems, true); getDerivations(state, v, pathPrefix, autoArgs, elems, true);
@ -360,13 +360,14 @@ static void queryInstSources(EvalState & state,
(import ./foo.nix)' = `(import ./foo.nix).bar'. */ (import ./foo.nix)' = `(import ./foo.nix).bar'. */
case srcNixExprs: { case srcNixExprs: {
Value vArg; auto vArg = state.allocValue();
loadSourceExpr(state, instSource.nixExprPath, vArg); loadSourceExpr(state, instSource.nixExprPath, vArg);
for (auto & i : args) { for (auto & i : args) {
Expr * eFun = state.parseExprFromString(i, absPath(".")); Expr * eFun = state.parseExprFromString(i, absPath("."));
Value vFun, vTmp; auto vFun = state.allocValue();
state.eval(eFun, vFun); state.eval(eFun, vFun);
auto vTmp = state.allocValue();
mkApp(vTmp, vFun, vArg); mkApp(vTmp, vFun, vArg);
getDerivations(state, vTmp, "", *instSource.autoArgs, elems, true); getDerivations(state, vTmp, "", *instSource.autoArgs, elems, true);
} }
@ -416,10 +417,10 @@ static void queryInstSources(EvalState & state,
} }
case srcAttrPath: { case srcAttrPath: {
Value vRoot; Root<Value> vRoot;
loadSourceExpr(state, instSource.nixExprPath, vRoot); loadSourceExpr(state, instSource.nixExprPath, vRoot);
for (auto & i : args) { for (auto & i : args) {
Value & v(*findAlongAttrPath(state, i, *instSource.autoArgs, vRoot)); auto v = findAlongAttrPath(state, i, *instSource.autoArgs, vRoot);
getDerivations(state, v, "", *instSource.autoArgs, elems, true); getDerivations(state, v, "", *instSource.autoArgs, elems, true);
} }
break; break;
@ -639,7 +640,7 @@ static void opUpgrade(Globals & globals, Strings opFlags, Strings opArgs)
static void setMetaFlag(EvalState & state, DrvInfo & drv, static void setMetaFlag(EvalState & state, DrvInfo & drv,
const string & name, const string & value) const string & name, const string & value)
{ {
Value * v = state.allocValue(); auto v = state.allocValue();
mkString(*v, value.c_str()); mkString(*v, value.c_str());
drv.setMeta(name, v); drv.setMeta(name, v);
} }

View file

@ -17,7 +17,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
DrvInfos elems; DrvInfos elems;
Path manifestFile = userEnv + "/manifest.nix"; Path manifestFile = userEnv + "/manifest.nix";
if (pathExists(manifestFile)) { if (pathExists(manifestFile)) {
Value v; Root<Value> v;
state.evalFile(manifestFile, v); state.evalFile(manifestFile, v);
auto bindings = Bindings::allocBindings(0); auto bindings = Bindings::allocBindings(0);
getDerivations(state, v, "", bindings, elems, false); getDerivations(state, v, "", bindings, elems, false);
@ -105,26 +105,27 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
fmt("%1%", (Value &) manifest), references); fmt("%1%", (Value &) manifest), references);
/* Get the environment builder expression. */ /* Get the environment builder expression. */
Value envBuilder; auto envBuilder = state.allocValue();
state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder); state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder);
/* 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; auto args = state.allocValue();
Root<Value> topLevel;
state.mkAttrs(args, 3); state.mkAttrs(args, 3);
mkString(*state.allocAttr(args, state.symbols.create("manifest")), mkString(*state.allocAttr(args, state.symbols.create("manifest")),
manifestFile, {manifestFile}); manifestFile, {manifestFile});
args.attrs->push_back(Attr(state.symbols.create("derivations"), (Value *) manifest)); args->attrs->push_back(Attr(state.symbols.create("derivations"), (Value *) manifest));
args.attrs->sort(); args->attrs->sort();
mkApp(topLevel, envBuilder, args); mkApp(topLevel, envBuilder, args);
/* Evaluate it. */ /* Evaluate it. */
debug("evaluating user environment builder"); debug("evaluating user environment builder");
state.forceValue(topLevel); state.forceValue(topLevel);
PathSet context; PathSet context;
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); Attr & aDrvPath(*topLevel->attrs->find(state.sDrvPath));
Path topLevelDrv = state.coerceToPath(aDrvPath.pos ? *(aDrvPath.pos) : noPos, *(aDrvPath.value), context); Path topLevelDrv = state.coerceToPath(aDrvPath.pos ? *(aDrvPath.pos) : noPos, *(aDrvPath.value), context);
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); Attr & aOutPath(*topLevel->attrs->find(state.sOutPath));
Path topLevelOut = state.coerceToPath(aOutPath.pos ? *(aOutPath.pos) : noPos, *(aOutPath.value), context); Path topLevelOut = state.coerceToPath(aOutPath.pos ? *(aOutPath.pos) : noPos, *(aOutPath.value), context);
/* Realise the resulting store expression. */ /* Realise the resulting store expression. */

View file

@ -35,18 +35,18 @@ void processExpr(EvalState & state, const Strings & attrPaths,
return; return;
} }
Value vRoot; Root<Value> vRoot;
state.eval(e, vRoot); state.eval(e, vRoot);
for (auto & i : attrPaths) { for (auto & i : attrPaths) {
Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot)); auto v = findAlongAttrPath(state, i, autoArgs, vRoot);
state.forceValue(v); state.forceValue(v);
PathSet context; PathSet context;
if (evalOnly) { if (evalOnly) {
Value vRes; Root<Value> vRes;
if (autoArgs.empty()) if (autoArgs.empty())
vRes = v; vRes = *v;
else else
state.autoCallFunction(autoArgs, v, vRes); state.autoCallFunction(autoArgs, v, vRes);
if (output == okXML) if (output == okXML)
@ -55,7 +55,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
printValueAsJSON(state, strict, vRes, std::cout, context); printValueAsJSON(state, strict, vRes, std::cout, context);
else { else {
if (strict) state.forceValueDeep(vRes); if (strict) state.forceValueDeep(vRes);
std::cout << vRes << std::endl; std::cout << *vRes << std::endl;
} }
} else { } else {
DrvInfos drvs; DrvInfos drvs;
@ -159,7 +159,7 @@ static int _main(int argc, char * * argv)
auto state = std::make_unique<EvalState>(myArgs.searchPath, store); auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
state->repair = repair; state->repair = repair;
Bindings & autoArgs = *myArgs.getAutoArgs(*state); auto autoArgs = myArgs.getAutoArgs(*state);
if (attrPaths.empty()) attrPaths = {""}; if (attrPaths.empty()) attrPaths = {""};
@ -174,7 +174,7 @@ static int _main(int argc, char * * argv)
if (readStdin) { if (readStdin) {
Expr * e = state->parseStdin(); Expr * e = state->parseStdin();
processExpr(*state, attrPaths, parseOnly, strict, autoArgs, processExpr(*state, attrPaths, parseOnly, strict, *autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e); evalOnly, outputKind, xmlOutputSourceLocation, e);
} else if (files.empty() && !fromArgs) } else if (files.empty() && !fromArgs)
files.push_back("./default.nix"); files.push_back("./default.nix");
@ -183,7 +183,7 @@ static int _main(int argc, char * * argv)
Expr * e = fromArgs Expr * e = fromArgs
? state->parseExprFromString(i, absPath(".")) ? state->parseExprFromString(i, absPath("."))
: state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, i)))); : state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, i))));
processExpr(*state, attrPaths, parseOnly, strict, autoArgs, processExpr(*state, attrPaths, parseOnly, strict, *autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e); evalOnly, outputKind, xmlOutputSourceLocation, e);
} }

View file

@ -30,12 +30,12 @@ string resolveMirrorUri(EvalState & state, string uri)
if (p == string::npos) throw Error("invalid mirror URI"); if (p == string::npos) throw Error("invalid mirror URI");
string mirrorName(s, 0, p); string mirrorName(s, 0, p);
Value vMirrors; Root<Value> vMirrors;
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors); state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
state.forceAttrs(vMirrors); state.forceAttrs(vMirrors);
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); auto mirrorList = vMirrors->attrs->find(state.symbols.create(mirrorName));
if (mirrorList == vMirrors.attrs->end()) if (mirrorList == vMirrors->attrs->end())
throw Error(format("unknown mirror name '%1%'") % mirrorName); throw Error(format("unknown mirror name '%1%'") % mirrorName);
state.forceList(*mirrorList->value); state.forceList(*mirrorList->value);
@ -106,7 +106,7 @@ static int _main(int argc, char * * argv)
auto store = openStore(); auto store = openStore();
auto state = std::make_unique<EvalState>(myArgs.searchPath, store); auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
Bindings & autoArgs = *myArgs.getAutoArgs(*state); auto autoArgs = myArgs.getAutoArgs(*state);
/* If -A is given, get the URI from the specified Nix /* If -A is given, get the URI from the specified Nix
expression. */ expression. */
@ -117,14 +117,14 @@ static int _main(int argc, char * * argv)
uri = args[0]; uri = args[0];
} else { } else {
Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0])); Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0]));
Value vRoot; Root<Value> vRoot;
state->evalFile(path, vRoot); state->evalFile(path, vRoot);
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot)); auto v = findAlongAttrPath(*state, attrPath, *autoArgs, vRoot);
state->forceAttrs(v); state->forceAttrs(v);
/* Extract the URI. */ /* Extract the URI. */
auto attr = v.attrs->find(state->symbols.create("urls")); auto attr = v->attrs->find(state->symbols.create("urls"));
if (attr == v.attrs->end()) if (attr == v->attrs->end())
throw Error("attribute set does not contain a 'urls' attribute"); throw Error("attribute set does not contain a 'urls' attribute");
state->forceList(*attr->value); state->forceList(*attr->value);
if (attr->value->listSize() < 1) if (attr->value->listSize() < 1)
@ -132,16 +132,16 @@ static int _main(int argc, char * * argv)
uri = state->forceString(*attr->value->listElems()[0]); uri = state->forceString(*attr->value->listElems()[0]);
/* Extract the hash mode. */ /* Extract the hash mode. */
attr = v.attrs->find(state->symbols.create("outputHashMode")); attr = v->attrs->find(state->symbols.create("outputHashMode"));
if (attr == v.attrs->end()) if (attr == v->attrs->end())
printInfo("warning: this does not look like a fetchurl call"); printInfo("warning: this does not look like a fetchurl call");
else else
unpack = state->forceString(*attr->value) == "recursive"; unpack = state->forceString(*attr->value) == "recursive";
/* Extract the name. */ /* Extract the name. */
if (name.empty()) { if (name.empty()) {
attr = v.attrs->find(state->symbols.create("name")); attr = v->attrs->find(state->symbols.create("name"));
if (attr != v.attrs->end()) if (attr != v->attrs->end())
name = state->forceString(*attr->value); name = state->forceString(*attr->value);
} }
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "args.hh" #include "args.hh"
#include "gc.hh"
#include "common-eval-args.hh" #include "common-eval-args.hh"
namespace nix { namespace nix {
@ -66,7 +67,7 @@ struct Installable
Buildable toBuildable(); Buildable toBuildable();
virtual Value * toValue(EvalState & state) virtual Ptr<Value> toValue(EvalState & state)
{ {
throw Error("argument '%s' cannot be evaluated", what()); throw Error("argument '%s' cannot be evaluated", what());
} }
@ -82,7 +83,7 @@ struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs
are installing. This is either the file specified by --file, are installing. This is either the file specified by --file,
or an attribute set constructed from $NIX_PATH, e.g. { nixpkgs or an attribute set constructed from $NIX_PATH, e.g. { nixpkgs
= import ...; bla = import ...; }. */ = import ...; bla = import ...; }. */
Value * getSourceExpr(EvalState & state); Ptr<Value> getSourceExpr(EvalState & state);
ref<EvalState> getEvalState(); ref<EvalState> getEvalState();
@ -90,7 +91,7 @@ private:
std::shared_ptr<EvalState> evalState; std::shared_ptr<EvalState> evalState;
Value * vSourceExpr = 0; Ptr<Value> vSourceExpr;
}; };
enum RealiseMode { Build, NoBuild, DryRun }; enum RealiseMode { Build, NoBuild, DryRun };

View file

@ -36,7 +36,7 @@ struct CmdEdit : InstallableCommand
auto v = installable->toValue(*state); auto v = installable->toValue(*state);
Value * v2; Ptr<Value> v2;
try { try {
auto dummyArgs = Bindings::allocBindings(0); auto dummyArgs = Bindings::allocBindings(0);
v2 = findAlongAttrPath(*state, "meta.position", dummyArgs, *v); v2 = findAlongAttrPath(*state, "meta.position", dummyArgs, *v);

View file

@ -22,7 +22,7 @@ SourceExprCommand::SourceExprCommand()
.dest(&file); .dest(&file);
} }
Value * SourceExprCommand::getSourceExpr(EvalState & state) Ptr<Value> SourceExprCommand::getSourceExpr(EvalState & state)
{ {
if (vSourceExpr) return vSourceExpr; if (vSourceExpr) return vSourceExpr;
@ -57,9 +57,9 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state)
state.getBuiltin("import"), state.getBuiltin("import"),
mkString(*state.allocValue(), res.second)); mkString(*state.allocValue(), res.second));
#endif #endif
Value * v1 = state.allocValue(); auto v1 = state.allocValue();
mkPrimOpApp(*v1, state.getBuiltin("findFile"), state.getBuiltin("nixPath")); mkPrimOpApp(*v1, state.getBuiltin("findFile"), state.getBuiltin("nixPath"));
Value * v2 = state.allocValue(); auto v2 = state.allocValue();
mkApp(*v2, *v1, mkString(*state.allocValue(), i.first)); mkApp(*v2, *v1, mkString(*state.allocValue(), i.first));
mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)), mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)),
state.getBuiltin("import"), *v2); state.getBuiltin("import"), *v2);
@ -155,7 +155,7 @@ struct InstallableExpr : InstallableValue
std::string what() override { return text; } std::string what() override { return text; }
Value * toValue(EvalState & state) override Ptr<Value> toValue(EvalState & state) override
{ {
auto v = state.allocValue(); auto v = state.allocValue();
state.eval(state.parseExprFromString(text, absPath(".")), *v); state.eval(state.parseExprFromString(text, absPath(".")), *v);
@ -173,13 +173,13 @@ struct InstallableAttrPath : InstallableValue
std::string what() override { return attrPath; } std::string what() override { return attrPath; }
Value * toValue(EvalState & state) override Ptr<Value> toValue(EvalState & state) override
{ {
auto source = cmd.getSourceExpr(state); auto source = cmd.getSourceExpr(state);
Bindings & autoArgs = *cmd.getAutoArgs(state); auto autoArgs = cmd.getAutoArgs(state);
Value * v = findAlongAttrPath(state, attrPath, autoArgs, *source); auto v = findAlongAttrPath(state, attrPath, *autoArgs, *source);
state.forceValue(*v); state.forceValue(*v);
return v; return v;

View file

@ -38,7 +38,7 @@ struct NixRepl
{ {
string curDir; string curDir;
EvalState state; EvalState state;
Bindings * autoArgs; Ptr<Bindings> autoArgs;
Strings loadedFiles; Strings loadedFiles;
@ -304,11 +304,11 @@ StringSet NixRepl::completePrefix(string prefix)
string cur2 = string(cur, dot + 1); string cur2 = string(cur, dot + 1);
Expr * e = parseString(expr); Expr * e = parseString(expr);
Value v; Root<Value> v;
e->eval(state, *env, v); e->eval(state, *env, v);
state.forceAttrs(v); state.forceAttrs(v);
for (auto & i : *v.attrs) { for (auto & i : *v->attrs) {
string name = i.name; string name = i.name;
if (string(name, 0, cur2.size()) != cur2) continue; if (string(name, 0, cur2.size()) != cur2) continue;
completions.insert(prev + expr + "." + name); completions.insert(prev + expr + "." + name);
@ -404,7 +404,7 @@ bool NixRepl::processLine(string line)
} }
else if (command == ":a" || command == ":add") { else if (command == ":a" || command == ":add") {
Value v; Root<Value> v;
evalString(arg, v); evalString(arg, v);
addAttrsToScope(v); addAttrsToScope(v);
} }
@ -420,12 +420,12 @@ bool NixRepl::processLine(string line)
} }
else if (command == ":t") { else if (command == ":t") {
Value v; Root<Value> v;
evalString(arg, v); evalString(arg, v);
std::cout << showType(v) << std::endl; std::cout << showType(v) << std::endl;
} else if (command == ":u") { } else if (command == ":u") {
Value v, f, result; Root<Value> v, f, result;
evalString(arg, v); evalString(arg, v);
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f); evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
state.callFunction(f, v, result, Pos()); state.callFunction(f, v, result, Pos());
@ -435,7 +435,7 @@ bool NixRepl::processLine(string line)
} }
else if (command == ":b" || command == ":i" || command == ":s") { else if (command == ":b" || command == ":i" || command == ":s") {
Value v; Root<Value> v;
evalString(arg, v); evalString(arg, v);
Path drvPath = getDerivationPath(v); Path drvPath = getDerivationPath(v);
@ -457,7 +457,7 @@ bool NixRepl::processLine(string line)
} }
else if (command == ":p" || command == ":print") { else if (command == ":p" || command == ":print") {
Value v; Root<Value> v;
evalString(arg, v); evalString(arg, v);
printValue(std::cout, v, 1000000000) << std::endl; printValue(std::cout, v, 1000000000) << std::endl;
} }
@ -483,7 +483,7 @@ bool NixRepl::processLine(string line)
v.thunk.expr = e; v.thunk.expr = e;
addVarToScope(state.symbols.create(name), v); addVarToScope(state.symbols.create(name), v);
} else { } else {
Value v; Root<Value> v;
evalString(line, v); evalString(line, v);
printValue(std::cout, v, 1) << std::endl; printValue(std::cout, v, 1) << std::endl;
} }
@ -497,9 +497,9 @@ void NixRepl::loadFile(const Path & path)
{ {
loadedFiles.remove(path); loadedFiles.remove(path);
loadedFiles.push_back(path); loadedFiles.push_back(path);
Value v, v2; Root<Value> v, v2;
state.evalFile(lookupFileArg(state, path), v); state.evalFile(lookupFileArg(state, path), v);
state.autoCallFunction(*autoArgs, v, v2); state.autoCallFunction(autoArgs, v, v2);
addAttrsToScope(v2); addAttrsToScope(v2);
} }

View file

@ -235,12 +235,12 @@ struct CmdSearch : SourceExprCommand, MixJSON
warn("using cached results; pass '-u' to update the cache"); warn("using cached results; pass '-u' to update the cache");
Value vRoot; Root<Value> vRoot;
parseJSON(*state, readFile(jsonCacheFileName), vRoot); parseJSON(*state, readFile(jsonCacheFileName), vRoot);
fromCache = true; fromCache = true;
doExpr(&vRoot, "", true, nullptr); doExpr(&*vRoot, "", true, nullptr);
} }
else { else {

View file

@ -35,6 +35,9 @@ export HAVE_SODIUM="@HAVE_SODIUM@"
export version=@PACKAGE_VERSION@ export version=@PACKAGE_VERSION@
export system=@system@ export system=@system@
# Make it more likely to trigger GC bugs.
export GC_INITIAL_HEAP_SIZE=10
cacheDir=$TEST_ROOT/binary-cache cacheDir=$TEST_ROOT/binary-cache
readLink() { readLink() {