From 9b9446e860a6fc6387d257b60f3760239ed94734 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Fri, 21 Nov 2025 18:38:14 +0100 Subject: [PATCH 1/2] c api: shovel EvalMemory * into nix_value this is a painful change. we should really add EvalState or EvalMemory as an argument to various functions as we need it, but because we want to preserve the stablity API, we hack it in as a field of nix_value. --- src/libexpr-c/nix_api_expr.cc | 22 ++++++---- src/libexpr-c/nix_api_expr_internal.h | 8 +++- src/libexpr-c/nix_api_value.cc | 58 ++++++++++++++++----------- src/libflake-c/nix_api_flake.cc | 2 +- 4 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 76f3b6486df..0dd9fa0a51d 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -69,8 +69,8 @@ nix_err nix_expr_eval_from_string( context->last_err_code = NIX_OK; try { nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path))); - state->state.eval(parsedExpr, value->value); - state->state.forceValue(value->value, nix::noPos); + state->state.eval(parsedExpr, *value->value); + state->state.forceValue(*value->value, nix::noPos); } NIXC_CATCH_ERRS } @@ -80,8 +80,8 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, n if (context) context->last_err_code = NIX_OK; try { - state->state.callFunction(fn->value, arg->value, value->value, nix::noPos); - state->state.forceValue(value->value, nix::noPos); + state->state.callFunction(*fn->value, *arg->value, *value->value, nix::noPos); + state->state.forceValue(*value->value, nix::noPos); } NIXC_CATCH_ERRS } @@ -91,9 +91,15 @@ nix_err nix_value_call_multi( { if (context) context->last_err_code = NIX_OK; + + std::vector internal_args; + internal_args.reserve(nargs); + for (size_t i = 0; i < nargs; i++) + internal_args.push_back(args[i]->value); + try { - state->state.callFunction(fn->value, {(nix::Value **) args, nargs}, value->value, nix::noPos); - state->state.forceValue(value->value, nix::noPos); + state->state.callFunction(*fn->value, {internal_args.data(), nargs}, *value->value, nix::noPos); + state->state.forceValue(*value->value, nix::noPos); } NIXC_CATCH_ERRS } @@ -103,7 +109,7 @@ nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value * if (context) context->last_err_code = NIX_OK; try { - state->state.forceValue(value->value, nix::noPos); + state->state.forceValue(*value->value, nix::noPos); } NIXC_CATCH_ERRS } @@ -113,7 +119,7 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_val if (context) context->last_err_code = NIX_OK; try { - state->state.forceValueDeep(value->value); + state->state.forceValueDeep(*value->value); } NIXC_CATCH_ERRS } diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index 3aa1d993225..07c7a2194df 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -39,7 +39,13 @@ struct ListBuilder struct nix_value { - nix::Value value; + nix::Value * value; + /** + * As we move to a managed heap, we need EvalMemory in more places. Ideally, we would take in EvalState or + * EvalMemory as an argument when we need it, but we don't want to make changes to the stable C api, so we stuff it + * into the nix_value that will get passed in to the relevant functions. + */ + nix::EvalMemory * mem; }; struct nix_string_return diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index e231c36f408..f41f50a016e 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -20,7 +20,7 @@ static const nix::Value & check_value_not_null(const nix_value * value) if (!value) { throw std::runtime_error("nix_value is null"); } - return *((const nix::Value *) value); + return *value->value; } static nix::Value & check_value_not_null(nix_value * value) @@ -28,7 +28,7 @@ static nix::Value & check_value_not_null(nix_value * value) if (!value) { throw std::runtime_error("nix_value is null"); } - return value->value; + return *value->value; } static const nix::Value & check_value_in(const nix_value * value) @@ -58,9 +58,14 @@ static nix::Value & check_value_out(nix_value * value) return v; } -static inline nix_value * as_nix_value_ptr(nix::Value * v) +static inline nix_value * new_nix_value(nix::Value * v, nix::EvalMemory & mem) { - return reinterpret_cast(v); + nix_value * ret = new (mem.allocBytes(sizeof(nix_value))) nix_value{ + .value = v, + .mem = &mem, + }; + nix_gc_incref(nullptr, ret); + return ret; } /** @@ -69,7 +74,13 @@ static inline nix_value * as_nix_value_ptr(nix::Value * v) * Deals with errors and converts arguments from C++ into C types. */ static void nix_c_primop_wrapper( - PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v) + PrimOpFun f, + void * userdata, + int arity, + nix::EvalState & state, + const nix::PosIdx pos, + nix::Value ** args, + nix::Value & v) { nix_c_context ctx; @@ -85,8 +96,15 @@ static void nix_c_primop_wrapper( // ok because we don't see a need for this yet (e.g. inspecting thunks, // or maybe something to make blackholes work better; we don't know). nix::Value vTmp; + nix_value * vTmpPtr = new_nix_value(&vTmp, state.mem); - f(userdata, &ctx, (EvalState *) &state, (nix_value **) args, (nix_value *) &vTmp); + std::vector external_args; + external_args.reserve(arity); + for (int i = 0; i < arity; i++) { + nix_value * external_arg = new_nix_value(args[i], state.mem); + external_args.push_back(external_arg); + } + f(userdata, &ctx, (EvalState *) &state, external_args.data(), vTmpPtr); if (ctx.last_err_code != NIX_OK) { /* TODO: Throw different errors depending on the error code */ @@ -135,7 +153,7 @@ PrimOp * nix_alloc_primop( .args = {}, .arity = (size_t) arity, .doc = doc, - .fun = std::bind(nix_c_primop_wrapper, fun, user_data, _1, _2, _3, _4)}; + .fun = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)}; if (args) for (size_t i = 0; args[i]; i++) p->args.emplace_back(*args); @@ -160,8 +178,7 @@ nix_value * nix_alloc_value(nix_c_context * context, EvalState * state) if (context) context->last_err_code = NIX_OK; try { - nix_value * res = as_nix_value_ptr(state->state.allocValue()); - nix_gc_incref(nullptr, res); + nix_value * res = new_nix_value(state->state.allocValue(), state->state.mem); return res; } NIXC_CATCH_ERRS_NULL @@ -331,10 +348,10 @@ nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value, return nullptr; } auto * p = v.listView()[ix]; - nix_gc_incref(nullptr, p); - if (p != nullptr) - state->state.forceValue(*p, nix::noPos); - return as_nix_value_ptr(p); + if (p == nullptr) + return nullptr; + state->state.forceValue(*p, nix::noPos); + return new_nix_value(p, state->state.mem); } NIXC_CATCH_ERRS_NULL } @@ -352,9 +369,8 @@ nix_get_list_byidx_lazy(nix_c_context * context, const nix_value * value, EvalSt return nullptr; } auto * p = v.listView()[ix]; - nix_gc_incref(nullptr, p); // Note: intentionally NOT calling forceValue() to keep the element lazy - return as_nix_value_ptr(p); + return new_nix_value(p, state->state.mem); } NIXC_CATCH_ERRS_NULL } @@ -369,9 +385,8 @@ nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); if (attr) { - nix_gc_incref(nullptr, attr->value); state->state.forceValue(*attr->value, nix::noPos); - return as_nix_value_ptr(attr->value); + return new_nix_value(attr->value, state->state.mem); } nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); return nullptr; @@ -390,9 +405,8 @@ nix_get_attr_byname_lazy(nix_c_context * context, const nix_value * value, EvalS nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); if (attr) { - nix_gc_incref(nullptr, attr->value); // Note: intentionally NOT calling forceValue() to keep the attribute lazy - return as_nix_value_ptr(attr->value); + return new_nix_value(attr->value, state->state.mem); } nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); return nullptr; @@ -440,9 +454,8 @@ nix_get_attr_byidx(nix_c_context * context, nix_value * value, EvalState * state } const nix::Attr & a = (*v.attrs())[i]; *name = state->state.symbols[a.name].c_str(); - nix_gc_incref(nullptr, a.value); state->state.forceValue(*a.value, nix::noPos); - return as_nix_value_ptr(a.value); + return new_nix_value(a.value, state->state.mem); } NIXC_CATCH_ERRS_NULL } @@ -461,9 +474,8 @@ nix_value * nix_get_attr_byidx_lazy( } const nix::Attr & a = (*v.attrs())[i]; *name = state->state.symbols[a.name].c_str(); - nix_gc_incref(nullptr, a.value); // Note: intentionally NOT calling forceValue() to keep the attribute lazy - return as_nix_value_ptr(a.value); + return new_nix_value(a.value, state->state.mem); } NIXC_CATCH_ERRS_NULL } diff --git a/src/libflake-c/nix_api_flake.cc b/src/libflake-c/nix_api_flake.cc index 2de0e667ec3..32329585a66 100644 --- a/src/libflake-c/nix_api_flake.cc +++ b/src/libflake-c/nix_api_flake.cc @@ -200,7 +200,7 @@ nix_value * nix_locked_flake_get_output_attrs( nix_clear_err(context); try { auto v = nix_alloc_value(context, evalState); - nix::flake::callFlake(evalState->state, *lockedFlake->lockedFlake, v->value); + nix::flake::callFlake(evalState->state, *lockedFlake->lockedFlake, *v->value); return v; } NIXC_CATCH_ERRS_NULL From 7cd325294681beb5d86cec094e4fc0ea7e3fc0e3 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 17 Nov 2025 16:33:12 +0100 Subject: [PATCH 2/2] libexpr: use allocBytes() to allocate StringData --- src/libcmd/common-eval-args.cc | 6 ++-- src/libexpr-c/nix_api_value.cc | 4 +-- src/libexpr-tests/json.cc | 2 +- src/libexpr-tests/value/print.cc | 4 +-- src/libexpr/eval.cc | 26 +++++++-------- src/libexpr/include/nix/expr/value.hh | 8 ++--- src/libexpr/json-to-value.cc | 2 +- src/libexpr/primops.cc | 46 ++++++++++++++------------- src/libexpr/primops/context.cc | 4 +-- src/libexpr/primops/fetchMercurial.cc | 6 ++-- src/libexpr/primops/fetchTree.cc | 17 +++++----- src/libexpr/primops/fromTOML.cc | 4 +-- src/libflake/flake-primops.cc | 4 +-- src/libflake/flake.cc | 4 +-- src/nix/main.cc | 2 +- src/nix/nix-env/nix-env.cc | 4 +-- src/nix/nix-env/user-env.cc | 12 +++---- 17 files changed, 79 insertions(+), 76 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 8b9f4926697..00b09f4ea8f 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -168,9 +168,9 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath("."))); }, - [&](const AutoArgString & arg) { v->mkString(arg.s); }, - [&](const AutoArgFile & arg) { v->mkString(readFile(arg.path.string())); }, - [&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO)); }}, + [&](const AutoArgString & arg) { v->mkString(arg.s, state.mem); }, + [&](const AutoArgFile & arg) { v->mkString(readFile(arg.path.string()), state.mem); }, + [&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO), state.mem); }}, arg); res.insert(state.symbols.create(name), v); } diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index f41f50a016e..be8ff4926f4 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -515,7 +515,7 @@ nix_err nix_init_string(nix_c_context * context, nix_value * value, const char * context->last_err_code = NIX_OK; try { auto & v = check_value_out(value); - v.mkString(std::string_view(str)); + v.mkString(std::string_view(str), *value->mem); } NIXC_CATCH_ERRS } @@ -526,7 +526,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, nix_value * context->last_err_code = NIX_OK; try { auto & v = check_value_out(value); - v.mkPath(s->state.rootPath(nix::CanonPath(str))); + v.mkPath(s->state.rootPath(nix::CanonPath(str)), s->state.mem); } NIXC_CATCH_ERRS } diff --git a/src/libexpr-tests/json.cc b/src/libexpr-tests/json.cc index aa71c4d86b2..31e7a18c5bd 100644 --- a/src/libexpr-tests/json.cc +++ b/src/libexpr-tests/json.cc @@ -73,7 +73,7 @@ TEST_F(JSONValueTest, StringQuotes) TEST_F(JSONValueTest, DISABLED_Path) { Value v; - v.mkPath(state.rootPath(CanonPath("/test"))); + v.mkPath(state.rootPath(CanonPath("/test")), state.mem); ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\""); } } /* namespace nix */ diff --git a/src/libexpr-tests/value/print.cc b/src/libexpr-tests/value/print.cc index 0456835b4fa..d226062197d 100644 --- a/src/libexpr-tests/value/print.cc +++ b/src/libexpr-tests/value/print.cc @@ -268,7 +268,7 @@ struct StringPrintingTests : LibExprTest void test(std::string_view literal, std::string_view expected, unsigned int maxLength, A... args) { Value v; - v.mkString(literal); + v.mkString(literal, state.mem); std::stringstream out; printValue(state, out, v, PrintOptions{.maxStringLength = maxLength}); @@ -353,7 +353,7 @@ TEST_F(ValuePrintingTests, ansiColorsStringElided) TEST_F(ValuePrintingTests, ansiColorsPath) { Value v; - v.mkPath(state.rootPath(CanonPath("puppy"))); + v.mkPath(state.rootPath(CanonPath("puppy")), state.mem); test(v, ANSI_GREEN "/puppy" ANSI_NORMAL, PrintOptions{.ansiColors = true}); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 385d4c05fee..19ff51de769 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -81,20 +81,20 @@ static const char * makeImmutableString(std::string_view s) return t; } -StringData & StringData::alloc(size_t size) +StringData & StringData::alloc(EvalMemory & mem, size_t size) { - void * t = GC_MALLOC_ATOMIC(sizeof(StringData) + size + 1); + void * t = mem.allocBytes(sizeof(StringData) + size + 1); if (!t) throw std::bad_alloc(); auto res = new (t) StringData(size); return *res; } -const StringData & StringData::make(std::string_view s) +const StringData & StringData::make(EvalMemory & mem, std::string_view s) { if (s.empty()) return ""_sds; - auto & res = alloc(s.size()); + auto & res = alloc(mem, s.size()); std::memcpy(&res.data_, s.data(), s.size()); res.data_[s.size()] = '\0'; return res; @@ -849,9 +849,9 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t) evalState.runDebugRepl(nullptr, trace.env, trace.expr); } -void Value::mkString(std::string_view s) +void Value::mkString(std::string_view s, EvalMemory & mem) { - mkStringNoCopy(StringData::make(s)); + mkStringNoCopy(StringData::make(mem, s)); } Value::StringWithContext::Context * @@ -862,13 +862,13 @@ Value::StringWithContext::Context::fromBuilder(const NixStringContext & context, auto ctx = new (mem.allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size()); std::ranges::transform( - context, ctx->elems, [](const NixStringContextElem & elt) { return &StringData::make(elt.to_string()); }); + context, ctx->elems, [&](const NixStringContextElem & elt) { return &StringData::make(mem, elt.to_string()); }); return ctx; } void Value::mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem) { - mkStringNoCopy(StringData::make(s), Value::StringWithContext::Context::fromBuilder(context, mem)); + mkStringNoCopy(StringData::make(mem, s), Value::StringWithContext::Context::fromBuilder(context, mem)); } void Value::mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem) @@ -876,9 +876,9 @@ void Value::mkStringMove(const StringData & s, const NixStringContext & context, mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context, mem)); } -void Value::mkPath(const SourcePath & path) +void Value::mkPath(const SourcePath & path, EvalMemory & mem) { - mkPath(&*path.accessor, StringData::make(path.path.abs())); + mkPath(&*path.accessor, StringData::make(mem, path.path.abs())); } inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) @@ -943,7 +943,7 @@ void EvalState::mkPos(Value & v, PosIdx p) auto origin = positions.originOf(p); if (auto path = std::get_if(&origin)) { auto attrs = buildBindings(3); - attrs.alloc(s.file).mkString(path->path.abs()); + attrs.alloc(s.file).mkString(path->path.abs(), mem); makePositionThunks(*this, p, attrs.alloc(s.line), attrs.alloc(s.column)); v.mkAttrs(attrs); } else @@ -2139,9 +2139,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) for (const auto & part : strings) { resultStr += *part; } - v.mkPath(state.rootPath(CanonPath(resultStr))); + v.mkPath(state.rootPath(CanonPath(resultStr)), state.mem); } else { - auto & resultStr = StringData::alloc(sSize); + auto & resultStr = StringData::alloc(state.mem, sSize); auto * tmp = resultStr.data(); for (const auto & part : strings) { std::memcpy(tmp, part->data(), part->size()); diff --git a/src/libexpr/include/nix/expr/value.hh b/src/libexpr/include/nix/expr/value.hh index ff62092f2d0..004dcc43f0f 100644 --- a/src/libexpr/include/nix/expr/value.hh +++ b/src/libexpr/include/nix/expr/value.hh @@ -232,13 +232,13 @@ public: * Allocate StringData on the (possibly) GC-managed heap and copy * the contents of s to it. */ - static const StringData & make(std::string_view s); + static const StringData & make(EvalMemory & mem, std::string_view s); /** * Allocate StringData on the (possibly) GC-managed heap. * @param size Length of the string (without the NUL terminator). */ - static StringData & alloc(size_t size); + static StringData & alloc(EvalMemory & mem, size_t size); size_t size() const { @@ -1147,13 +1147,13 @@ public: setStorage(StringWithContext{.str = &s, .context = context}); } - void mkString(std::string_view s); + void mkString(std::string_view s, EvalMemory & mem); void mkString(std::string_view s, const NixStringContext & context, EvalMemory & mem); void mkStringMove(const StringData & s, const NixStringContext & context, EvalMemory & mem); - void mkPath(const SourcePath & path); + void mkPath(const SourcePath & path, EvalMemory & mem); inline void mkPath(SourceAccessor * accessor, const StringData & path) noexcept { diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 9c645e7fd83..4a68308c641 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -151,7 +151,7 @@ class JSONSax : nlohmann::json_sax bool string(string_t & val) override { forceNoNullByte(val); - rs->value(state).mkString(val); + rs->value(state).mkString(val, state.mem); rs->add(); return true; } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 35f16a68de0..573dee74f80 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -53,7 +53,7 @@ RegisterPrimOp::PrimOps & RegisterPrimOp::primOps() static inline Value * mkString(EvalState & state, const std::csub_match & match) { Value * v = state.allocValue(); - v->mkString({match.first, match.second}); + v->mkString({match.first, match.second}, state.mem); return v; } @@ -230,12 +230,12 @@ void derivationToValue( NixStringContextElem::DrvDeep{.drvPath = storePath}, }, state.mem); - attrs.alloc(state.s.name).mkString(drv.env["name"]); + attrs.alloc(state.s.name).mkString(drv.env["name"], state.mem); auto list = state.buildList(drv.outputs.size()); for (const auto & [i, o] : enumerate(drv.outputs)) { mkOutputString(state, attrs, storePath, o); - (list[i] = state.allocValue())->mkString(o.first); + (list[i] = state.allocValue())->mkString(o.first, state.mem); } attrs.alloc(state.s.outputs).mkList(list); @@ -519,7 +519,7 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value ** args, Valu v.mkStringNoCopy("lambda"_sds); break; case nExternal: - v.mkString(args[0]->external()->typeOf()); + v.mkString(args[0]->external()->typeOf(), state.mem); break; case nFloat: v.mkStringNoCopy("float"_sds); @@ -1176,7 +1176,7 @@ static void prim_getEnv(EvalState & state, const PosIdx pos, Value ** args, Valu { std::string name( state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getEnv")); - v.mkString(state.settings.restrictEval || state.settings.pureEval ? "" : getEnv(name).value_or("")); + v.mkString(state.settings.restrictEval || state.settings.pureEval ? "" : getEnv(name).value_or(""), state.mem); } static RegisterPrimOp primop_getEnv({ @@ -1842,8 +1842,10 @@ static RegisterPrimOp primop_derivationStrict( ‘out’. */ static void prim_placeholder(EvalState & state, const PosIdx pos, Value ** args, Value & v) { - v.mkString(hashPlaceholder( - state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.placeholder"))); + v.mkString( + hashPlaceholder(state.forceStringNoCtx( + *args[0], pos, "while evaluating the first argument passed to builtins.placeholder")), + state.mem); } static RegisterPrimOp primop_placeholder({ @@ -2027,7 +2029,7 @@ static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, Value state.forceValue(*args[0], pos); if (args[0]->type() == nPath) { auto path = args[0]->path(); - v.mkPath(path.path.isRoot() ? path : path.parent()); + v.mkPath(path.path.isRoot() ? path : path.parent(), state.mem); } else { NixStringContext context; auto path = state.coerceToString( @@ -2144,7 +2146,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value ** args, Va auto path = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.findFile"); - v.mkPath(state.findFile(lookupPath, path, pos)); + v.mkPath(state.findFile(lookupPath, path, pos), state.mem); } static RegisterPrimOp primop_findFile( @@ -2293,7 +2295,7 @@ static void prim_hashFile(EvalState & state, const PosIdx pos, Value ** args, Va auto path = realisePath(state, pos, *args[1]); - v.mkString(hashString(*ha, path.readFile()).to_string(HashFormat::Base16, false)); + v.mkString(hashString(*ha, path.readFile()).to_string(HashFormat::Base16, false), state.mem); } static RegisterPrimOp primop_hashFile({ @@ -2382,7 +2384,7 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value ** args, Val // detailed node info quickly in this case we produce a thunk to // query the file type lazily. auto epath = state.allocValue(); - epath->mkPath(path / name); + epath->mkPath(path / name, state.mem); if (!readFileType) readFileType = &state.getBuiltin("readFileType"); attr.mkApp(readFileType, epath); @@ -2763,7 +2765,7 @@ bool EvalState::callPathFilter(Value * filterFun, const SourcePath & path, PosId /* Call the filter function. The first argument is the path, the second is a string indicating the type of the file. */ Value arg1; - arg1.mkString(path.path.abs()); + arg1.mkString(path.path.abs(), mem); // assert that type is not "unknown" Value * args[]{&arg1, const_cast(&fileTypeToString(*this, st.type))}; @@ -4541,7 +4543,7 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value ** args, auto s = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.hashString"); - v.mkString(hashString(*ha, s).to_string(HashFormat::Base16, false)); + v.mkString(hashString(*ha, s).to_string(HashFormat::Base16, false), state.mem); } static RegisterPrimOp primop_hashString({ @@ -4574,7 +4576,7 @@ static void prim_convertHash(EvalState & state, const PosIdx pos, Value ** args, HashFormat hf = parseHashFormat( state.forceStringNoCtx(*iteratorToHashFormat->value, pos, "while evaluating the attribute 'toHashFormat'")); - v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI)); + v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI), state.mem); } static RegisterPrimOp primop_convertHash({ @@ -4992,8 +4994,8 @@ static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value ** args state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.parseDrvName"); DrvName parsed(name); auto attrs = state.buildBindings(2); - attrs.alloc(state.s.name).mkString(parsed.name); - attrs.alloc("version").mkString(parsed.version); + attrs.alloc(state.s.name).mkString(parsed.name, state.mem); + attrs.alloc("version").mkString(parsed.version, state.mem); v.mkAttrs(attrs); } @@ -5048,7 +5050,7 @@ static void prim_splitVersion(EvalState & state, const PosIdx pos, Value ** args } auto list = state.buildList(components.size()); for (const auto & [n, component] : enumerate(components)) - (list[n] = state.allocValue())->mkString(std::move(component)); + (list[n] = state.allocValue())->mkString(std::move(component), state.mem); v.mkList(list); } @@ -5192,7 +5194,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings) }); if (!settings.pureEval) - v.mkString(settings.getCurrentSystem()); + v.mkString(settings.getCurrentSystem(), mem); addConstant( "__currentSystem", v, @@ -5224,7 +5226,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings) .impureOnly = true, }); - v.mkString(nixVersion); + v.mkString(nixVersion, mem); addConstant( "__nixVersion", v, @@ -5249,7 +5251,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings) )", }); - v.mkString(store->storeDir); + v.mkString(store->storeDir, mem); addConstant( "__storeDir", v, @@ -5314,8 +5316,8 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings) auto list = buildList(lookupPath.elements.size()); for (const auto & [n, i] : enumerate(lookupPath.elements)) { auto attrs = buildBindings(2); - attrs.alloc("path").mkString(i.path.s); - attrs.alloc("prefix").mkString(i.prefix.s); + attrs.alloc("path").mkString(i.path.s, mem); + attrs.alloc("prefix").mkString(i.prefix.s, mem); (list[n] = allocValue())->mkAttrs(attrs); } v.mkList(list); diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 2c5add14826..70c13e2985b 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -11,7 +11,7 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, NixStringContext context; auto s = state.coerceToString( pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext"); - v.mkString(*s); + v.mkString(*s, state.mem); } static RegisterPrimOp primop_unsafeDiscardStringContext({ @@ -218,7 +218,7 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args, if (!info.second.outputs.empty()) { auto list = state.buildList(info.second.outputs.size()); for (const auto & [i, output] : enumerate(info.second.outputs)) - (list[i] = state.allocValue())->mkString(output); + (list[i] = state.allocValue())->mkString(output, state.mem); infoAttrs.alloc(state.s.outputs).mkList(list); } attrs.alloc(state.store->printStorePath(info.first)).mkAttrs(infoAttrs); diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 2174275ec8e..cc42931a61e 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -86,12 +86,12 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** ar auto attrs2 = state.buildBindings(8); state.mkStorePathString(storePath, attrs2.alloc(state.s.outPath)); if (input2.getRef()) - attrs2.alloc("branch").mkString(*input2.getRef()); + attrs2.alloc("branch").mkString(*input2.getRef(), state.mem); // Backward compatibility: set 'rev' to // 0000000000000000000000000000000000000000 for a dirty tree. auto rev2 = input2.getRev().value_or(Hash(HashAlgorithm::SHA1)); - attrs2.alloc("rev").mkString(rev2.gitRev()); - attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12)); + attrs2.alloc("rev").mkString(rev2.gitRev(), state.mem); + attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12), state.mem); if (auto revCount = input2.getRevCount()) attrs2.alloc("revCount").mkInt(*revCount); v.mkAttrs(attrs2); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index db976a3e010..1614fcc595d 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -35,7 +35,7 @@ void emitTreeAttrs( // FIXME: support arbitrary input attributes. if (auto narHash = input.getNarHash()) - attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true)); + attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true), state.mem); if (input.getType() == "git") attrs.alloc("submodules").mkBool(fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false)); @@ -43,13 +43,13 @@ void emitTreeAttrs( if (!forceDirty) { if (auto rev = input.getRev()) { - attrs.alloc("rev").mkString(rev->gitRev()); - attrs.alloc("shortRev").mkString(rev->gitShortRev()); + attrs.alloc("rev").mkString(rev->gitRev(), state.mem); + attrs.alloc("shortRev").mkString(rev->gitShortRev(), state.mem); } else if (emptyRevFallback) { // Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev auto emptyHash = Hash(HashAlgorithm::SHA1); - attrs.alloc("rev").mkString(emptyHash.gitRev()); - attrs.alloc("shortRev").mkString(emptyHash.gitShortRev()); + attrs.alloc("rev").mkString(emptyHash.gitRev(), state.mem); + attrs.alloc("shortRev").mkString(emptyHash.gitShortRev(), state.mem); } if (auto revCount = input.getRevCount()) @@ -59,13 +59,14 @@ void emitTreeAttrs( } if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) { - attrs.alloc("dirtyRev").mkString(*dirtyRev); - attrs.alloc("dirtyShortRev").mkString(*fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev")); + attrs.alloc("dirtyRev").mkString(*dirtyRev, state.mem); + attrs.alloc("dirtyShortRev").mkString(*fetchers::maybeGetStrAttr(input.attrs, "dirtyShortRev"), state.mem); } if (auto lastModified = input.getLastModified()) { attrs.alloc("lastModified").mkInt(*lastModified); - attrs.alloc("lastModifiedDate").mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S"))); + attrs.alloc("lastModifiedDate") + .mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")), state.mem); } v.mkAttrs(attrs); diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index a06224fee0c..562ff3d1497 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -126,7 +126,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va case toml::value_t::string: { auto s = toml::get(t); forceNoNullByte(s); - v.mkString(s); + v.mkString(s, state.mem); } break; case toml::value_t::local_datetime: case toml::value_t::offset_datetime: @@ -142,7 +142,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va s << t; auto str = s.view(); forceNoNullByte(str); - attrs.alloc("value").mkString(str); + attrs.alloc("value").mkString(str, state.mem); v.mkAttrs(attrs); } else { throw std::runtime_error("Dates and times are not supported"); diff --git a/src/libflake/flake-primops.cc b/src/libflake/flake-primops.cc index 3f65dc47acc..962edbb3de4 100644 --- a/src/libflake/flake-primops.cc +++ b/src/libflake/flake-primops.cc @@ -93,7 +93,7 @@ static void prim_parseFlakeRef(EvalState & state, const PosIdx pos, Value ** arg auto & vv = binds.alloc(s); std::visit( overloaded{ - [&vv](const std::string & value) { vv.mkString(value); }, + [&vv, &state](const std::string & value) { vv.mkString(value, state.mem); }, [&vv](const uint64_t & value) { vv.mkInt(value); }, [&vv](const Explicit & value) { vv.mkBool(value.t); }}, value); @@ -156,7 +156,7 @@ static void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value ** } } auto flakeRef = FlakeRef::fromAttrs(state.fetchSettings, attrs); - v.mkString(flakeRef.to_string()); + v.mkString(flakeRef.to_string(), state.mem); } nix::PrimOp flakeRefToString({ diff --git a/src/libflake/flake.cc b/src/libflake/flake.cc index 6e5fa6ca16d..9f7476bd0e2 100644 --- a/src/libflake/flake.cc +++ b/src/libflake/flake.cc @@ -956,7 +956,7 @@ void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes) auto key = keyMap.find(node); assert(key != keyMap.end()); - override.alloc(state.symbols.create("dir")).mkString(CanonPath(subdir).rel()); + override.alloc(state.symbols.create("dir")).mkString(CanonPath(subdir).rel(), state.mem); overrides.alloc(state.symbols.create(key->second)).mkAttrs(override); } @@ -966,7 +966,7 @@ void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes) Value * vCallFlake = requireInternalFile(state, CanonPath("call-flake.nix")); auto vLocks = state.allocValue(); - vLocks->mkString(lockFileStr); + vLocks->mkString(lockFileStr, state.mem); auto vFetchFinalTree = get(state.internalPrimOps, "fetchFinalTree"); assert(vFetchFinalTree); diff --git a/src/nix/main.cc b/src/nix/main.cc index 945cce9acac..1d7066449bb 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -271,7 +271,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) ); auto vDump = state.allocValue(); - vDump->mkString(toplevel.dumpCli()); + vDump->mkString(toplevel.dumpCli(), state.mem); auto vRes = state.allocValue(); Value * args[]{&state.getBuiltin("false"), vDump}; diff --git a/src/nix/nix-env/nix-env.cc b/src/nix/nix-env/nix-env.cc index 2a0984d1825..54443a22c94 100644 --- a/src/nix/nix-env/nix-env.cc +++ b/src/nix/nix-env/nix-env.cc @@ -132,7 +132,7 @@ static void getAllExprs(EvalState & state, const SourcePath & path, StringSet & } /* Load the expression on demand. */ auto vArg = state.allocValue(); - vArg->mkPath(path2); + vArg->mkPath(path2, state.mem); if (seen.size() == maxAttrs) throw Error("too many Nix expressions in directory '%1%'", path); attrs.alloc(attrName).mkApp(&state.getBuiltin("import"), vArg); @@ -483,7 +483,7 @@ static bool keep(PackageInfo & drv) static void setMetaFlag(EvalState & state, PackageInfo & drv, const std::string & name, const std::string & value) { auto v = state.allocValue(); - v->mkString(value); + v->mkString(value, state.mem); drv.setMeta(name, v); } diff --git a/src/nix/nix-env/user-env.cc b/src/nix/nix-env/user-env.cc index 21fdf25bc55..d093650731c 100644 --- a/src/nix/nix-env/user-env.cc +++ b/src/nix/nix-env/user-env.cc @@ -58,20 +58,20 @@ bool createUserEnv( auto attrs = state.buildBindings(7 + outputs.size()); attrs.alloc(state.s.type).mkStringNoCopy("derivation"_sds); - attrs.alloc(state.s.name).mkString(i.queryName()); + attrs.alloc(state.s.name).mkString(i.queryName(), state.mem); auto system = i.querySystem(); if (!system.empty()) - attrs.alloc(state.s.system).mkString(system); - attrs.alloc(state.s.outPath).mkString(state.store->printStorePath(i.queryOutPath())); + attrs.alloc(state.s.system).mkString(system, state.mem); + attrs.alloc(state.s.outPath).mkString(state.store->printStorePath(i.queryOutPath()), state.mem); if (drvPath) - attrs.alloc(state.s.drvPath).mkString(state.store->printStorePath(*drvPath)); + attrs.alloc(state.s.drvPath).mkString(state.store->printStorePath(*drvPath), state.mem); // Copy each output meant for installation. auto outputsList = state.buildList(outputs.size()); for (const auto & [m, j] : enumerate(outputs)) { - (outputsList[m] = state.allocValue())->mkString(j.first); + (outputsList[m] = state.allocValue())->mkString(j.first, state.mem); auto outputAttrs = state.buildBindings(2); - outputAttrs.alloc(state.s.outPath).mkString(state.store->printStorePath(*j.second)); + outputAttrs.alloc(state.s.outPath).mkString(state.store->printStorePath(*j.second), state.mem); attrs.alloc(j.first).mkAttrs(outputAttrs); /* This is only necessary when installing store paths, e.g.,