mirror of
https://github.com/NixOS/nix.git
synced 2025-11-15 06:52:43 +01:00
Checkpoint
This commit is contained in:
parent
bb6e6923f2
commit
4237414f4d
23 changed files with 844 additions and 295 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -133,7 +126,7 @@ std::ostream & operator << (std::ostream & str, 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();
|
||||
#endif
|
||||
case tFloat: return "a float";
|
||||
}
|
||||
default:
|
||||
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
|
||||
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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
287
src/libexpr/gc.cc
Normal 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
346
src/libexpr/gc.hh
Normal 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;
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue