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 76f3b6486..0dd9fa0a5 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 3aa1d9932..07c7a2194 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 e231c36f4..f41f50a01 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 2de0e667e..32329585a 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 8b9f49266..00b09f4ea 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 f41f50a01..be8ff4926 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 aa71c4d86..31e7a18c5 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 0456835b4..d22606219 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 385d4c05f..19ff51de7 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 ff62092f2..004dcc43f 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 9c645e7fd..4a68308c6 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -151,7 +151,7 @@ public: 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 35f16a68d..573dee74f 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 2c5add148..70c13e298 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 2174275ec..cc42931a6 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 db976a3e0..1614fcc59 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 a06224fee..562ff3d14 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 3f65dc47a..962edbb3d 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 6e5fa6ca1..9f7476bd0 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 945cce9ac..1d7066449 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 2a0984d18..54443a22c 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 21fdf25bc..d09365073 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.,