33#include " nix/expr/primops.hh"
44#include " nix/expr/print-options.hh"
55#include " nix/expr/symbol-table.hh"
6+ #include " nix/expr/value.hh"
67#include " nix/util/exit.hh"
78#include " nix/util/types.hh"
89#include " nix/util/util.hh"
2829#include " parser-tab.hh"
2930
3031#include < algorithm>
32+ #include < cstddef>
33+ #include < cstdlib>
3134#include < iostream>
3235#include < sstream>
3336#include < cstring>
@@ -48,6 +51,9 @@ using json = nlohmann::json;
4851
4952namespace nix {
5053
54+ /* *
55+ * Just for doc strings. Not for regular string values.
56+ */
5157static char * allocString (size_t size)
5258{
5359 char * t;
@@ -61,6 +67,9 @@ static char * allocString(size_t size)
6167// string allocations.
6268// This function handles makeImmutableString(std::string_view()) by returning
6369// the empty string.
70+ /* *
71+ * Just for doc strings. Not for regular string values.
72+ */
6473static const char * makeImmutableString (std::string_view s)
6574{
6675 const size_t size = s.size ();
@@ -72,6 +81,25 @@ static const char * makeImmutableString(std::string_view s)
7281 return t;
7382}
7483
84+ StringData & StringData::alloc (size_t size)
85+ {
86+ void * t = GC_MALLOC_ATOMIC (sizeof (StringData) + size + 1 );
87+ if (!t)
88+ throw std::bad_alloc ();
89+ auto res = new (t) StringData (size);
90+ return *res;
91+ }
92+
93+ const StringData & StringData::make (std::string_view s)
94+ {
95+ if (s.empty ())
96+ return " " _sds;
97+ auto & res = alloc (s.size ());
98+ std::memcpy (&res.data_ , s.data (), s.size ());
99+ res.data_ [s.size ()] = ' \0 ' ;
100+ return res;
101+ }
102+
75103RootValue allocRootValue (Value * v)
76104{
77105 return std::allocate_shared<Value *>(traceable_allocator<Value *>(), v);
@@ -585,7 +613,9 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
585613 .name = name,
586614 .arity = 0 , // FIXME: figure out how deep by syntax only? It's not semantically useful though...
587615 .args = {},
588- .doc = makeImmutableString (s.view ()), // NOTE: memory leak when compiled without GC
616+ /* N.B. Can't use StringData here, because that would lead to an interior pointer.
617+ NOTE: memory leak when compiled without GC. */
618+ .doc = makeImmutableString (s.view ()),
589619 };
590620 }
591621 if (isFunctor (v)) {
@@ -819,7 +849,7 @@ DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)
819849
820850void Value::mkString (std::string_view s)
821851{
822- mkStringNoCopy (makeImmutableString (s));
852+ mkStringNoCopy (StringData::make (s));
823853}
824854
825855Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder (const NixStringContext & context)
@@ -829,23 +859,23 @@ Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuild
829859
830860 auto ctx = new (allocBytes (sizeof (Context) + context.size () * sizeof (value_type))) Context (context.size ());
831861 std::ranges::transform (
832- context, ctx->elems , [](const NixStringContextElem & elt) { return makeImmutableString (elt.to_string ()); });
862+ context, ctx->elems , [](const NixStringContextElem & elt) { return & StringData::make (elt.to_string ()); });
833863 return ctx;
834864}
835865
836866void Value::mkString (std::string_view s, const NixStringContext & context)
837867{
838- mkStringNoCopy (makeImmutableString (s), Value::StringWithContext::Context::fromBuilder (context));
868+ mkStringNoCopy (StringData::make (s), Value::StringWithContext::Context::fromBuilder (context));
839869}
840870
841- void Value::mkStringMove (const char * s, const NixStringContext & context)
871+ void Value::mkStringMove (const StringData & s, const NixStringContext & context)
842872{
843873 mkStringNoCopy (s, Value::StringWithContext::Context::fromBuilder (context));
844874}
845875
846876void Value::mkPath (const SourcePath & path)
847877{
848- mkPath (&*path.accessor , makeImmutableString (path.path .abs ()));
878+ mkPath (&*path.accessor , StringData::make (path.path .abs ()));
849879}
850880
851881inline Value * EvalState::lookupVar (Env * env, const ExprVar & var, bool noEval)
@@ -2099,21 +2129,21 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
20992129 .atPos (pos)
21002130 .withFrame (env, *this )
21012131 .debugThrow ();
2102- std::string result_str ;
2103- result_str .reserve (sSize );
2132+ std::string resultStr ;
2133+ resultStr .reserve (sSize );
21042134 for (const auto & part : strings) {
2105- result_str += *part;
2135+ resultStr += *part;
21062136 }
2107- v.mkPath (state.rootPath (CanonPath (result_str )));
2137+ v.mkPath (state.rootPath (CanonPath (resultStr )));
21082138 } else {
2109- char * result_str = allocString (sSize + 1 );
2110- char * tmp = result_str ;
2139+ auto & resultStr = StringData::alloc (sSize );
2140+ auto * tmp = resultStr. data () ;
21112141 for (const auto & part : strings) {
2112- memcpy (tmp, part->data (), part->size ());
2142+ std:: memcpy (tmp, part->data (), part->size ());
21132143 tmp += part->size ();
21142144 }
2115- *tmp = 0 ;
2116- v.mkStringMove (result_str , context);
2145+ *tmp = ' \0 ' ;
2146+ v.mkStringMove (resultStr , context);
21172147 }
21182148}
21192149
@@ -2288,7 +2318,7 @@ void copyContext(const Value & v, NixStringContext & context, const Experimental
22882318{
22892319 if (auto * ctx = v.context ())
22902320 for (auto * elem : *ctx)
2291- context.insert (NixStringContextElem::parse (elem, xpSettings));
2321+ context.insert (NixStringContextElem::parse (elem-> view () , xpSettings));
22922322}
22932323
22942324std::string_view EvalState::forceString (
0 commit comments