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 {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
/* Allocate a new array of attributes for an attribute set with a specific
|
/* Allocate a new array of attributes for an attribute set with a
|
||||||
capacity. The space is implicitly reserved after the Bindings
|
specific capacity. The space is implicitly reserved after the
|
||||||
structure. */
|
Bindings structure. */
|
||||||
Bindings * EvalState::allocBindings(size_t capacity)
|
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);
|
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)
|
void EvalState::mkAttrs(Value & v, size_t capacity)
|
||||||
{
|
{
|
||||||
|
v.type = tAttrs;
|
||||||
if (capacity == 0) {
|
if (capacity == 0) {
|
||||||
v = vEmptySet;
|
v.attrs = emptyBindings;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clearValue(v);
|
v.attrs = Bindings::allocBindings(capacity);
|
||||||
v.type = tAttrs;
|
|
||||||
v.attrs = allocBindings(capacity);
|
|
||||||
nrAttrsets++;
|
nrAttrsets++;
|
||||||
nrAttrsInAttrsets += capacity;
|
nrAttrsInAttrsets += capacity;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
|
#include "gc.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
@ -30,19 +31,22 @@ struct Attr
|
||||||
by its size and its capacity, the capacity being the number of Attr
|
by its size and its capacity, the capacity being the number of Attr
|
||||||
elements allocated after this structure, while the size corresponds to
|
elements allocated after this structure, while the size corresponds to
|
||||||
the number of elements already inserted in this structure. */
|
the number of elements already inserted in this structure. */
|
||||||
class Bindings
|
class Bindings : public Object
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef uint32_t size_t;
|
typedef uint32_t size_t;
|
||||||
|
|
||||||
private:
|
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];
|
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;
|
Bindings(const Bindings & bindings) = delete;
|
||||||
|
|
||||||
public:
|
|
||||||
size_t size() const { return size_; }
|
size_t size() const { return size_; }
|
||||||
|
|
||||||
bool empty() const { return !size_; }
|
bool empty() const { return !size_; }
|
||||||
|
|
@ -51,7 +55,7 @@ public:
|
||||||
|
|
||||||
void push_back(const Attr & attr)
|
void push_back(const Attr & attr)
|
||||||
{
|
{
|
||||||
assert(size_ < capacity_);
|
assert(size_ < capacity());
|
||||||
attrs[size_++] = attr;
|
attrs[size_++] = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +77,7 @@ public:
|
||||||
|
|
||||||
void sort();
|
void sort();
|
||||||
|
|
||||||
size_t capacity() { return capacity_; }
|
size_t capacity() const { return getMisc(); }
|
||||||
|
|
||||||
/* Returns the attributes in lexicographically sorted order. */
|
/* Returns the attributes in lexicographically sorted order. */
|
||||||
std::vector<const Attr *> lexicographicOrder() const
|
std::vector<const Attr *> lexicographicOrder() const
|
||||||
|
|
@ -88,7 +92,19 @@ public:
|
||||||
return res;
|
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 * MixEvalArgs::getAutoArgs(EvalState & state)
|
||||||
{
|
{
|
||||||
Bindings * res = state.allocBindings(autoArgs.size());
|
Bindings * res = Bindings::allocBindings(autoArgs.size());
|
||||||
for (auto & i : autoArgs) {
|
for (auto & i : autoArgs) {
|
||||||
Value * v = state.allocValue();
|
Value * v = state.allocValue();
|
||||||
if (i.second[0] == 'E')
|
if (i.second[0] == 'E')
|
||||||
|
|
|
||||||
|
|
@ -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);
|
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/time.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
|
|
||||||
#include <gc/gc.h>
|
|
||||||
#include <gc/gc_cpp.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME
|
||||||
static char * dupString(const char * s)
|
static char * dupString(const char * s)
|
||||||
{
|
{
|
||||||
char * t;
|
char * t;
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
t = GC_STRDUP(s);
|
|
||||||
#else
|
|
||||||
t = strdup(s);
|
t = strdup(s);
|
||||||
#endif
|
|
||||||
if (!t) throw std::bad_alloc();
|
if (!t) throw std::bad_alloc();
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +75,7 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
|
||||||
str << "}";
|
str << "}";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case tList0:
|
||||||
case tList1:
|
case tList1:
|
||||||
case tList2:
|
case tList2:
|
||||||
case tListN:
|
case tListN:
|
||||||
|
|
@ -108,9 +99,11 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
|
||||||
case tPrimOpApp:
|
case tPrimOpApp:
|
||||||
str << "<PRIMOP-APP>";
|
str << "<PRIMOP-APP>";
|
||||||
break;
|
break;
|
||||||
|
#if 0
|
||||||
case tExternal:
|
case tExternal:
|
||||||
str << *v.external;
|
str << *v.external;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case tFloat:
|
case tFloat:
|
||||||
str << v.fpoint;
|
str << v.fpoint;
|
||||||
break;
|
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;
|
const Value * primOp = &v;
|
||||||
while (primOp->type == tPrimOpApp) {
|
while (primOp->type == tPrimOpApp) {
|
||||||
primOp = primOp->primOpApp.left;
|
primOp = primOp->app.left;
|
||||||
}
|
}
|
||||||
assert(primOp->type == tPrimOp);
|
assert(primOp->type == tPrimOp);
|
||||||
return primOp;
|
return primOp;
|
||||||
|
|
@ -149,7 +142,7 @@ string showType(const Value & v)
|
||||||
case tPath: return "a path";
|
case tPath: return "a path";
|
||||||
case tNull: return "null";
|
case tNull: return "null";
|
||||||
case tAttrs: return "a set";
|
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 tThunk: return "a thunk";
|
||||||
case tApp: return "a function application";
|
case tApp: return "a function application";
|
||||||
case tLambda: return "a function";
|
case tLambda: return "a function";
|
||||||
|
|
@ -158,21 +151,14 @@ string showType(const Value & v)
|
||||||
return fmt("the built-in function '%s'", string(v.primOp->name));
|
return fmt("the built-in function '%s'", string(v.primOp->name));
|
||||||
case tPrimOpApp:
|
case tPrimOpApp:
|
||||||
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
|
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
|
||||||
|
#if 0
|
||||||
case tExternal: return v.external->showType();
|
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
|
#endif
|
||||||
|
case tFloat: return "a float";
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
||||||
|
|
@ -194,46 +180,6 @@ void initGC()
|
||||||
{
|
{
|
||||||
if (gcInitialised) return;
|
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;
|
gcInitialised = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,7 +257,9 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||||
|
|
||||||
assert(gcInitialised);
|
assert(gcInitialised);
|
||||||
|
|
||||||
|
#if 0
|
||||||
static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes");
|
static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes");
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Initialise the Nix expression search path. */
|
/* Initialise the Nix expression search path. */
|
||||||
if (!evalSettings.pureEval) {
|
if (!evalSettings.pureEval) {
|
||||||
|
|
@ -340,9 +288,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearValue(vEmptySet);
|
emptyBindings = Bindings::allocBindings(0);
|
||||||
vEmptySet.type = tAttrs;
|
|
||||||
vEmptySet.attrs = allocBindings(0);
|
|
||||||
|
|
||||||
createBaseEnv();
|
createBaseEnv();
|
||||||
}
|
}
|
||||||
|
|
@ -441,9 +387,9 @@ Value * EvalState::addConstant(const string & name, Value & v)
|
||||||
Value * v2 = allocValue();
|
Value * v2 = allocValue();
|
||||||
*v2 = v;
|
*v2 = v;
|
||||||
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
||||||
baseEnv.values[baseEnvDispl++] = v2;
|
baseEnv->values[baseEnvDispl++] = v2;
|
||||||
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
|
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;
|
return v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -462,15 +408,15 @@ Value * EvalState::addPrimOp(const string & name,
|
||||||
v->type = tPrimOp;
|
v->type = tPrimOp;
|
||||||
v->primOp = new PrimOp(primOp, arity, sym);
|
v->primOp = new PrimOp(primOp, arity, sym);
|
||||||
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
||||||
baseEnv.values[baseEnvDispl++] = v;
|
baseEnv->values[baseEnvDispl++] = v;
|
||||||
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
baseEnv->values[0]->attrs->push_back(Attr(sym, v));
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value & EvalState::getBuiltin(const string & name)
|
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());
|
mkString(v, s.c_str());
|
||||||
if (!context.empty()) {
|
if (!context.empty()) {
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
|
// FIXME: store as Symbols?
|
||||||
v.string.context = (const char * *)
|
v.string.context = (const char * *)
|
||||||
allocBytes((context.size() + 1) * sizeof(char *));
|
malloc((context.size() + 1) * sizeof(char *));
|
||||||
for (auto & i : context)
|
for (auto & i : context)
|
||||||
v.string.context[n++] = dupString(i.c_str());
|
v.string.context[n++] = dupString(i.c_str());
|
||||||
v.string.context[n] = 0;
|
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};
|
Ptr<Value> EvalState::allocValue()
|
||||||
|
|
||||||
void finalizeValue(void * obj, void * data)
|
|
||||||
{
|
|
||||||
nrValuesFreed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value * EvalState::allocValue()
|
|
||||||
{
|
{
|
||||||
nrValues++;
|
nrValues++;
|
||||||
auto v = (Value *) allocBytes(sizeof(Value));
|
return gc.alloc<Value>(Value::words());
|
||||||
//GC_register_finalizer_no_order(v, finalizeValue, nullptr, nullptr, nullptr);
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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);
|
throw Error("environment size %d is too big", size);
|
||||||
|
|
||||||
nrEnvs++;
|
nrEnvs++;
|
||||||
nrValuesInEnvs += size;
|
nrValuesInEnvs += size;
|
||||||
Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
|
auto env = gc.alloc<Env>(Env::wordsFor(size), size);
|
||||||
env->size = (decltype(Env::size)) size;
|
|
||||||
env->type = Env::Plain;
|
|
||||||
|
|
||||||
|
// FIXME
|
||||||
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
/* 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)
|
void EvalState::mkList(Value & v, size_t size)
|
||||||
{
|
{
|
||||||
clearValue(v);
|
if (size == 0)
|
||||||
if (size == 1)
|
v.type = tList0;
|
||||||
|
else if (size == 1)
|
||||||
v.type = tList1;
|
v.type = tList1;
|
||||||
else if (size == 2)
|
else if (size == 2)
|
||||||
v.type = tList2;
|
v.type = tList2;
|
||||||
else {
|
else {
|
||||||
v.type = tListN;
|
v.type = tListN;
|
||||||
v.bigList.size = size;
|
v.bigList = gc.alloc<PtrList<Value>>(
|
||||||
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
|
PtrList<Value>::wordsFor(size), tValueList, size);
|
||||||
}
|
}
|
||||||
nrListElems += size;
|
nrListElems += size;
|
||||||
}
|
}
|
||||||
|
|
@ -704,25 +642,25 @@ Value * ExprVar::maybeThunk(EvalState & state, Env & env)
|
||||||
Value * ExprString::maybeThunk(EvalState & state, Env & env)
|
Value * ExprString::maybeThunk(EvalState & state, Env & env)
|
||||||
{
|
{
|
||||||
nrAvoided++;
|
nrAvoided++;
|
||||||
return &v;
|
return &*v;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * ExprInt::maybeThunk(EvalState & state, Env & env)
|
Value * ExprInt::maybeThunk(EvalState & state, Env & env)
|
||||||
{
|
{
|
||||||
nrAvoided++;
|
nrAvoided++;
|
||||||
return &v;
|
return &*v;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * ExprFloat::maybeThunk(EvalState & state, Env & env)
|
Value * ExprFloat::maybeThunk(EvalState & state, Env & env)
|
||||||
{
|
{
|
||||||
nrAvoided++;
|
nrAvoided++;
|
||||||
return &v;
|
return &*v;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
||||||
{
|
{
|
||||||
nrAvoided++;
|
nrAvoided++;
|
||||||
return &v;
|
return &*v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -732,13 +670,13 @@ void EvalState::evalFile(const Path & path_, Value & v)
|
||||||
|
|
||||||
FileEvalCache::iterator i;
|
FileEvalCache::iterator i;
|
||||||
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
|
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
|
||||||
v = i->second;
|
v = *i->second;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path path2 = resolveExprPath(path);
|
Path path2 = resolveExprPath(path);
|
||||||
if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) {
|
if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) {
|
||||||
v = i->second;
|
v = *i->second;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -755,14 +693,18 @@ void EvalState::evalFile(const Path & path_, Value & v)
|
||||||
fileParseCache[path2] = e;
|
fileParseCache[path2] = e;
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (Error & e) {
|
||||||
addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
|
addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
|
||||||
throw;
|
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)
|
void ExprInt::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v = this->v;
|
v = *this->v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprFloat::eval(EvalState & state, Env & env, Value & 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)
|
void ExprString::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v = this->v;
|
v = *this->v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprPath::eval(EvalState & state, Env & env, Value & 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) {
|
if (hasOverrides) {
|
||||||
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
||||||
state.forceAttrs(*vOverrides);
|
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)
|
for (auto & i : *v.attrs)
|
||||||
newBnds->push_back(i);
|
newBnds->push_back(i);
|
||||||
for (auto & i : *vOverrides->attrs) {
|
for (auto & i : *vOverrides->attrs) {
|
||||||
|
|
@ -1065,7 +1007,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
|
||||||
Value * primOp = &fun;
|
Value * primOp = &fun;
|
||||||
while (primOp->type == tPrimOpApp) {
|
while (primOp->type == tPrimOpApp) {
|
||||||
argsDone++;
|
argsDone++;
|
||||||
primOp = primOp->primOpApp.left;
|
primOp = primOp->app.left;
|
||||||
}
|
}
|
||||||
assert(primOp->type == tPrimOp);
|
assert(primOp->type == tPrimOp);
|
||||||
auto arity = primOp->primOp->arity;
|
auto arity = primOp->primOp->arity;
|
||||||
|
|
@ -1078,8 +1020,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
|
||||||
Value * vArgs[arity];
|
Value * vArgs[arity];
|
||||||
auto n = arity - 1;
|
auto n = arity - 1;
|
||||||
vArgs[n--] = &arg;
|
vArgs[n--] = &arg;
|
||||||
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left)
|
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->app.left)
|
||||||
vArgs[n--] = arg->primOpApp.right;
|
vArgs[n--] = arg->app.right;
|
||||||
|
|
||||||
/* And call the primop. */
|
/* And call the primop. */
|
||||||
nrPrimOpCalls++;
|
nrPrimOpCalls++;
|
||||||
|
|
@ -1089,8 +1031,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
|
||||||
Value * fun2 = allocValue();
|
Value * fun2 = allocValue();
|
||||||
*fun2 = fun;
|
*fun2 = fun;
|
||||||
v.type = tPrimOpApp;
|
v.type = tPrimOpApp;
|
||||||
v.primOpApp.left = fun2;
|
v.app.left = fun2;
|
||||||
v.primOpApp.right = &arg;
|
v.app.right = &arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1379,7 +1321,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
NixFloat nf = 0;
|
NixFloat nf = 0;
|
||||||
|
|
||||||
bool first = !forceString;
|
bool first = !forceString;
|
||||||
ValueType firstType = tString;
|
Tag firstType = tString;
|
||||||
|
|
||||||
for (auto & i : *es) {
|
for (auto & i : *es) {
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
|
|
@ -1594,8 +1536,10 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
if (v.type == tExternal)
|
if (v.type == tExternal)
|
||||||
return v.external->coerceToString(pos, context, coerceMore, copyToStore);
|
return v.external->coerceToString(pos, context, coerceMore, copyToStore);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (coerceMore) {
|
if (coerceMore) {
|
||||||
|
|
||||||
|
|
@ -1692,6 +1636,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
case tNull:
|
case tNull:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case tList0:
|
||||||
case tList1:
|
case tList1:
|
||||||
case tList2:
|
case tList2:
|
||||||
case tListN:
|
case tListN:
|
||||||
|
|
@ -1727,8 +1672,10 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
case tPrimOpApp:
|
case tPrimOpApp:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#if 0
|
||||||
case tExternal:
|
case tExternal:
|
||||||
return *v1.external == *v2.external;
|
return *v1.external == *v2.external;
|
||||||
|
#endif
|
||||||
|
|
||||||
case tFloat:
|
case tFloat:
|
||||||
return v1.fpoint == v2.fpoint;
|
return v1.fpoint == v2.fpoint;
|
||||||
|
|
@ -1751,10 +1698,6 @@ void EvalState::printStats()
|
||||||
uint64_t bValues = nrValues * sizeof(Value);
|
uint64_t bValues = nrValues * sizeof(Value);
|
||||||
uint64_t bAttrsets = nrAttrsets * sizeof(Bindings) + nrAttrsInAttrsets * sizeof(Attr);
|
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) {
|
if (showStats) {
|
||||||
auto outPath = getEnv("NIX_SHOW_STATS_PATH","-");
|
auto outPath = getEnv("NIX_SHOW_STATS_PATH","-");
|
||||||
std::fstream fs;
|
std::fstream fs;
|
||||||
|
|
@ -1804,13 +1747,6 @@ void EvalState::printStats()
|
||||||
topObj.attr("nrLookups", nrLookups);
|
topObj.attr("nrLookups", nrLookups);
|
||||||
topObj.attr("nrPrimOpCalls", nrPrimOpCalls);
|
topObj.attr("nrPrimOpCalls", nrPrimOpCalls);
|
||||||
topObj.attr("nrFunctionCalls", nrFunctionCalls);
|
topObj.attr("nrFunctionCalls", nrFunctionCalls);
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
{
|
|
||||||
auto gc = topObj.object("gc");
|
|
||||||
gc.attr("heapSize", heapSize);
|
|
||||||
gc.attr("totalBytes", totalBytes);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (countCalls) {
|
if (countCalls) {
|
||||||
{
|
{
|
||||||
|
|
@ -1893,6 +1829,7 @@ size_t valueSize(Value & v)
|
||||||
sz += doValue(*i.value);
|
sz += doValue(*i.value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case tList0:
|
||||||
case tList1:
|
case tList1:
|
||||||
case tList2:
|
case tList2:
|
||||||
case tListN:
|
case tListN:
|
||||||
|
|
@ -1914,14 +1851,16 @@ size_t valueSize(Value & v)
|
||||||
sz += doEnv(*v.lambda.env);
|
sz += doEnv(*v.lambda.env);
|
||||||
break;
|
break;
|
||||||
case tPrimOpApp:
|
case tPrimOpApp:
|
||||||
sz += doValue(*v.primOpApp.left);
|
sz += doValue(*v.app.left);
|
||||||
sz += doValue(*v.primOpApp.right);
|
sz += doValue(*v.app.right);
|
||||||
break;
|
break;
|
||||||
|
#if 0
|
||||||
case tExternal:
|
case tExternal:
|
||||||
if (seen.find(v.external) != seen.end()) break;
|
if (seen.find(v.external) != seen.end()) break;
|
||||||
seen.insert(v.external);
|
seen.insert(v.external);
|
||||||
sz += v.external->valueSize(seen);
|
sz += v.external->valueSize(seen);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
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
|
string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
|
||||||
{
|
{
|
||||||
throw TypeError(format("cannot coerce %1% to a string, at %2%") %
|
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) {
|
std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) {
|
||||||
return v.print(str);
|
return v.print(str);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
EvalSettings evalSettings;
|
EvalSettings evalSettings;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
#include "config.hh"
|
#include "config.hh"
|
||||||
|
#include "gc.hh"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
@ -32,13 +33,26 @@ struct PrimOp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Env
|
struct Env : Object
|
||||||
{
|
{
|
||||||
Env * up;
|
Env * up;
|
||||||
|
// FIXME: use misc field
|
||||||
unsigned short size; // used by ‘valueSize’
|
unsigned short size; // used by ‘valueSize’
|
||||||
unsigned short prevWith:14; // nr of levels up to next `with' environment
|
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];
|
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. */
|
mode. */
|
||||||
std::optional<PathSet> allowedPaths;
|
std::optional<PathSet> allowedPaths;
|
||||||
|
|
||||||
Value vEmptySet;
|
Ptr<Bindings> emptyBindings;
|
||||||
|
|
||||||
const ref<Store> store;
|
const ref<Store> store;
|
||||||
|
|
||||||
|
|
@ -91,19 +105,11 @@ private:
|
||||||
SrcToStore srcToStore;
|
SrcToStore srcToStore;
|
||||||
|
|
||||||
/* A cache from path names to parse trees. */
|
/* 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;
|
typedef std::map<Path, Expr *> FileParseCache;
|
||||||
#endif
|
|
||||||
FileParseCache fileParseCache;
|
FileParseCache fileParseCache;
|
||||||
|
|
||||||
/* A cache from path names to values. */
|
/* A cache from path names to values. */
|
||||||
#if HAVE_BOEHMGC
|
typedef std::map<Path, Ptr<Value>> FileEvalCache;
|
||||||
typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value> > > FileEvalCache;
|
|
||||||
#else
|
|
||||||
typedef std::map<Path, Value> FileEvalCache;
|
|
||||||
#endif
|
|
||||||
FileEvalCache fileEvalCache;
|
FileEvalCache fileEvalCache;
|
||||||
|
|
||||||
SearchPath searchPath;
|
SearchPath searchPath;
|
||||||
|
|
@ -213,7 +219,7 @@ public:
|
||||||
|
|
||||||
/* The base environment, containing the builtin functions and
|
/* The base environment, containing the builtin functions and
|
||||||
values. */
|
values. */
|
||||||
Env & baseEnv;
|
Ptr<Env> baseEnv;
|
||||||
|
|
||||||
/* The same, but used during parsing to resolve variables. */
|
/* The same, but used during parsing to resolve variables. */
|
||||||
StaticEnv staticBaseEnv; // !!! should be private
|
StaticEnv staticBaseEnv; // !!! should be private
|
||||||
|
|
@ -260,13 +266,13 @@ public:
|
||||||
void autoCallFunction(Bindings & args, Value & fun, Value & res);
|
void autoCallFunction(Bindings & args, Value & fun, Value & res);
|
||||||
|
|
||||||
/* Allocation primitives. */
|
/* Allocation primitives. */
|
||||||
Value * allocValue();
|
Ptr<Value> allocValue();
|
||||||
Env & allocEnv(size_t size);
|
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);
|
Value * allocAttr(Value & vAttrs, const Symbol & name);
|
||||||
|
|
||||||
Bindings * allocBindings(size_t capacity);
|
|
||||||
|
|
||||||
void mkList(Value & v, size_t length);
|
void mkList(Value & v, size_t length);
|
||||||
void mkAttrs(Value & v, size_t capacity);
|
void mkAttrs(Value & v, size_t capacity);
|
||||||
void mkThunk_(Value & v, Expr * expr);
|
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();
|
getMeta();
|
||||||
Bindings * old = meta;
|
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);
|
Symbol sym = state->symbols.create(name);
|
||||||
if (old)
|
if (old)
|
||||||
for (auto i : *old)
|
for (auto i : *old)
|
||||||
|
|
|
||||||
|
|
@ -69,11 +69,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
typedef std::list<DrvInfo> DrvInfos;
|
||||||
typedef list<DrvInfo, traceable_allocator<DrvInfo> > DrvInfos;
|
|
||||||
#else
|
|
||||||
typedef list<DrvInfo> DrvInfos;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* If value `v' denotes a derivation, return a DrvInfo object
|
/* 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);
|
skipWhitespace(s);
|
||||||
while (1) {
|
while (1) {
|
||||||
if (values.empty() && *s == ']') break;
|
if (values.empty() && *s == ']') break;
|
||||||
Value * v2 = state.allocValue();
|
Ptr<Value> v2 = state.allocValue();
|
||||||
parseJSON(state, s, *v2);
|
parseJSON(state, s, *v2);
|
||||||
values.push_back(v2);
|
values.push_back(std::move(v2));
|
||||||
skipWhitespace(s);
|
skipWhitespace(s);
|
||||||
if (*s == ']') break;
|
if (*s == ']') break;
|
||||||
if (*s != ',') throw JSONParseError("expected ',' or ']' after JSON array element");
|
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);
|
skipWhitespace(s);
|
||||||
if (*s != ':') throw JSONParseError("expected ':' in JSON object");
|
if (*s != ':') throw JSONParseError("expected ':' in JSON object");
|
||||||
s++;
|
s++;
|
||||||
Value * v2 = state.allocValue();
|
Ptr<Value> v2 = state.allocValue();
|
||||||
parseJSON(state, s, *v2);
|
parseJSON(state, s, *v2);
|
||||||
attrs[state.symbols.create(name)] = v2;
|
attrs.emplace(state.symbols.create(name), std::move(v2));
|
||||||
skipWhitespace(s);
|
skipWhitespace(s);
|
||||||
if (*s == '}') break;
|
if (*s == '}') break;
|
||||||
if (*s != ',') throw JSONParseError("expected ',' or '}' after JSON member");
|
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 == '.' ) {
|
else if (isdigit(*s) || *s == '-' || *s == '.' ) {
|
||||||
// Buffer into a string first, then use built-in C++ conversions
|
// Buffer into a string first, then use built-in C++ conversions
|
||||||
std::string tmp_number;
|
std::string tmp_number;
|
||||||
ValueType number_type = tInt;
|
Tag number_type = tInt;
|
||||||
|
|
||||||
while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') {
|
while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') {
|
||||||
if (*s == '.' || *s == 'e' || *s == 'E')
|
if (*s == '.' || *s == 'e' || *s == 'E')
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,6 @@ ifneq ($(OS), FreeBSD)
|
||||||
libexpr_LDFLAGS += -ldl
|
libexpr_LDFLAGS += -ldl
|
||||||
endif
|
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
|
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
|
$(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
|
struct ExprInt : Expr
|
||||||
{
|
{
|
||||||
NixInt n;
|
NixInt n;
|
||||||
Value v;
|
Ptr<Value> v;
|
||||||
ExprInt(NixInt n) : n(n) { mkInt(v, n); };
|
ExprInt(NixInt n) : n(n) {
|
||||||
|
v = gc.alloc<Value>(Value::words());
|
||||||
|
mkInt(v, n);
|
||||||
|
};
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
};
|
};
|
||||||
|
|
@ -101,8 +104,11 @@ struct ExprInt : Expr
|
||||||
struct ExprFloat : Expr
|
struct ExprFloat : Expr
|
||||||
{
|
{
|
||||||
NixFloat nf;
|
NixFloat nf;
|
||||||
Value v;
|
Ptr<Value> v;
|
||||||
ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); };
|
ExprFloat(NixFloat nf) : nf(nf) {
|
||||||
|
v = gc.alloc<Value>(Value::words());
|
||||||
|
mkFloat(v, nf);
|
||||||
|
};
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
};
|
};
|
||||||
|
|
@ -110,8 +116,11 @@ struct ExprFloat : Expr
|
||||||
struct ExprString : Expr
|
struct ExprString : Expr
|
||||||
{
|
{
|
||||||
Symbol s;
|
Symbol s;
|
||||||
Value v;
|
Ptr<Value> v;
|
||||||
ExprString(const Symbol & s) : s(s) { mkString(v, s); };
|
ExprString(const Symbol & s) : s(s) {
|
||||||
|
v = gc.alloc<Value>(Value::words());
|
||||||
|
mkString(v, s);
|
||||||
|
};
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
};
|
};
|
||||||
|
|
@ -126,8 +135,11 @@ struct ExprIndStr : Expr
|
||||||
struct ExprPath : Expr
|
struct ExprPath : Expr
|
||||||
{
|
{
|
||||||
string s;
|
string s;
|
||||||
Value v;
|
Ptr<Value> v;
|
||||||
ExprPath(const string & s) : s(s) { mkPathNoCopy(v, this->s.c_str()); };
|
ExprPath(const string & s) : s(s) {
|
||||||
|
v = gc.alloc<Value>(Value::words());
|
||||||
|
mkPathNoCopy(v, this->s.c_str());
|
||||||
|
};
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
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())
|
if (args[0]->attrs->empty())
|
||||||
state.evalFile(realPath, v);
|
state.evalFile(realPath, v);
|
||||||
else {
|
else {
|
||||||
Env * env = &state.allocEnv(args[0]->attrs->size());
|
auto env = state.allocEnv(args[0]->attrs->size());
|
||||||
env->up = &state.baseEnv;
|
env->up = state.baseEnv;
|
||||||
|
|
||||||
StaticEnv staticEnv(false, &state.staticBaseEnv);
|
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 tPath: t = "path"; break;
|
||||||
case tNull: t = "null"; break;
|
case tNull: t = "null"; break;
|
||||||
case tAttrs: t = "set"; 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 tLambda:
|
||||||
case tPrimOp:
|
case tPrimOp:
|
||||||
case tPrimOpApp:
|
case tPrimOpApp:
|
||||||
t = "lambda";
|
t = "lambda";
|
||||||
break;
|
break;
|
||||||
|
#if 0
|
||||||
case tExternal:
|
case tExternal:
|
||||||
t = args[0]->external->typeOf();
|
t = args[0]->external->typeOf();
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case tFloat: t = "float"; break;
|
case tFloat: t = "float"; break;
|
||||||
default: abort();
|
default: abort();
|
||||||
}
|
}
|
||||||
|
|
@ -348,15 +350,12 @@ struct CompareValues
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
typedef list<Ptr<Value>> ValueList;
|
||||||
typedef list<Value *, gc_allocator<Value *> > ValueList;
|
|
||||||
#else
|
|
||||||
typedef list<Value *> ValueList;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
/* Get the start set. */
|
/* Get the start set. */
|
||||||
|
|
@ -417,6 +416,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
unsigned int n = 0;
|
unsigned int n = 0;
|
||||||
for (auto & i : res)
|
for (auto & i : res)
|
||||||
v.listElems()[n++] = i;
|
v.listElems()[n++] = i;
|
||||||
|
#endif
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1603,7 +1604,8 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
|
|
||||||
auto len = args[1]->listSize();
|
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) {
|
for (unsigned int n = 0; n < len; ++n) {
|
||||||
auto vElem = args[1]->listElems()[n];
|
auto vElem = args[1]->listElems()[n];
|
||||||
|
|
@ -2107,10 +2109,10 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
|
||||||
|
|
||||||
void EvalState::createBaseEnv()
|
void EvalState::createBaseEnv()
|
||||||
{
|
{
|
||||||
baseEnv.up = 0;
|
baseEnv->up = 0;
|
||||||
|
|
||||||
/* Add global constants such as `true' to the base environment. */
|
/* Add global constants such as `true' to the base environment. */
|
||||||
Value v;
|
auto v = allocValue();
|
||||||
|
|
||||||
/* `builtins' must be first! */
|
/* `builtins' must be first! */
|
||||||
mkAttrs(v, 128);
|
mkAttrs(v, 128);
|
||||||
|
|
@ -2287,7 +2289,7 @@ void EvalState::createBaseEnv()
|
||||||
mkList(v, searchPath.size());
|
mkList(v, searchPath.size());
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (auto & i : searchPath) {
|
for (auto & i : searchPath) {
|
||||||
v2 = v.listElems()[n++] = allocValue();
|
v2 = v->listElems()[n++] = allocValue();
|
||||||
mkAttrs(*v2, 2);
|
mkAttrs(*v2, 2);
|
||||||
mkString(*allocAttr(*v2, symbols.create("path")), i.second);
|
mkString(*allocAttr(*v2, symbols.create("path")), i.second);
|
||||||
mkString(*allocAttr(*v2, symbols.create("prefix")), i.first);
|
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,
|
/* Now that we've added all primops, sort the `builtins' set,
|
||||||
because attribute lookups expect it to be sorted. */
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case tList1: case tList2: case tListN: {
|
case tList0: case tList1: case tList2: case tListN: {
|
||||||
auto list(out.list());
|
auto list(out.list());
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||||
auto placeholder(list.placeholder());
|
auto placeholder(list.placeholder());
|
||||||
|
|
@ -65,9 +65,11 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
case tExternal:
|
case tExternal:
|
||||||
v.external->printValueAsJSON(state, strict, out, context);
|
v.external->printValueAsJSON(state, strict, out, context);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case tFloat:
|
case tFloat:
|
||||||
out.write(v.fpoint);
|
out.write(v.fpoint);
|
||||||
|
|
@ -85,11 +87,13 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
printValueAsJSON(state, strict, v, out, context);
|
printValueAsJSON(state, strict, v, out, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
||||||
JSONPlaceholder & out, PathSet & context) const
|
JSONPlaceholder & out, PathSet & context) const
|
||||||
{
|
{
|
||||||
throw TypeError(format("cannot convert %1% to JSON") % showType());
|
throw TypeError(format("cannot convert %1% to JSON") % showType());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case tList1: case tList2: case tListN: {
|
case tList0: case tList1: case tList2: case tListN: {
|
||||||
XMLOpenElement _(doc, "list");
|
XMLOpenElement _(doc, "list");
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||||
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
|
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
|
||||||
|
|
@ -144,9 +144,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
case tExternal:
|
case tExternal:
|
||||||
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
|
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case tFloat:
|
case tFloat:
|
||||||
doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
|
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,
|
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
|
||||||
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const
|
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const
|
||||||
{
|
{
|
||||||
doc.writeEmptyElement("unevaluated");
|
doc.writeEmptyElement("unevaluated");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
|
#include "gc.hh"
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
#include <gc/gc_allocator.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace nix {
|
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;
|
class Bindings;
|
||||||
struct Env;
|
struct Env;
|
||||||
struct Expr;
|
struct Expr;
|
||||||
|
|
@ -46,6 +21,7 @@ class JSONPlaceholder;
|
||||||
typedef int64_t NixInt;
|
typedef int64_t NixInt;
|
||||||
typedef double NixFloat;
|
typedef double NixFloat;
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* External values must descend from ExternalValueBase, so that
|
/* External values must descend from ExternalValueBase, so that
|
||||||
* type-agnostic nix functions (e.g. showType) can be implemented
|
* 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);
|
std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
struct Value
|
struct Value : Object
|
||||||
{
|
{
|
||||||
ValueType type;
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
NixInt integer;
|
NixInt integer;
|
||||||
|
|
@ -127,10 +103,7 @@ struct Value
|
||||||
|
|
||||||
const char * path;
|
const char * path;
|
||||||
Bindings * attrs;
|
Bindings * attrs;
|
||||||
struct {
|
PtrList<Value> * bigList;
|
||||||
size_t size;
|
|
||||||
Value * * elems;
|
|
||||||
} bigList;
|
|
||||||
Value * smallList[2];
|
Value * smallList[2];
|
||||||
struct {
|
struct {
|
||||||
Env * env;
|
Env * env;
|
||||||
|
|
@ -144,46 +117,38 @@ struct Value
|
||||||
ExprLambda * fun;
|
ExprLambda * fun;
|
||||||
} lambda;
|
} lambda;
|
||||||
PrimOp * primOp;
|
PrimOp * primOp;
|
||||||
struct {
|
//ExternalValueBase * external;
|
||||||
Value * left, * right;
|
|
||||||
} primOpApp;
|
|
||||||
ExternalValueBase * external;
|
|
||||||
NixFloat fpoint;
|
NixFloat fpoint;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Value() : Object(tBlackhole, 0) { }
|
||||||
|
|
||||||
bool isList() const
|
bool isList() const
|
||||||
{
|
{
|
||||||
return type == tList1 || type == tList2 || type == tListN;
|
return type >= tList0 && type <= tListN;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * * listElems()
|
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
|
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
|
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)
|
static inline void mkInt(Value & v, NixInt n)
|
||||||
{
|
{
|
||||||
clearValue(v);
|
|
||||||
v.type = tInt;
|
v.type = tInt;
|
||||||
v.integer = n;
|
v.integer = n;
|
||||||
}
|
}
|
||||||
|
|
@ -191,7 +156,6 @@ static inline void mkInt(Value & v, NixInt n)
|
||||||
|
|
||||||
static inline void mkFloat(Value & v, NixFloat n)
|
static inline void mkFloat(Value & v, NixFloat n)
|
||||||
{
|
{
|
||||||
clearValue(v);
|
|
||||||
v.type = tFloat;
|
v.type = tFloat;
|
||||||
v.fpoint = n;
|
v.fpoint = n;
|
||||||
}
|
}
|
||||||
|
|
@ -199,7 +163,6 @@ static inline void mkFloat(Value & v, NixFloat n)
|
||||||
|
|
||||||
static inline void mkBool(Value & v, bool b)
|
static inline void mkBool(Value & v, bool b)
|
||||||
{
|
{
|
||||||
clearValue(v);
|
|
||||||
v.type = tBool;
|
v.type = tBool;
|
||||||
v.boolean = b;
|
v.boolean = b;
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +170,6 @@ static inline void mkBool(Value & v, bool b)
|
||||||
|
|
||||||
static inline void mkNull(Value & v)
|
static inline void mkNull(Value & v)
|
||||||
{
|
{
|
||||||
clearValue(v);
|
|
||||||
v.type = tNull;
|
v.type = tNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,7 +209,6 @@ void mkString(Value & v, const char * s);
|
||||||
|
|
||||||
static inline void mkPathNoCopy(Value & v, const char * s)
|
static inline void mkPathNoCopy(Value & v, const char * s)
|
||||||
{
|
{
|
||||||
clearValue(v);
|
|
||||||
v.type = tPath;
|
v.type = tPath;
|
||||||
v.path = s;
|
v.path = s;
|
||||||
}
|
}
|
||||||
|
|
@ -262,13 +223,8 @@ void mkPath(Value & v, const char * s);
|
||||||
size_t valueSize(Value & v);
|
size_t valueSize(Value & v);
|
||||||
|
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
typedef std::vector<Ptr<Value>> ValueVector; // FIXME: make more efficient
|
||||||
typedef std::vector<Value *, gc_allocator<Value *> > ValueVector;
|
typedef std::map<Symbol, Ptr<Value>> ValueMap; // FIXME: use Bindings?
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -244,9 +244,6 @@ void printVersion(const string & programName)
|
||||||
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
|
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
|
||||||
if (verbosity > lvlInfo) {
|
if (verbosity > lvlInfo) {
|
||||||
Strings cfg;
|
Strings cfg;
|
||||||
#if HAVE_BOEHMGC
|
|
||||||
cfg.push_back("gc");
|
|
||||||
#endif
|
|
||||||
#if HAVE_SODIUM
|
#if HAVE_SODIUM
|
||||||
cfg.push_back("signed-caches");
|
cfg.push_back("signed-caches");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
|
||||||
if (pathExists(manifestFile)) {
|
if (pathExists(manifestFile)) {
|
||||||
Value v;
|
Value v;
|
||||||
state.evalFile(manifestFile, v);
|
state.evalFile(manifestFile, v);
|
||||||
Bindings & bindings(*state.allocBindings(0));
|
auto bindings = Bindings::allocBindings(0);
|
||||||
getDerivations(state, v, "", bindings, elems, false);
|
getDerivations(state, v, "", bindings, elems, false);
|
||||||
}
|
}
|
||||||
return elems;
|
return elems;
|
||||||
|
|
@ -42,7 +42,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
|
|
||||||
/* Construct the whole top level derivation. */
|
/* Construct the whole top level derivation. */
|
||||||
PathSet references;
|
PathSet references;
|
||||||
Value manifest;
|
auto manifest = state.allocValue();
|
||||||
state.mkList(manifest, elems.size());
|
state.mkList(manifest, elems.size());
|
||||||
unsigned int n = 0;
|
unsigned int n = 0;
|
||||||
for (auto & i : elems) {
|
for (auto & i : elems) {
|
||||||
|
|
@ -51,8 +51,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
as the meta attributes. */
|
as the meta attributes. */
|
||||||
Path drvPath = keepDerivations ? i.queryDrvPath() : "";
|
Path drvPath = keepDerivations ? i.queryDrvPath() : "";
|
||||||
|
|
||||||
Value & v(*state.allocValue());
|
auto v = state.allocValue();
|
||||||
manifest.listElems()[n++] = &v;
|
manifest->listElems()[n++] = (Value *) v;
|
||||||
state.mkAttrs(v, 16);
|
state.mkAttrs(v, 16);
|
||||||
|
|
||||||
mkString(*state.allocAttr(v, state.sType), "derivation");
|
mkString(*state.allocAttr(v, state.sType), "derivation");
|
||||||
|
|
@ -70,7 +70,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
state.mkList(vOutputs, outputs.size());
|
state.mkList(vOutputs, outputs.size());
|
||||||
unsigned int m = 0;
|
unsigned int m = 0;
|
||||||
for (auto & j : outputs) {
|
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));
|
Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first));
|
||||||
state.mkAttrs(vOutputs, 2);
|
state.mkAttrs(vOutputs, 2);
|
||||||
mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second);
|
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->push_back(Attr(state.symbols.create(j), v));
|
||||||
}
|
}
|
||||||
vMeta.attrs->sort();
|
vMeta.attrs->sort();
|
||||||
v.attrs->sort();
|
v->attrs->sort();
|
||||||
|
|
||||||
if (drvPath != "") references.insert(drvPath);
|
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
|
the store; we need it for future modifications of the
|
||||||
environment. */
|
environment. */
|
||||||
Path manifestFile = state.store->addTextToStore("env-manifest.nix",
|
Path manifestFile = state.store->addTextToStore("env-manifest.nix",
|
||||||
(format("%1%") % manifest).str(), references);
|
fmt("%1%", (Value &) manifest), references);
|
||||||
|
|
||||||
/* Get the environment builder expression. */
|
/* Get the environment builder expression. */
|
||||||
Value envBuilder;
|
Value envBuilder;
|
||||||
|
|
@ -114,7 +114,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
state.mkAttrs(args, 3);
|
state.mkAttrs(args, 3);
|
||||||
mkString(*state.allocAttr(args, state.symbols.create("manifest")),
|
mkString(*state.allocAttr(args, state.symbols.create("manifest")),
|
||||||
manifestFile, {manifestFile});
|
manifestFile, {manifestFile});
|
||||||
args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest));
|
args.attrs->push_back(Attr(state.symbols.create("derivations"), (Value *) manifest));
|
||||||
args.attrs->sort();
|
args.attrs->sort();
|
||||||
mkApp(topLevel, envBuilder, args);
|
mkApp(topLevel, envBuilder, args);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ struct CmdEdit : InstallableCommand
|
||||||
|
|
||||||
Value * v2;
|
Value * v2;
|
||||||
try {
|
try {
|
||||||
auto dummyArgs = state->allocBindings(0);
|
auto dummyArgs = Bindings::allocBindings(0);
|
||||||
v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v);
|
v2 = findAlongAttrPath(*state, "meta.position", dummyArgs, *v);
|
||||||
} catch (Error &) {
|
} catch (Error &) {
|
||||||
throw Error("package '%s' has no source location information", installable->what());
|
throw Error("package '%s' has no source location information", installable->what());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ struct NixRepl
|
||||||
|
|
||||||
const static int envSize = 32768;
|
const static int envSize = 32768;
|
||||||
StaticEnv staticEnv;
|
StaticEnv staticEnv;
|
||||||
Env * env;
|
Ptr<Env> env;
|
||||||
int displ;
|
int displ;
|
||||||
StringSet varNames;
|
StringSet varNames;
|
||||||
|
|
||||||
|
|
@ -506,8 +506,8 @@ void NixRepl::loadFile(const Path & path)
|
||||||
|
|
||||||
void NixRepl::initEnv()
|
void NixRepl::initEnv()
|
||||||
{
|
{
|
||||||
env = &state.allocEnv(envSize);
|
env = state.allocEnv(envSize);
|
||||||
env->up = &state.baseEnv;
|
env->up = state.baseEnv;
|
||||||
displ = 0;
|
displ = 0;
|
||||||
staticEnv.vars.clear();
|
staticEnv.vars.clear();
|
||||||
|
|
||||||
|
|
@ -665,6 +665,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case tList0:
|
||||||
case tList1:
|
case tList1:
|
||||||
case tList2:
|
case tList2:
|
||||||
case tListN:
|
case tListN:
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ struct CmdSearch : SourceExprCommand, MixJSON
|
||||||
|
|
||||||
if (v->type == tLambda && toplevel) {
|
if (v->type == tLambda && toplevel) {
|
||||||
Value * v2 = state->allocValue();
|
Value * v2 = state->allocValue();
|
||||||
state->autoCallFunction(*state->allocBindings(1), *v, *v2);
|
state->autoCallFunction(*Bindings::allocBindings(1), *v, *v2);
|
||||||
v = v2;
|
v = v2;
|
||||||
state->forceValue(*v);
|
state->forceValue(*v);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
|
||||||
auto state = std::make_unique<EvalState>(Strings(), store);
|
auto state = std::make_unique<EvalState>(Strings(), store);
|
||||||
auto v = state->allocValue();
|
auto v = state->allocValue();
|
||||||
state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v);
|
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);
|
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v);
|
||||||
|
|
||||||
return state->forceString(*v2);
|
return state->forceString(*v2);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue