diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 152cc6494..e39b2c513 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -523,8 +523,6 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) { for (size_t l = var.level; l; --l, env = env->up) ; - assert(((Object *) env)->type == tEnv); - if (!var.fromWith) { auto v = env->values[var.displ]; if (v) gc.assertObject(v); @@ -532,12 +530,12 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) } while (1) { - if (env->type == Env::HasWithExpr) { + if (env->type == tWithExprEnv) { if (noEval) return 0; auto v = allocValue(); evalAttrs(*env->up, (Expr *) env->values[0], *v); env->values[0] = v; - env->type = Env::HasWithAttrs; + env->type = tWithAttrsEnv; } Bindings::iterator j = env->values[0]->attrs->find(var.name); if (j != env->values[0]->attrs->end()) { @@ -545,9 +543,9 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) gc.assertObject(j->value); return j->value; } - if (!env->prevWith) + if (!env->getPrevWith()) throwUndefinedVarError("undefined variable '%1%' at %2%", var.name, var.pos); - for (size_t l = env->prevWith; l; --l, env = env->up) ; + for (size_t l = env->getPrevWith(); l; --l, env = env->up) ; } } @@ -559,15 +557,11 @@ Ptr EvalState::allocValue() } -Ptr EvalState::allocEnv(size_t size) +Ptr EvalState::allocEnv(size_t size, size_t prevWith, Tag type) { - if (size > std::numeric_limits::max()) // FIXME - throw Error("environment size %d is too big", size); - nrEnvs++; nrValuesInEnvs += size; - - return gc.alloc(Env::wordsFor(size), size); + return gc.alloc(Env::wordsFor(size), type, size, prevWith); } @@ -1181,12 +1175,9 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) void ExprWith::eval(EvalState & state, Env & env, Value & v) { - auto env2 = state.allocEnv(1); + auto env2 = state.allocEnv(1, prevWith, tWithExprEnv); env2->up = &env; - env2->prevWith = prevWith; - env2->type = Env::HasWithExpr; env2->values[0] = (Value *) attrs; - body->eval(state, env2, v); } @@ -1880,10 +1871,10 @@ size_t valueSize(Value & v) if (seen.find(&env) != seen.end()) return 0; seen.insert(&env); - size_t sz = sizeof(Env) + sizeof(Value *) * env.size; + size_t sz = sizeof(Env) + sizeof(Value *) * env.getSize(); - if (env.type != Env::HasWithExpr) - for (size_t i = 0; i < env.size; ++i) + if (env.type != tWithExprEnv) + for (size_t i = 0; i < env.getSize(); ++i) if (env.values[i]) sz += doValue(*env.values[i]); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b100f7661..ee37a6a68 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -36,16 +36,23 @@ struct PrimOp 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; // FIXME: fold into type? Value * values[0]; private: - Env(unsigned short size) : Object(tEnv, 0), size(size) { - for (auto i = 0; i < size; i++) + constexpr static size_t maxSize = 1 << 16; + constexpr static size_t maxPrevWith = 1 << 10; + + Env(Tag type, size_t size, size_t prevWith) + : Object(type, size | (prevWith << 16)) + { + if (size >= maxSize) + throw Error("environment size %d is too big", size); + + if (prevWith >= maxPrevWith) + throw Error("too many nesting levels"); + + for (size_t i = 0; i < size; i++) values[i] = nullptr; } @@ -53,14 +60,24 @@ private: public: + unsigned short getPrevWith() const + { + return getMisc() >> 16; + } + + unsigned short getSize() const + { + return getMisc() & 0xffff; + } + Size words() const { - return wordsFor(size); + return wordsFor(getSize()); } static Size wordsFor(unsigned short size) { - return 3 + size; // FIXME + return 2 + size; } }; @@ -276,7 +293,7 @@ public: /* Allocation primitives. */ Ptr allocValue(); - Ptr allocEnv(size_t size); + Ptr allocEnv(size_t size, size_t prevWith = 0, Tag type = tEnv); // Note: the resulting Value is only reachable as long as vAttrs // is reachable. diff --git a/src/libexpr/gc.cc b/src/libexpr/gc.cc index d24f8a98a..3f3999133 100644 --- a/src/libexpr/gc.cc +++ b/src/libexpr/gc.cc @@ -82,9 +82,21 @@ void GC::gc() 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); + for (auto i = obj2->values; i < obj2->values + obj2->getSize(); ++i) + push(*i); + break; + } + + case tWithExprEnv: { + auto obj2 = (Env *) obj; + push(obj2->up); + break; + } + + case tWithAttrsEnv: { + auto obj2 = (Env *) obj; + push(obj2->up); + push(obj2->values[0]); break; } @@ -229,6 +241,8 @@ std::pair GC::Arena::freeUnmarked() objSize = ((PtrList *) obj)->words(); break; case tEnv: + case tWithExprEnv: + case tWithAttrsEnv: objSize = ((Env *) obj)->words(); break; default: diff --git a/src/libexpr/gc.hh b/src/libexpr/gc.hh index 411883e87..99fee8bf8 100644 --- a/src/libexpr/gc.hh +++ b/src/libexpr/gc.hh @@ -18,6 +18,8 @@ enum Tag { tBindings, tValueList, tEnv, + tWithExprEnv, + tWithAttrsEnv, // Value tags tInt,