mirror of
https://github.com/NixOS/nix
synced 2025-06-24 22:11:15 +02:00
Add a ListBuilder helper for constructing list values
Previously, `state.mkList()` would set the type of the value to tList and allocate the list vector, but it would not initialize the values in the list. This has two problems: * If an exception occurs, the list is left in an undefined state. * More importantly, for multithreaded evaluation, if a value transitions from thunk to non-thunk, it should be final (i.e. other threads should be able to access the value safely). To address this, there now is a `ListBuilder` class (analogous to `BindingsBuilder`) to build the list vector prior to the call to `Value::mkList()`. Typical usage: auto list = state.buildList(size); for (auto & v : list) v = ... set value ...; vRes.mkList(list);
This commit is contained in:
parent
bff5c94184
commit
fecff520d7
10 changed files with 228 additions and 157 deletions
|
@ -187,13 +187,13 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
|
|||
NixStringContextElem::DrvDeep { .drvPath = *storePath },
|
||||
});
|
||||
attrs.alloc(state.sName).mkString(drv.env["name"]);
|
||||
auto & outputsVal = attrs.alloc(state.sOutputs);
|
||||
state.mkList(outputsVal, drv.outputs.size());
|
||||
|
||||
auto list = state.buildList(drv.outputs.size());
|
||||
for (const auto & [i, o] : enumerate(drv.outputs)) {
|
||||
mkOutputString(state, attrs, *storePath, o);
|
||||
(outputsVal.listElems()[i] = state.allocValue())->mkString(o.first);
|
||||
(list[i] = state.allocValue())->mkString(o.first);
|
||||
}
|
||||
attrs.alloc(state.sOutputs).mkList(list);
|
||||
|
||||
auto w = state.allocValue();
|
||||
w->mkAttrs(attrs);
|
||||
|
@ -694,10 +694,10 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
|
|||
}
|
||||
|
||||
/* Create the result list. */
|
||||
state.mkList(v, res.size());
|
||||
unsigned int n = 0;
|
||||
for (auto & i : res)
|
||||
v.listElems()[n++] = i;
|
||||
auto list = state.buildList(res.size());
|
||||
for (const auto & [n, i] : enumerate(res))
|
||||
list[n] = i;
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_genericClosure(PrimOp {
|
||||
|
@ -2423,14 +2423,15 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args,
|
|||
{
|
||||
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames");
|
||||
|
||||
state.mkList(v, args[0]->attrs->size());
|
||||
auto list = state.buildList(args[0]->attrs->size());
|
||||
|
||||
size_t n = 0;
|
||||
for (auto & i : *args[0]->attrs)
|
||||
(v.listElems()[n++] = state.allocValue())->mkString(state.symbols[i.name]);
|
||||
for (const auto & [n, i] : enumerate(*args[0]->attrs))
|
||||
(list[n] = state.allocValue())->mkString(state.symbols[i.name]);
|
||||
|
||||
std::sort(v.listElems(), v.listElems() + n,
|
||||
std::sort(list.begin(), list.end(),
|
||||
[](Value * v1, Value * v2) { return strcmp(v1->c_str(), v2->c_str()) < 0; });
|
||||
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_attrNames({
|
||||
|
@ -2450,21 +2451,22 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args,
|
|||
{
|
||||
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues");
|
||||
|
||||
state.mkList(v, args[0]->attrs->size());
|
||||
auto list = state.buildList(args[0]->attrs->size());
|
||||
|
||||
unsigned int n = 0;
|
||||
for (auto & i : *args[0]->attrs)
|
||||
v.listElems()[n++] = (Value *) &i;
|
||||
for (const auto & [n, i] : enumerate(*args[0]->attrs))
|
||||
list[n] = (Value *) &i;
|
||||
|
||||
std::sort(v.listElems(), v.listElems() + n,
|
||||
std::sort(list.begin(), list.end(),
|
||||
[&](Value * v1, Value * v2) {
|
||||
std::string_view s1 = state.symbols[((Attr *) v1)->name],
|
||||
s2 = state.symbols[((Attr *) v2)->name];
|
||||
return s1 < s2;
|
||||
});
|
||||
|
||||
for (unsigned int i = 0; i < n; ++i)
|
||||
v.listElems()[i] = ((Attr *) v.listElems()[i])->value;
|
||||
for (auto & v : list)
|
||||
v = ((Attr *) v)->value;
|
||||
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_attrValues({
|
||||
|
@ -2805,9 +2807,10 @@ static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
res[found++] = i->value;
|
||||
}
|
||||
|
||||
state.mkList(v, found);
|
||||
auto list = state.buildList(found);
|
||||
for (unsigned int n = 0; n < found; ++n)
|
||||
v.listElems()[n] = res[n];
|
||||
list[n] = res[n];
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_catAttrs({
|
||||
|
@ -2908,43 +2911,50 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg
|
|||
// attribute with the merge function application. this way we need not
|
||||
// use (slightly slower) temporary storage the GC does not know about.
|
||||
|
||||
std::map<Symbol, std::pair<size_t, Value * *>> attrsSeen;
|
||||
struct Item
|
||||
{
|
||||
size_t size = 0;
|
||||
size_t pos = 0;
|
||||
std::optional<ListBuilder> list;
|
||||
};
|
||||
|
||||
std::map<Symbol, Item> attrsSeen;
|
||||
|
||||
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.zipAttrsWith");
|
||||
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.zipAttrsWith");
|
||||
const auto listSize = args[1]->listSize();
|
||||
const auto listElems = args[1]->listElems();
|
||||
const auto listItems = args[1]->listItems();
|
||||
|
||||
for (unsigned int n = 0; n < listSize; ++n) {
|
||||
Value * vElem = listElems[n];
|
||||
for (auto & vElem : listItems) {
|
||||
state.forceAttrs(*vElem, noPos, "while evaluating a value of the list passed as second argument to builtins.zipAttrsWith");
|
||||
for (auto & attr : *vElem->attrs)
|
||||
attrsSeen[attr.name].first++;
|
||||
attrsSeen.try_emplace(attr.name).first->second.size++;
|
||||
}
|
||||
|
||||
for (auto & [sym, elem] : attrsSeen)
|
||||
elem.list.emplace(state.buildList(elem.size));
|
||||
|
||||
for (auto & vElem : listItems) {
|
||||
for (auto & attr : *vElem->attrs) {
|
||||
auto & item = attrsSeen.at(attr.name);
|
||||
(*item.list)[item.pos++] = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
auto attrs = state.buildBindings(attrsSeen.size());
|
||||
|
||||
for (auto & [sym, elem] : attrsSeen) {
|
||||
auto & list = attrs.alloc(sym);
|
||||
state.mkList(list, elem.first);
|
||||
elem.second = list.listElems();
|
||||
}
|
||||
v.mkAttrs(attrs.alreadySorted());
|
||||
|
||||
for (unsigned int n = 0; n < listSize; ++n) {
|
||||
Value * vElem = listElems[n];
|
||||
for (auto & attr : *vElem->attrs)
|
||||
*attrsSeen[attr.name].second++ = attr.value;
|
||||
}
|
||||
|
||||
for (auto & attr : *v.attrs) {
|
||||
auto name = state.allocValue();
|
||||
name->mkString(state.symbols[attr.name]);
|
||||
name->mkString(state.symbols[sym]);
|
||||
auto call1 = state.allocValue();
|
||||
call1->mkApp(args[0], name);
|
||||
auto call2 = state.allocValue();
|
||||
call2->mkApp(call1, attr.value);
|
||||
attr.value = call2;
|
||||
auto arg = state.allocValue();
|
||||
arg->mkList(*elem.list);
|
||||
call2->mkApp(call1, arg);
|
||||
attrs.insert(sym, call2);
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs.alreadySorted());
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_zipAttrsWith({
|
||||
|
@ -3055,9 +3065,10 @@ static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value
|
|||
if (args[0]->listSize() == 0)
|
||||
state.error<EvalError>("'tail' called on an empty list").atPos(pos).debugThrow();
|
||||
|
||||
state.mkList(v, args[0]->listSize() - 1);
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
v.listElems()[n] = args[0]->listElems()[n + 1];
|
||||
auto list = state.buildList(args[0]->listSize() - 1);
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
v = args[0]->listElems()[n + 1];
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_tail({
|
||||
|
@ -3088,10 +3099,11 @@ static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value
|
|||
|
||||
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.map");
|
||||
|
||||
state.mkList(v, args[1]->listSize());
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
(v.listElems()[n] = state.allocValue())->mkApp(
|
||||
auto list = state.buildList(args[1]->listSize());
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
(v = state.allocValue())->mkApp(
|
||||
args[0], args[1]->listElems()[n]);
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_map({
|
||||
|
@ -3140,8 +3152,9 @@ static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Val
|
|||
if (same)
|
||||
v = *args[1];
|
||||
else {
|
||||
state.mkList(v, k);
|
||||
for (unsigned int n = 0; n < k; ++n) v.listElems()[n] = vs[n];
|
||||
auto list = state.buildList(k);
|
||||
for (const auto & [n, v] : enumerate(list)) v = vs[n];
|
||||
v.mkList(list);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3316,12 +3329,13 @@ static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Va
|
|||
// as evaluating map without accessing any values makes little sense.
|
||||
state.forceFunction(*args[0], noPos, "while evaluating the first argument passed to builtins.genList");
|
||||
|
||||
state.mkList(v, len);
|
||||
for (unsigned int n = 0; n < (unsigned int) len; ++n) {
|
||||
auto list = state.buildList(len);
|
||||
for (const auto & [n, v] : enumerate(list)) {
|
||||
auto arg = state.allocValue();
|
||||
arg->mkInt(n);
|
||||
(v.listElems()[n] = state.allocValue())->mkApp(args[0], arg);
|
||||
(v = state.allocValue())->mkApp(args[0], arg);
|
||||
}
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_genList({
|
||||
|
@ -3355,11 +3369,10 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value
|
|||
|
||||
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.sort");
|
||||
|
||||
state.mkList(v, len);
|
||||
for (unsigned int n = 0; n < len; ++n) {
|
||||
state.forceValue(*args[1]->listElems()[n], pos);
|
||||
v.listElems()[n] = args[1]->listElems()[n];
|
||||
}
|
||||
auto list = state.buildList(len);
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
state.forceValue(*(v = args[1]->listElems()[n]), pos);
|
||||
v.mkList(list);
|
||||
|
||||
auto comparator = [&](Value * a, Value * b) {
|
||||
/* Optimization: if the comparator is lessThan, bypass
|
||||
|
@ -3424,17 +3437,17 @@ static void prim_partition(EvalState & state, const PosIdx pos, Value * * args,
|
|||
|
||||
auto attrs = state.buildBindings(2);
|
||||
|
||||
auto & vRight = attrs.alloc(state.sRight);
|
||||
auto rsize = right.size();
|
||||
state.mkList(vRight, rsize);
|
||||
auto rlist = state.buildList(rsize);
|
||||
if (rsize)
|
||||
memcpy(vRight.listElems(), right.data(), sizeof(Value *) * rsize);
|
||||
memcpy(rlist.elems, right.data(), sizeof(Value *) * rsize);
|
||||
attrs.alloc(state.sRight).mkList(rlist);
|
||||
|
||||
auto & vWrong = attrs.alloc(state.sWrong);
|
||||
auto wsize = wrong.size();
|
||||
state.mkList(vWrong, wsize);
|
||||
auto wlist = state.buildList(wsize);
|
||||
if (wsize)
|
||||
memcpy(vWrong.listElems(), wrong.data(), sizeof(Value *) * wsize);
|
||||
memcpy(wlist.elems, wrong.data(), sizeof(Value *) * wsize);
|
||||
attrs.alloc(state.sWrong).mkList(wlist);
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
@ -3481,10 +3494,10 @@ static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Va
|
|||
auto attrs2 = state.buildBindings(attrs.size());
|
||||
|
||||
for (auto & i : attrs) {
|
||||
auto & list = attrs2.alloc(i.first);
|
||||
auto size = i.second.size();
|
||||
state.mkList(list, size);
|
||||
memcpy(list.listElems(), i.second.data(), sizeof(Value *) * size);
|
||||
auto list = state.buildList(size);
|
||||
memcpy(list.elems, i.second.data(), sizeof(Value *) * size);
|
||||
attrs2.alloc(i.first).mkList(list);
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs2.alreadySorted());
|
||||
|
@ -3531,14 +3544,15 @@ static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args,
|
|||
len += lists[n].listSize();
|
||||
}
|
||||
|
||||
state.mkList(v, len);
|
||||
auto out = v.listElems();
|
||||
auto list = state.buildList(len);
|
||||
auto out = list.elems;
|
||||
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
|
||||
auto l = lists[n].listSize();
|
||||
if (l)
|
||||
memcpy(out + pos, lists[n].listElems(), l * sizeof(Value *));
|
||||
pos += l;
|
||||
}
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_concatMap({
|
||||
|
@ -3986,14 +4000,13 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
|||
}
|
||||
|
||||
// the first match is the whole string
|
||||
const size_t len = match.size() - 1;
|
||||
state.mkList(v, len);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (!match[i+1].matched)
|
||||
(v.listElems()[i] = state.allocValue())->mkNull();
|
||||
auto list = state.buildList(match.size() - 1);
|
||||
for (const auto & [i, v2] : enumerate(list))
|
||||
if (!match[i + 1].matched)
|
||||
(v2 = state.allocValue())->mkNull();
|
||||
else
|
||||
(v.listElems()[i] = state.allocValue())->mkString(match[i + 1].str());
|
||||
}
|
||||
(v2 = state.allocValue())->mkString(match[i + 1].str());
|
||||
v.mkList(list);
|
||||
|
||||
} catch (std::regex_error & e) {
|
||||
if (e.code() == std::regex_constants::error_space) {
|
||||
|
@ -4062,11 +4075,12 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
|||
|
||||
// Any matches results are surrounded by non-matching results.
|
||||
const size_t len = std::distance(begin, end);
|
||||
state.mkList(v, 2 * len + 1);
|
||||
auto list = state.buildList(2 * len + 1);
|
||||
size_t idx = 0;
|
||||
|
||||
if (len == 0) {
|
||||
v.listElems()[idx++] = args[1];
|
||||
list[0] = args[1];
|
||||
v.mkList(list);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4075,28 +4089,31 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
|||
auto match = *i;
|
||||
|
||||
// Add a string for non-matched characters.
|
||||
(v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str());
|
||||
(list[idx++] = state.allocValue())->mkString(match.prefix().str());
|
||||
|
||||
// Add a list for matched substrings.
|
||||
const size_t slen = match.size() - 1;
|
||||
auto elem = v.listElems()[idx++] = state.allocValue();
|
||||
|
||||
// Start at 1, beacause the first match is the whole string.
|
||||
state.mkList(*elem, slen);
|
||||
for (size_t si = 0; si < slen; ++si) {
|
||||
auto list2 = state.buildList(slen);
|
||||
for (const auto & [si, v2] : enumerate(list2)) {
|
||||
if (!match[si + 1].matched)
|
||||
(elem->listElems()[si] = state.allocValue())->mkNull();
|
||||
v2 = &state.vNull;
|
||||
else
|
||||
(elem->listElems()[si] = state.allocValue())->mkString(match[si + 1].str());
|
||||
(v2 = state.allocValue())->mkString(match[si + 1].str());
|
||||
}
|
||||
|
||||
(list[idx++] = state.allocValue())->mkList(list2);
|
||||
|
||||
// Add a string for non-matched suffix characters.
|
||||
if (idx == 2 * len)
|
||||
(v.listElems()[idx++] = state.allocValue())->mkString(match.suffix().str());
|
||||
(list[idx++] = state.allocValue())->mkString(match.suffix().str());
|
||||
}
|
||||
|
||||
assert(idx == 2 * len + 1);
|
||||
|
||||
v.mkList(list);
|
||||
|
||||
} catch (std::regex_error & e) {
|
||||
if (e.code() == std::regex_constants::error_space) {
|
||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||
|
@ -4316,9 +4333,10 @@ static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * arg
|
|||
break;
|
||||
components.emplace_back(component);
|
||||
}
|
||||
state.mkList(v, components.size());
|
||||
auto list = state.buildList(components.size());
|
||||
for (const auto & [n, component] : enumerate(components))
|
||||
(v.listElems()[n] = state.allocValue())->mkString(std::move(component));
|
||||
(list[n] = state.allocValue())->mkString(std::move(component));
|
||||
v.mkList(list);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_splitVersion({
|
||||
|
@ -4559,14 +4577,14 @@ void EvalState::createBaseEnv()
|
|||
});
|
||||
|
||||
/* Add a value containing the current Nix expression search path. */
|
||||
mkList(v, searchPath.elements.size());
|
||||
int n = 0;
|
||||
for (auto & i : searchPath.elements) {
|
||||
auto list = buildList(searchPath.elements.size());
|
||||
for (const auto & [n, i] : enumerate(searchPath.elements)) {
|
||||
auto attrs = buildBindings(2);
|
||||
attrs.alloc("path").mkString(i.path.s);
|
||||
attrs.alloc("prefix").mkString(i.prefix.s);
|
||||
(v.listElems()[n++] = allocValue())->mkAttrs(attrs);
|
||||
(list[n] = allocValue())->mkAttrs(attrs);
|
||||
}
|
||||
v.mkList(list);
|
||||
addConstant("__nixPath", v, {
|
||||
.type = nList,
|
||||
.doc = R"(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue