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-15 18:40:35 +02:00
parent bb6e6923f2
commit 4237414f4d
23 changed files with 844 additions and 295 deletions

View file

@ -7,26 +7,25 @@
namespace nix {
/* Allocate a new array of attributes for an attribute set with a specific
capacity. The space is implicitly reserved after the Bindings
structure. */
Bindings * EvalState::allocBindings(size_t capacity)
/* Allocate a new array of attributes for an attribute set with a
specific capacity. The space is implicitly reserved after the
Bindings structure. */
Ptr<Bindings> Bindings::allocBindings(size_t capacity)
{
if (capacity > std::numeric_limits<Bindings::size_t>::max())
if (capacity >= 1UL << Object::miscBits)
throw Error("attribute set of size %d is too big", capacity);
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
return gc.alloc<Bindings>(Bindings::wordsFor(capacity), capacity);
}
void EvalState::mkAttrs(Value & v, size_t capacity)
{
v.type = tAttrs;
if (capacity == 0) {
v = vEmptySet;
v.attrs = emptyBindings;
return;
}
clearValue(v);
v.type = tAttrs;
v.attrs = allocBindings(capacity);
v.attrs = Bindings::allocBindings(capacity);
nrAttrsets++;
nrAttrsInAttrsets += capacity;
}

View file

@ -2,6 +2,7 @@
#include "nixexpr.hh"
#include "symbol-table.hh"
#include "gc.hh"
#include <algorithm>
@ -30,19 +31,22 @@ struct Attr
by its size and its capacity, the capacity being the number of Attr
elements allocated after this structure, while the size corresponds to
the number of elements already inserted in this structure. */
class Bindings
class Bindings : public Object
{
public:
typedef uint32_t size_t;
private:
size_t size_, capacity_;
// FIXME: eliminate size_. We can just rely on capacity by sorting
// null entries towards the end of the vector.
size_t size_;
Attr attrs[0];
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
public:
// FIXME: make private
Bindings(size_t capacity) : Object(tBindings, capacity), size_(0) { }
Bindings(const Bindings & bindings) = delete;
public:
size_t size() const { return size_; }
bool empty() const { return !size_; }
@ -51,7 +55,7 @@ public:
void push_back(const Attr & attr)
{
assert(size_ < capacity_);
assert(size_ < capacity());
attrs[size_++] = attr;
}
@ -73,7 +77,7 @@ public:
void sort();
size_t capacity() { return capacity_; }
size_t capacity() const { return getMisc(); }
/* Returns the attributes in lexicographically sorted order. */
std::vector<const Attr *> lexicographicOrder() const
@ -88,7 +92,19 @@ public:
return res;
}
friend class EvalState;
Size words() const
{
return wordsFor(capacity());
}
static Size wordsFor(size_t capacity)
{
return 2 + 3 * capacity; // FIXME
}
static Ptr<Bindings> allocBindings(size_t capacity);
friend class GC;
};

View file

@ -30,7 +30,7 @@ MixEvalArgs::MixEvalArgs()
Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
{
Bindings * res = state.allocBindings(autoArgs.size());
Bindings * res = Bindings::allocBindings(autoArgs.size());
for (auto & i : autoArgs) {
Value * v = state.allocValue();
if (i.second[0] == 'E')

View file

@ -78,18 +78,5 @@ inline void EvalState::forceList(Value & v, const Pos & pos)
throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
}
/* Note: Various places expect the allocated memory to be zeroed. */
inline void * allocBytes(size_t n)
{
void * p;
#if HAVE_BOEHMGC
p = GC_MALLOC(n);
#else
p = calloc(n, 1);
#endif
if (!p) throw std::bad_alloc();
return p;
}
}

View file

@ -19,24 +19,14 @@
#include <sys/time.h>
#include <sys/resource.h>
#if HAVE_BOEHMGC
#include <gc/gc.h>
#include <gc/gc_cpp.h>
#endif
namespace nix {
// FIXME
static char * dupString(const char * s)
{
char * t;
#if HAVE_BOEHMGC
t = GC_STRDUP(s);
#else
t = strdup(s);
#endif
if (!t) throw std::bad_alloc();
return t;
}
@ -85,6 +75,7 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
str << "}";
break;
}
case tList0:
case tList1:
case tList2:
case tListN:
@ -108,9 +99,11 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
case tPrimOpApp:
str << "<PRIMOP-APP>";
break;
#if 0
case tExternal:
str << *v.external;
break;
#endif
case tFloat:
str << v.fpoint;
break;
@ -130,10 +123,10 @@ std::ostream & operator << (std::ostream & str, const Value & v)
}
const Value *getPrimOp(const Value &v) {
const Value * getPrimOp(const Value & v) {
const Value * primOp = &v;
while (primOp->type == tPrimOpApp) {
primOp = primOp->primOpApp.left;
primOp = primOp->app.left;
}
assert(primOp->type == tPrimOp);
return primOp;
@ -149,7 +142,7 @@ string showType(const Value & v)
case tPath: return "a path";
case tNull: return "null";
case tAttrs: return "a set";
case tList1: case tList2: case tListN: return "a list";
case tList0: case tList1: case tList2: case tListN: return "a list";
case tThunk: return "a thunk";
case tApp: return "a function application";
case tLambda: return "a function";
@ -158,21 +151,14 @@ string showType(const Value & v)
return fmt("the built-in function '%s'", string(v.primOp->name));
case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
#if 0
case tExternal: return v.external->showType();
case tFloat: return "a float";
}
abort();
}
#if HAVE_BOEHMGC
/* Called when the Boehm GC runs out of memory. */
static void * oomHandler(size_t requested)
{
/* Convert this to a proper C++ exception. */
throw std::bad_alloc();
}
#endif
case tFloat: return "a float";
default:
abort();
}
}
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
@ -194,46 +180,6 @@ void initGC()
{
if (gcInitialised) return;
#if HAVE_BOEHMGC
/* Initialise the Boehm garbage collector. */
/* Don't look for interior pointers. This reduces the odds of
misdetection a bit. */
GC_set_all_interior_pointers(0);
/* We don't have any roots in data segments, so don't scan from
there. */
GC_set_no_dls(1);
GC_INIT();
GC_set_oom_fn(oomHandler);
/* Set the initial heap size to something fairly big (25% of
physical RAM, up to a maximum of 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 overridden
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")) {
size_t size = 32 * 1024 * 1024;
#if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES)
size_t maxSize = 384 * 1024 * 1024;
long pageSize = sysconf(_SC_PAGESIZE);
long pages = sysconf(_SC_PHYS_PAGES);
if (pageSize != -1)
size = (pageSize * pages) / 4; // 25% of RAM
if (size > maxSize) size = maxSize;
#endif
debug(format("setting initial heap size to %1% bytes") % size);
GC_expand_hp(size);
}
#endif
gcInitialised = true;
}
@ -311,7 +257,9 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
assert(gcInitialised);
#if 0
static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes");
#endif
/* Initialise the Nix expression search path. */
if (!evalSettings.pureEval) {
@ -340,9 +288,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
}
}
clearValue(vEmptySet);
vEmptySet.type = tAttrs;
vEmptySet.attrs = allocBindings(0);
emptyBindings = Bindings::allocBindings(0);
createBaseEnv();
}
@ -441,9 +387,9 @@ Value * EvalState::addConstant(const string & name, Value & v)
Value * v2 = allocValue();
*v2 = v;
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v2;
baseEnv->values[baseEnvDispl++] = v2;
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v2));
baseEnv->values[0]->attrs->push_back(Attr(symbols.create(name2), v2));
return v2;
}
@ -462,15 +408,15 @@ Value * EvalState::addPrimOp(const string & name,
v->type = tPrimOp;
v->primOp = new PrimOp(primOp, arity, sym);
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
baseEnv->values[baseEnvDispl++] = v;
baseEnv->values[0]->attrs->push_back(Attr(sym, v));
return v;
}
Value & EvalState::getBuiltin(const string & name)
{
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
return *baseEnv->values[0]->attrs->find(symbols.create(name))->value;
}
@ -556,8 +502,9 @@ Value & mkString(Value & v, const string & s, const PathSet & context)
mkString(v, s.c_str());
if (!context.empty()) {
size_t n = 0;
// FIXME: store as Symbols?
v.string.context = (const char * *)
allocBytes((context.size() + 1) * sizeof(char *));
malloc((context.size() + 1) * sizeof(char *));
for (auto & i : context)
v.string.context[n++] = dupString(i.c_str());
v.string.context[n] = 0;
@ -598,50 +545,41 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
}
std::atomic<uint64_t> nrValuesFreed{0};
void finalizeValue(void * obj, void * data)
{
nrValuesFreed++;
}
Value * EvalState::allocValue()
Ptr<Value> EvalState::allocValue()
{
nrValues++;
auto v = (Value *) allocBytes(sizeof(Value));
//GC_register_finalizer_no_order(v, finalizeValue, nullptr, nullptr, nullptr);
return v;
return gc.alloc<Value>(Value::words());
}
Env & EvalState::allocEnv(size_t size)
Ptr<Env> EvalState::allocEnv(size_t size)
{
if (size > std::numeric_limits<decltype(Env::size)>::max())
if (size > std::numeric_limits<decltype(Env::size)>::max()) // FIXME
throw Error("environment size %d is too big", size);
nrEnvs++;
nrValuesInEnvs += size;
Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
env->size = (decltype(Env::size)) size;
env->type = Env::Plain;
auto env = gc.alloc<Env>(Env::wordsFor(size), size);
// FIXME
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
return *env;
return env;
}
void EvalState::mkList(Value & v, size_t size)
{
clearValue(v);
if (size == 1)
if (size == 0)
v.type = tList0;
else if (size == 1)
v.type = tList1;
else if (size == 2)
v.type = tList2;
else {
v.type = tListN;
v.bigList.size = size;
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
v.bigList = gc.alloc<PtrList<Value>>(
PtrList<Value>::wordsFor(size), tValueList, size);
}
nrListElems += size;
}
@ -704,25 +642,25 @@ Value * ExprVar::maybeThunk(EvalState & state, Env & env)
Value * ExprString::maybeThunk(EvalState & state, Env & env)
{
nrAvoided++;
return &v;
return &*v;
}
Value * ExprInt::maybeThunk(EvalState & state, Env & env)
{
nrAvoided++;
return &v;
return &*v;
}
Value * ExprFloat::maybeThunk(EvalState & state, Env & env)
{
nrAvoided++;
return &v;
return &*v;
}
Value * ExprPath::maybeThunk(EvalState & state, Env & env)
{
nrAvoided++;
return &v;
return &*v;
}
@ -732,13 +670,13 @@ void EvalState::evalFile(const Path & path_, Value & v)
FileEvalCache::iterator i;
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
v = i->second;
v = *i->second;
return;
}
Path path2 = resolveExprPath(path);
if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) {
v = i->second;
v = *i->second;
return;
}
@ -755,14 +693,18 @@ void EvalState::evalFile(const Path & path_, Value & v)
fileParseCache[path2] = e;
try {
eval(e, v);
auto v2 = allocValue();
eval(e, *v2);
v = *v2;
if (path != path2) fileEvalCache.emplace(path, v2);
fileEvalCache.emplace(path2, std::move(v2));
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
throw;
}
fileEvalCache[path2] = v;
if (path != path2) fileEvalCache[path] = v;
}
@ -815,24 +757,24 @@ void Expr::eval(EvalState & state, Env & env, Value & v)
void ExprInt::eval(EvalState & state, Env & env, Value & v)
{
v = this->v;
v = *this->v;
}
void ExprFloat::eval(EvalState & state, Env & env, Value & v)
{
v = this->v;
v = *this->v;
}
void ExprString::eval(EvalState & state, Env & env, Value & v)
{
v = this->v;
v = *this->v;
}
void ExprPath::eval(EvalState & state, Env & env, Value & v)
{
v = this->v;
v = *this->v;
}
@ -877,7 +819,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
if (hasOverrides) {
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
state.forceAttrs(*vOverrides);
Bindings * newBnds = state.allocBindings(v.attrs->size() + vOverrides->attrs->size());
Bindings * newBnds = Bindings::allocBindings(v.attrs->size() + vOverrides->attrs->size());
for (auto & i : *v.attrs)
newBnds->push_back(i);
for (auto & i : *vOverrides->attrs) {
@ -1065,7 +1007,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
Value * primOp = &fun;
while (primOp->type == tPrimOpApp) {
argsDone++;
primOp = primOp->primOpApp.left;
primOp = primOp->app.left;
}
assert(primOp->type == tPrimOp);
auto arity = primOp->primOp->arity;
@ -1078,8 +1020,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
Value * vArgs[arity];
auto n = arity - 1;
vArgs[n--] = &arg;
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left)
vArgs[n--] = arg->primOpApp.right;
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->app.left)
vArgs[n--] = arg->app.right;
/* And call the primop. */
nrPrimOpCalls++;
@ -1089,8 +1031,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
Value * fun2 = allocValue();
*fun2 = fun;
v.type = tPrimOpApp;
v.primOpApp.left = fun2;
v.primOpApp.right = &arg;
v.app.left = fun2;
v.app.right = &arg;
}
}
@ -1379,7 +1321,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
NixFloat nf = 0;
bool first = !forceString;
ValueType firstType = tString;
Tag firstType = tString;
for (auto & i : *es) {
Value vTmp;
@ -1594,8 +1536,10 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
}
#if 0
if (v.type == tExternal)
return v.external->coerceToString(pos, context, coerceMore, copyToStore);
#endif
if (coerceMore) {
@ -1692,6 +1636,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tNull:
return true;
case tList0:
case tList1:
case tList2:
case tListN:
@ -1727,8 +1672,10 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tPrimOpApp:
return false;
#if 0
case tExternal:
return *v1.external == *v2.external;
#endif
case tFloat:
return v1.fpoint == v2.fpoint;
@ -1751,10 +1698,6 @@ void EvalState::printStats()
uint64_t bValues = nrValues * sizeof(Value);
uint64_t bAttrsets = nrAttrsets * sizeof(Bindings) + nrAttrsInAttrsets * sizeof(Attr);
#if HAVE_BOEHMGC
GC_word heapSize, totalBytes;
GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes);
#endif
if (showStats) {
auto outPath = getEnv("NIX_SHOW_STATS_PATH","-");
std::fstream fs;
@ -1804,13 +1747,6 @@ void EvalState::printStats()
topObj.attr("nrLookups", nrLookups);
topObj.attr("nrPrimOpCalls", nrPrimOpCalls);
topObj.attr("nrFunctionCalls", nrFunctionCalls);
#if HAVE_BOEHMGC
{
auto gc = topObj.object("gc");
gc.attr("heapSize", heapSize);
gc.attr("totalBytes", totalBytes);
}
#endif
if (countCalls) {
{
@ -1893,6 +1829,7 @@ size_t valueSize(Value & v)
sz += doValue(*i.value);
}
break;
case tList0:
case tList1:
case tList2:
case tListN:
@ -1914,14 +1851,16 @@ size_t valueSize(Value & v)
sz += doEnv(*v.lambda.env);
break;
case tPrimOpApp:
sz += doValue(*v.primOpApp.left);
sz += doValue(*v.primOpApp.right);
sz += doValue(*v.app.left);
sz += doValue(*v.app.right);
break;
#if 0
case tExternal:
if (seen.find(v.external) != seen.end()) break;
seen.insert(v.external);
sz += v.external->valueSize(seen);
break;
#endif
default:
;
}
@ -1949,6 +1888,7 @@ size_t valueSize(Value & v)
}
#if 0
string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
{
throw TypeError(format("cannot coerce %1% to a string, at %2%") %
@ -1965,6 +1905,7 @@ bool ExternalValueBase::operator==(const ExternalValueBase & b) const
std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) {
return v.print(str);
}
#endif
EvalSettings evalSettings;

View file

@ -6,6 +6,7 @@
#include "symbol-table.hh"
#include "hash.hh"
#include "config.hh"
#include "gc.hh"
#include <map>
#include <unordered_map>
@ -32,13 +33,26 @@ struct PrimOp
};
struct Env
struct Env : Object
{
Env * up;
// FIXME: use misc field
unsigned short size; // used by valueSize
unsigned short prevWith:14; // nr of levels up to next `with' environment
enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2;
enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2; // FIXME: fold into type?
Value * values[0];
Env(unsigned short size) : Object(tEnv, 0), size(size) {}
Size words() const
{
return wordsFor(size);
}
static Size wordsFor(unsigned short size)
{
return 3 + size; // FIXME
}
};
@ -83,7 +97,7 @@ public:
mode. */
std::optional<PathSet> allowedPaths;
Value vEmptySet;
Ptr<Bindings> emptyBindings;
const ref<Store> store;
@ -91,19 +105,11 @@ private:
SrcToStore srcToStore;
/* A cache from path names to parse trees. */
#if HAVE_BOEHMGC
typedef std::map<Path, Expr *, std::less<Path>, traceable_allocator<std::pair<const Path, Expr *> > > FileParseCache;
#else
typedef std::map<Path, Expr *> FileParseCache;
#endif
FileParseCache fileParseCache;
/* A cache from path names to values. */
#if HAVE_BOEHMGC
typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value> > > FileEvalCache;
#else
typedef std::map<Path, Value> FileEvalCache;
#endif
typedef std::map<Path, Ptr<Value>> FileEvalCache;
FileEvalCache fileEvalCache;
SearchPath searchPath;
@ -213,7 +219,7 @@ public:
/* The base environment, containing the builtin functions and
values. */
Env & baseEnv;
Ptr<Env> baseEnv;
/* The same, but used during parsing to resolve variables. */
StaticEnv staticBaseEnv; // !!! should be private
@ -260,13 +266,13 @@ public:
void autoCallFunction(Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */
Value * allocValue();
Env & allocEnv(size_t size);
Ptr<Value> allocValue();
Ptr<Env> allocEnv(size_t size);
// Note: the resulting Value is only reachable as long as vAttrs
// is reachable.
Value * allocAttr(Value & vAttrs, const Symbol & name);
Bindings * allocBindings(size_t capacity);
void mkList(Value & v, size_t length);
void mkAttrs(Value & v, size_t capacity);
void mkThunk_(Value & v, Expr * expr);

287
src/libexpr/gc.cc Normal file
View file

@ -0,0 +1,287 @@
#include "gc.hh"
#include "value.hh"
#include "attr-set.hh"
#include "eval.hh"
namespace nix {
GC gc;
GC::GC()
{
// FIXME: placement new
frontSentinel = (Ptr<Object> *) malloc(sizeof(Ptr<Object>));
backSentinel = (Ptr<Object> *) malloc(sizeof(Ptr<Object>));
frontSentinel->prev = nullptr;
frontSentinel->next = backSentinel;
backSentinel->prev = frontSentinel;
backSentinel->next = nullptr;
}
GC::~GC()
{
size_t n = 0;
for (Ptr<Object> * p = frontSentinel->next; p != backSentinel; p = p->next)
n++;
if (n)
warn("%d GC roots still exist on exit", n);
assert(!frontSentinel->prev);
assert(!backSentinel->next);
}
void GC::gc()
{
size_t marked = 0;
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.
auto push = [&](Object * p) { if (p) { /* FIXME */ assert(isObject(p)); stack.push(p); } };
//printError("MARK %x", obj);
if (!obj->isMarked()) {
marked++;
obj->mark();
switch (obj->type) {
case tFree:
printError("reached a freed object at %x", obj);
abort();
case tBindings: {
auto obj2 = (Bindings *) obj;
for (auto i = obj2->attrs; i < obj2->attrs + obj2->size_; ++i)
push(i->value);
break;
}
case tValueList: {
auto obj2 = (PtrList<Object> *) obj;
for (auto i = obj2->elems; i < obj2->elems + obj2->size(); ++i)
push(*i);
break;
}
case tEnv: {
auto obj2 = (Env *) obj;
push(obj2->up);
if (obj2->type != Env::HasWithExpr)
for (auto i = obj2->values; i < obj2->values + obj2->size; ++i)
push(*i);
break;
}
case tInt:
case tBool:
case tNull:
case tList0:
case tFloat:
break;
case tString:
// FIXME
break;
case tPath:
// FIXME
break;
case tAttrs:
push(((Value *) obj)->attrs);
break;
case tList1:
push(((Value *) obj)->smallList[0]);
break;
case tList2:
push(((Value *) obj)->smallList[0]);
push(((Value *) obj)->smallList[1]);
break;
case tListN:
push(((Value *) obj)->bigList);
break;
case tThunk:
push(((Value *) obj)->thunk.env);
break;
case tApp:
case tPrimOpApp:
push(((Value *) obj)->app.left);
push(((Value *) obj)->app.right);
break;
case tLambda:
push(((Value *) obj)->lambda.env);
break;
case tBlackhole:
// FIXME
break;
case tPrimOp:
// FIXME: GC primops?
break;
default:
printError("don't know how to traverse object at %x (tag %d)", obj, obj->type);
abort();
}
}
}
}
Size totalObjectsFreed = 0;
Size totalWordsFreed = 0;
for (auto & arenaList : arenaLists) {
for (auto & arena : arenaList.arenas) {
auto [objectsFreed, wordsFreed] = arena.freeUnmarked();
totalObjectsFreed += objectsFreed;
totalWordsFreed += wordsFreed;
}
std::sort(arenaList.arenas.begin(), arenaList.arenas.end(),
[](const Arena & a, const Arena & b) {
return b.free < a.free;
});
}
printError("freed %d bytes in %d dead objects, keeping %d objects",
totalWordsFreed * WORD_SIZE, totalObjectsFreed, marked);
}
std::pair<Size, Size> GC::Arena::freeUnmarked()
{
Size objectsFreed = 0;
Size wordsFreed = 0;
auto end = start + size;
auto pos = start;
Free * curFree = nullptr;
Free * * freeLink = &firstFree;
free = 0;
while (pos < end) {
auto obj = (Object *) pos;
auto tag = obj->type;
auto linkFree = [&]() {
*freeLink = curFree;
freeLink = &curFree->next;
};
Size objSize;
if (tag >= tInt && tag <= tFloat) {
objSize = ((Value *) obj)->words();
} else {
switch (tag) {
case tFree:
objSize = ((Free *) obj)->words();
break;
case tBindings:
objSize = ((Bindings *) obj)->words();
break;
case tValueList:
objSize = ((PtrList<Value> *) obj)->words();
break;
case tEnv:
objSize = ((Env *) obj)->words();
break;
default:
abort();
//throw Error("GC encountered invalid object with tag %d", tag);
}
}
// Merge current object into the previous free object.
auto mergeFree = [&]() {
//printError("MERGE %x %x %d", curFree, obj, curFree->size() + objSize);
assert(curFree->words() >= 1);
if (curFree->words() == 1) {
linkFree();
free += 1;
}
curFree->setSize(curFree->words() + objSize);
free += objSize;
};
if (tag == tFree) {
if (curFree) {
// Merge this object into the previous free
// object.
mergeFree();
} else {
curFree = (Free *) obj;
if (curFree->words() > 1) {
linkFree();
free += curFree->words();
}
}
} else {
if (obj->isMarked()) {
// Unmark to prepare for the next GC run.
curFree = nullptr;
obj->unmark();
} else {
//printError("FREE %x %d %d", obj, obj->type, objSize);
objectsFreed += 1;
wordsFreed += objSize;
if (curFree) {
mergeFree();
} else {
// Convert to a free object.
curFree = (Free *) obj;
curFree->type = tFree;
curFree->setSize(objSize);
linkFree();
free += objSize;
}
}
}
pos += objSize;
}
assert(pos == end);
*freeLink = nullptr;
return {objectsFreed, wordsFreed};
}
bool GC::isObject(void * p)
{
for (auto & arenaList : arenaLists) {
for (auto & arena : arenaList.arenas) {
if (p >= arena.start && p < arena.start + arena.size)
return true;
}
}
return false;
}
GC::ArenaList::ArenaList()
: nextSize(1024)
{
}
}

346
src/libexpr/gc.hh Normal file
View file

@ -0,0 +1,346 @@
#pragma once
#include "logging.hh"
#include <stack>
#include <limits>
#include <cassert>
namespace nix {
typedef unsigned long Word;
typedef size_t Size; // size in words
enum Tag {
tFree = 3,
// Misc types
tBindings,
tValueList,
tEnv,
// Value tags
tInt,
tBool,
tString,
tPath,
tNull,
tAttrs,
tList0,
tList1,
tList2,
tListN,
tThunk,
tApp,
tLambda,
tBlackhole,
tPrimOp,
tPrimOpApp,
tExternal,
tFloat
};
constexpr size_t WORD_SIZE = 8;
struct Object
{
friend class GC;
public:
constexpr static unsigned int miscBits = 58;
private:
unsigned long misc:58;
public: // FIXME
Tag type:5;
private:
bool marked:1;
void unmark()
{
marked = false;
}
protected:
Object(Tag type, unsigned long misc) : misc(misc), type(type), marked(false) { }
bool isMarked()
{
return marked;
}
void mark()
{
marked = true;
}
void setMisc(unsigned int misc)
{
this->misc = misc;
}
unsigned int getMisc() const
{
return misc;
}
};
template<class T>
struct PtrList : Object
{
T * elems[0];
PtrList(Tag type, Size size) : Object(type, size)
{
for (Size i = 0; i < size; i++)
elems[i] = nullptr;
}
Size size() const { return getMisc(); }
Size words() const { return wordsFor(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
{
Free * next;
Free(Size size) : Object(tFree, size), next(nullptr) { }
// return size in words
Size words() const { return getMisc(); }
void setSize(Size size) { assert(size >= 1); setMisc(size); }
};
struct GC
{
private:
Ptr<Object> * frontSentinel;
Ptr<Object> * backSentinel;
struct Arena
{
Size size; // in words
Size free; // words free
Free * firstFree;
Word * start;
Arena(Size size)
: size(size)
, free(size)
, start(new Word[size])
{
assert(size >= 2);
firstFree = new (start) Free(size);
}
Arena(const Arena & arena) = delete;
Arena(Arena && arena)
{
*this = std::move(arena);
}
Arena & operator =(Arena && arena)
{
size = arena.size;
free = arena.free;
firstFree = arena.firstFree;
start = arena.start;
arena.start = nullptr;
return *this;
}
~Arena()
{
delete[] start;
}
Object * alloc(Size size)
{
assert(size >= 2);
Free * * prev = &firstFree;
while (Free * freeObj = *prev) {
//printError("LOOK %x %d %x", freeObj, freeObj->words(), freeObj->next);
assert(freeObj->words() >= 2);
if (freeObj->words() == size) {
*prev = freeObj->next;
assert(free >= size);
free -= size;
return (Object *) freeObj;
} else if (freeObj->words() >= size + 2) {
// Split this free object.
auto newSize = freeObj->words() - size;
freeObj->setSize(newSize);
assert(free >= size);
free -= size;
return (Object *) (((Word *) freeObj) + newSize);
} else if (freeObj->words() == size + 1) {
// Return this free object and add a padding word.
*prev = freeObj->next;
freeObj->setSize(1);
assert(free >= size + 1);
free -= size + 1;
return (Object *) (((Word *) freeObj) + 1);
} else {
assert(freeObj->words() < size);
prev = &freeObj->next;
}
}
return nullptr;
}
std::pair<Size, Size> freeUnmarked();
};
// Note: arenas are sorted by ascending amount of free space.
struct ArenaList
{
Size nextSize;
std::vector<Arena> arenas;
ArenaList();
};
std::array<ArenaList, 2> arenaLists;
public:
GC();
~GC();
template<typename T, typename... Args>
Ptr<T> alloc(Size size, const Args & ... args)
{
ArenaList & arenaList = size == 3 ? arenaLists[0] : arenaLists[1];
for (int i = 0; i < 3; i++) {
for (auto j = arenaList.arenas.rbegin(); j != arenaList.arenas.rend(); ++j) {
auto & arena = *j;
auto raw = arena.alloc(size);
if (raw) {
auto obj = new (raw) T(args...);
//printError("ALLOC %x", obj);
return Ptr<T>((Ptr<T> *) frontSentinel->next, obj);
}
}
if (i == 0) {
printError("allocation of %d bytes failed, GCing...", size * WORD_SIZE);
gc();
} else {
Size arenaSize = std::max(arenaList.nextSize, size);
arenaList.nextSize = arenaSize * 2; // FIXME: overflow
printError("allocating arena of %d bytes", arenaSize * WORD_SIZE);
arenaList.arenas.emplace_back(arenaSize);
}
}
throw Error("allocation of %d bytes failed", size);
}
void gc();
bool isObject(void * p);
};
extern GC gc;
}

View file

@ -248,7 +248,7 @@ void DrvInfo::setMeta(const string & name, Value * v)
{
getMeta();
Bindings * old = meta;
meta = state->allocBindings(1 + (old ? old->size() : 0));
meta = Bindings::allocBindings(1 + (old ? old->size() : 0));
Symbol sym = state->symbols.create(name);
if (old)
for (auto i : *old)

View file

@ -69,11 +69,7 @@ public:
};
#if HAVE_BOEHMGC
typedef list<DrvInfo, traceable_allocator<DrvInfo> > DrvInfos;
#else
typedef list<DrvInfo> DrvInfos;
#endif
typedef std::list<DrvInfo> DrvInfos;
/* If value `v' denotes a derivation, return a DrvInfo object

View file

@ -52,9 +52,9 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
skipWhitespace(s);
while (1) {
if (values.empty() && *s == ']') break;
Value * v2 = state.allocValue();
Ptr<Value> v2 = state.allocValue();
parseJSON(state, s, *v2);
values.push_back(v2);
values.push_back(std::move(v2));
skipWhitespace(s);
if (*s == ']') break;
if (*s != ',') throw JSONParseError("expected ',' or ']' after JSON array element");
@ -76,9 +76,9 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
skipWhitespace(s);
if (*s != ':') throw JSONParseError("expected ':' in JSON object");
s++;
Value * v2 = state.allocValue();
Ptr<Value> v2 = state.allocValue();
parseJSON(state, s, *v2);
attrs[state.symbols.create(name)] = v2;
attrs.emplace(state.symbols.create(name), std::move(v2));
skipWhitespace(s);
if (*s == '}') break;
if (*s != ',') throw JSONParseError("expected ',' or '}' after JSON member");
@ -98,7 +98,7 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
else if (isdigit(*s) || *s == '-' || *s == '.' ) {
// Buffer into a string first, then use built-in C++ conversions
std::string tmp_number;
ValueType number_type = tInt;
Tag number_type = tInt;
while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') {
if (*s == '.' || *s == 'e' || *s == 'E')

View file

@ -13,11 +13,6 @@ ifneq ($(OS), FreeBSD)
libexpr_LDFLAGS += -ldl
endif
# The dependency on libgc must be propagated (i.e. meaning that
# programs/libraries that use libexpr must explicitly pass -lgc),
# because inline functions in libexpr's header files call libgc.
libexpr_LDFLAGS_PROPAGATED = $(BDW_GC_LIBS)
libexpr_ORDER_AFTER := $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh
$(d)/parser-tab.cc $(d)/parser-tab.hh: $(d)/parser.y

View file

@ -92,8 +92,11 @@ std::ostream & operator << (std::ostream & str, const Expr & e);
struct ExprInt : Expr
{
NixInt n;
Value v;
ExprInt(NixInt n) : n(n) { mkInt(v, n); };
Ptr<Value> v;
ExprInt(NixInt n) : n(n) {
v = gc.alloc<Value>(Value::words());
mkInt(v, n);
};
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
@ -101,8 +104,11 @@ struct ExprInt : Expr
struct ExprFloat : Expr
{
NixFloat nf;
Value v;
ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); };
Ptr<Value> v;
ExprFloat(NixFloat nf) : nf(nf) {
v = gc.alloc<Value>(Value::words());
mkFloat(v, nf);
};
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
@ -110,8 +116,11 @@ struct ExprFloat : Expr
struct ExprString : Expr
{
Symbol s;
Value v;
ExprString(const Symbol & s) : s(s) { mkString(v, s); };
Ptr<Value> v;
ExprString(const Symbol & s) : s(s) {
v = gc.alloc<Value>(Value::words());
mkString(v, s);
};
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
@ -126,8 +135,11 @@ struct ExprIndStr : Expr
struct ExprPath : Expr
{
string s;
Value v;
ExprPath(const string & s) : s(s) { mkPathNoCopy(v, this->s.c_str()); };
Ptr<Value> v;
ExprPath(const string & s) : s(s) {
v = gc.alloc<Value>(Value::words());
mkPathNoCopy(v, this->s.c_str());
};
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};

View file

@ -130,8 +130,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
if (args[0]->attrs->empty())
state.evalFile(realPath, v);
else {
Env * env = &state.allocEnv(args[0]->attrs->size());
env->up = &state.baseEnv;
auto env = state.allocEnv(args[0]->attrs->size());
env->up = state.baseEnv;
StaticEnv staticEnv(false, &state.staticBaseEnv);
@ -243,15 +243,17 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
case tPath: t = "path"; break;
case tNull: t = "null"; break;
case tAttrs: t = "set"; break;
case tList1: case tList2: case tListN: t = "list"; break;
case tList0: case tList1: case tList2: case tListN: t = "list"; break;
case tLambda:
case tPrimOp:
case tPrimOpApp:
t = "lambda";
break;
#if 0
case tExternal:
t = args[0]->external->typeOf();
break;
#endif
case tFloat: t = "float"; break;
default: abort();
}
@ -348,15 +350,12 @@ struct CompareValues
};
#if HAVE_BOEHMGC
typedef list<Value *, gc_allocator<Value *> > ValueList;
#else
typedef list<Value *> ValueList;
#endif
typedef list<Ptr<Value>> ValueList;
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
#if 0
state.forceAttrs(*args[0], pos);
/* Get the start set. */
@ -417,6 +416,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
unsigned int n = 0;
for (auto & i : res)
v.listElems()[n++] = i;
#endif
abort();
}
@ -1603,7 +1604,8 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V
auto len = args[1]->listSize();
ValueVector right, wrong;
// Note: these Values are reachable via args[0].
std::vector<Value *> right, wrong;
for (unsigned int n = 0; n < len; ++n) {
auto vElem = args[1]->listElems()[n];
@ -2107,10 +2109,10 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
void EvalState::createBaseEnv()
{
baseEnv.up = 0;
baseEnv->up = 0;
/* Add global constants such as `true' to the base environment. */
Value v;
auto v = allocValue();
/* `builtins' must be first! */
mkAttrs(v, 128);
@ -2287,7 +2289,7 @@ void EvalState::createBaseEnv()
mkList(v, searchPath.size());
int n = 0;
for (auto & i : searchPath) {
v2 = v.listElems()[n++] = allocValue();
v2 = v->listElems()[n++] = allocValue();
mkAttrs(*v2, 2);
mkString(*allocAttr(*v2, symbols.create("path")), i.second);
mkString(*allocAttr(*v2, symbols.create("prefix")), i.first);
@ -2301,7 +2303,7 @@ void EvalState::createBaseEnv()
/* Now that we've added all primops, sort the `builtins' set,
because attribute lookups expect it to be sorted. */
baseEnv.values[0]->attrs->sort();
baseEnv->values[0]->attrs->sort();
}

View file

@ -56,7 +56,7 @@ void printValueAsJSON(EvalState & state, bool strict,
break;
}
case tList1: case tList2: case tListN: {
case tList0: case tList1: case tList2: case tListN: {
auto list(out.list());
for (unsigned int n = 0; n < v.listSize(); ++n) {
auto placeholder(list.placeholder());
@ -65,9 +65,11 @@ void printValueAsJSON(EvalState & state, bool strict,
break;
}
#if 0
case tExternal:
v.external->printValueAsJSON(state, strict, out, context);
break;
#endif
case tFloat:
out.write(v.fpoint);
@ -85,11 +87,13 @@ void printValueAsJSON(EvalState & state, bool strict,
printValueAsJSON(state, strict, v, out, context);
}
#if 0
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
JSONPlaceholder & out, PathSet & context) const
{
throw TypeError(format("cannot convert %1% to JSON") % showType());
}
#endif
}

View file

@ -119,7 +119,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break;
case tList1: case tList2: case tListN: {
case tList0: case tList1: case tList2: case tListN: {
XMLOpenElement _(doc, "list");
for (unsigned int n = 0; n < v.listSize(); ++n)
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
@ -144,9 +144,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break;
}
#if 0
case tExternal:
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
break;
#endif
case tFloat:
doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
@ -158,11 +160,13 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
}
#if 0
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const
{
doc.writeEmptyElement("unevaluated");
}
#endif
void printValueAsXML(EvalState & state, bool strict, bool location,

View file

@ -1,35 +1,10 @@
#pragma once
#include "symbol-table.hh"
#if HAVE_BOEHMGC
#include <gc/gc_allocator.h>
#endif
#include "gc.hh"
namespace nix {
typedef enum {
tInt = 1,
tBool,
tString,
tPath,
tNull,
tAttrs,
tList1,
tList2,
tListN,
tThunk,
tApp,
tLambda,
tBlackhole,
tPrimOp,
tPrimOpApp,
tExternal,
tFloat
} ValueType;
class Bindings;
struct Env;
struct Expr;
@ -46,6 +21,7 @@ class JSONPlaceholder;
typedef int64_t NixInt;
typedef double NixFloat;
#if 0
/* External values must descend from ExternalValueBase, so that
* type-agnostic nix functions (e.g. showType) can be implemented
*/
@ -90,11 +66,11 @@ class ExternalValueBase
};
std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
#endif
struct Value
struct Value : Object
{
ValueType type;
union
{
NixInt integer;
@ -127,10 +103,7 @@ struct Value
const char * path;
Bindings * attrs;
struct {
size_t size;
Value * * elems;
} bigList;
PtrList<Value> * bigList;
Value * smallList[2];
struct {
Env * env;
@ -144,46 +117,38 @@ struct Value
ExprLambda * fun;
} lambda;
PrimOp * primOp;
struct {
Value * left, * right;
} primOpApp;
ExternalValueBase * external;
//ExternalValueBase * external;
NixFloat fpoint;
};
Value() : Object(tBlackhole, 0) { }
bool isList() const
{
return type == tList1 || type == tList2 || type == tListN;
return type >= tList0 && type <= tListN;
}
Value * * listElems()
{
return type == tList1 || type == tList2 ? smallList : bigList.elems;
return type == tList0 || type == tList1 || type == tList2 ? smallList : bigList->elems;
}
const Value * const * listElems() const
{
return type == tList1 || type == tList2 ? smallList : bigList.elems;
return type == tList0 || type == tList1 || type == tList2 ? smallList : bigList->elems;
}
size_t listSize() const
{
return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size;
return type == tList0 ? 0 : type == tList1 ? 1 : type == tList2 ? 2 : bigList->size();
}
constexpr static Size words() { return 3; } // FIXME
};
/* After overwriting an app node, be sure to clear pointers in the
Value to ensure that the target isn't kept alive unnecessarily. */
static inline void clearValue(Value & v)
{
v.app.left = v.app.right = 0;
}
static inline void mkInt(Value & v, NixInt n)
{
clearValue(v);
v.type = tInt;
v.integer = n;
}
@ -191,7 +156,6 @@ static inline void mkInt(Value & v, NixInt n)
static inline void mkFloat(Value & v, NixFloat n)
{
clearValue(v);
v.type = tFloat;
v.fpoint = n;
}
@ -199,7 +163,6 @@ static inline void mkFloat(Value & v, NixFloat n)
static inline void mkBool(Value & v, bool b)
{
clearValue(v);
v.type = tBool;
v.boolean = b;
}
@ -207,7 +170,6 @@ static inline void mkBool(Value & v, bool b)
static inline void mkNull(Value & v)
{
clearValue(v);
v.type = tNull;
}
@ -247,7 +209,6 @@ void mkString(Value & v, const char * s);
static inline void mkPathNoCopy(Value & v, const char * s)
{
clearValue(v);
v.type = tPath;
v.path = s;
}
@ -262,13 +223,8 @@ void mkPath(Value & v, const char * s);
size_t valueSize(Value & v);
#if HAVE_BOEHMGC
typedef std::vector<Value *, gc_allocator<Value *> > ValueVector;
typedef std::map<Symbol, Value *, std::less<Symbol>, gc_allocator<std::pair<const Symbol, Value *> > > ValueMap;
#else
typedef std::vector<Value *> ValueVector;
typedef std::map<Symbol, Value *> ValueMap;
#endif
typedef std::vector<Ptr<Value>> ValueVector; // FIXME: make more efficient
typedef std::map<Symbol, Ptr<Value>> ValueMap; // FIXME: use Bindings?
}

View file

@ -244,9 +244,6 @@ void printVersion(const string & programName)
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
if (verbosity > lvlInfo) {
Strings cfg;
#if HAVE_BOEHMGC
cfg.push_back("gc");
#endif
#if HAVE_SODIUM
cfg.push_back("signed-caches");
#endif

View file

@ -19,7 +19,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
if (pathExists(manifestFile)) {
Value v;
state.evalFile(manifestFile, v);
Bindings & bindings(*state.allocBindings(0));
auto bindings = Bindings::allocBindings(0);
getDerivations(state, v, "", bindings, elems, false);
}
return elems;
@ -42,7 +42,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* Construct the whole top level derivation. */
PathSet references;
Value manifest;
auto manifest = state.allocValue();
state.mkList(manifest, elems.size());
unsigned int n = 0;
for (auto & i : elems) {
@ -51,8 +51,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
as the meta attributes. */
Path drvPath = keepDerivations ? i.queryDrvPath() : "";
Value & v(*state.allocValue());
manifest.listElems()[n++] = &v;
auto v = state.allocValue();
manifest->listElems()[n++] = (Value *) v;
state.mkAttrs(v, 16);
mkString(*state.allocAttr(v, state.sType), "derivation");
@ -70,7 +70,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.mkList(vOutputs, outputs.size());
unsigned int m = 0;
for (auto & j : outputs) {
mkString(*(vOutputs.listElems()[m++] = state.allocValue()), j.first);
mkString(*(vOutputs.listElems()[m++] = (Value *) state.allocValue()), j.first);
Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first));
state.mkAttrs(vOutputs, 2);
mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second);
@ -93,7 +93,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
vMeta.attrs->push_back(Attr(state.symbols.create(j), v));
}
vMeta.attrs->sort();
v.attrs->sort();
v->attrs->sort();
if (drvPath != "") references.insert(drvPath);
}
@ -102,7 +102,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
the store; we need it for future modifications of the
environment. */
Path manifestFile = state.store->addTextToStore("env-manifest.nix",
(format("%1%") % manifest).str(), references);
fmt("%1%", (Value &) manifest), references);
/* Get the environment builder expression. */
Value envBuilder;
@ -114,7 +114,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.mkAttrs(args, 3);
mkString(*state.allocAttr(args, state.symbols.create("manifest")),
manifestFile, {manifestFile});
args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest));
args.attrs->push_back(Attr(state.symbols.create("derivations"), (Value *) manifest));
args.attrs->sort();
mkApp(topLevel, envBuilder, args);

View file

@ -38,8 +38,8 @@ struct CmdEdit : InstallableCommand
Value * v2;
try {
auto dummyArgs = state->allocBindings(0);
v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v);
auto dummyArgs = Bindings::allocBindings(0);
v2 = findAlongAttrPath(*state, "meta.position", dummyArgs, *v);
} catch (Error &) {
throw Error("package '%s' has no source location information", installable->what());
}

View file

@ -44,7 +44,7 @@ struct NixRepl
const static int envSize = 32768;
StaticEnv staticEnv;
Env * env;
Ptr<Env> env;
int displ;
StringSet varNames;
@ -506,8 +506,8 @@ void NixRepl::loadFile(const Path & path)
void NixRepl::initEnv()
{
env = &state.allocEnv(envSize);
env->up = &state.baseEnv;
env = state.allocEnv(envSize);
env->up = state.baseEnv;
displ = 0;
staticEnv.vars.clear();
@ -665,6 +665,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break;
}
case tList0:
case tList1:
case tList2:
case tListN:

View file

@ -129,7 +129,7 @@ struct CmdSearch : SourceExprCommand, MixJSON
if (v->type == tLambda && toplevel) {
Value * v2 = state->allocValue();
state->autoCallFunction(*state->allocBindings(1), *v, *v2);
state->autoCallFunction(*Bindings::allocBindings(1), *v, *v2);
v = v2;
state->forceValue(*v);
}

View file

@ -150,7 +150,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
auto state = std::make_unique<EvalState>(Strings(), store);
auto v = state->allocValue();
state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v);
Bindings & bindings(*state->allocBindings(0));
Bindings & bindings(*Bindings::allocBindings(0));
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v);
return state->forceString(*v2);