mirror of
https://github.com/NixOS/nix
synced 2025-07-02 13:31:48 +02:00
Merge remote-tracking branch 'origin/master' into relative-flakes
This commit is contained in:
commit
8534c4222c
263 changed files with 2129 additions and 805 deletions
|
@ -129,7 +129,6 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
|||
try {
|
||||
auto colon = fn.rfind(':');
|
||||
if (colon == std::string::npos) fail();
|
||||
std::string filename(fn, 0, colon);
|
||||
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
|
||||
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
|
||||
} catch (std::invalid_argument & e) {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../build-utils-meson
|
|
@ -347,6 +347,16 @@ void EvalState::allowPath(const StorePath & storePath)
|
|||
rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath)));
|
||||
}
|
||||
|
||||
void EvalState::allowClosure(const StorePath & storePath)
|
||||
{
|
||||
if (!rootFS.dynamic_pointer_cast<AllowListSourceAccessor>()) return;
|
||||
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure(storePath, closure);
|
||||
for (auto & p : closure)
|
||||
allowPath(p);
|
||||
}
|
||||
|
||||
void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v)
|
||||
{
|
||||
allowPath(storePath);
|
||||
|
@ -3099,10 +3109,7 @@ std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Pat
|
|||
allowPath(path.path.abs());
|
||||
if (store->isInStore(path.path.abs())) {
|
||||
try {
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure(store->toStorePath(path.path.abs()).first, closure);
|
||||
for (auto & p : closure)
|
||||
allowPath(p);
|
||||
allowClosure(store->toStorePath(path.path.abs()).first);
|
||||
} catch (InvalidPath &) { }
|
||||
}
|
||||
}
|
||||
|
@ -3178,5 +3185,14 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) {
|
|||
return v.print(str);
|
||||
}
|
||||
|
||||
void forceNoNullByte(std::string_view s)
|
||||
{
|
||||
if (s.find('\0') != s.npos) {
|
||||
using namespace std::string_view_literals;
|
||||
auto str = replaceStrings(std::string(s), "\0"sv, "␀"sv);
|
||||
throw Error("input string '%s' cannot be represented as Nix string because it contains null bytes", str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -400,6 +400,11 @@ public:
|
|||
*/
|
||||
void allowPath(const StorePath & storePath);
|
||||
|
||||
/**
|
||||
* Allow access to the closure of a store path.
|
||||
*/
|
||||
void allowClosure(const StorePath & storePath);
|
||||
|
||||
/**
|
||||
* Allow access to a store path and return it as a string.
|
||||
*/
|
||||
|
|
|
@ -50,6 +50,7 @@ class JSONSax : nlohmann::json_sax<json> {
|
|||
public:
|
||||
void key(string_t & name, EvalState & state)
|
||||
{
|
||||
forceNoNullByte(name);
|
||||
attrs.insert_or_assign(state.symbols.create(name), &value(state));
|
||||
}
|
||||
};
|
||||
|
@ -122,6 +123,7 @@ public:
|
|||
|
||||
bool string(string_t & val) override
|
||||
{
|
||||
forceNoNullByte(val);
|
||||
rs->value(state).mkString(val);
|
||||
rs->add();
|
||||
return true;
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// inluding the generated headers twice leads to errors
|
||||
#ifndef BISON_HEADER
|
||||
# include "lexer-tab.hh"
|
||||
# include "parser-tab.hh"
|
||||
#endif
|
||||
|
||||
namespace nix::lexer::internal {
|
||||
|
||||
void initLoc(YYLTYPE * loc);
|
||||
|
|
|
@ -14,7 +14,7 @@ project('nix-expr', 'cpp',
|
|||
|
||||
cxx = meson.get_compiler('cpp')
|
||||
|
||||
subdir('build-utils-meson/deps-lists')
|
||||
subdir('nix-meson-build-support/deps-lists')
|
||||
|
||||
configdata = configuration_data()
|
||||
|
||||
|
@ -25,7 +25,7 @@ deps_public_maybe_subproject = [
|
|||
dependency('nix-store'),
|
||||
dependency('nix-fetchers'),
|
||||
]
|
||||
subdir('build-utils-meson/subprojects')
|
||||
subdir('nix-meson-build-support/subprojects')
|
||||
|
||||
boost = dependency(
|
||||
'boost',
|
||||
|
@ -77,7 +77,7 @@ add_project_arguments(
|
|||
language : 'cpp',
|
||||
)
|
||||
|
||||
subdir('build-utils-meson/common')
|
||||
subdir('nix-meson-build-support/common')
|
||||
|
||||
parser_tab = custom_target(
|
||||
input : 'parser.y',
|
||||
|
@ -121,7 +121,7 @@ lexer_tab = custom_target(
|
|||
install_dir : get_option('includedir') / 'nix',
|
||||
)
|
||||
|
||||
subdir('build-utils-meson/generate-header')
|
||||
subdir('nix-meson-build-support/generate-header')
|
||||
|
||||
generated_headers = []
|
||||
foreach header : [
|
||||
|
@ -205,4 +205,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
|
|||
|
||||
libraries_private = []
|
||||
|
||||
subdir('build-utils-meson/export')
|
||||
subdir('nix-meson-build-support/export')
|
||||
|
|
1
src/libexpr/nix-meson-build-support
Symbolic link
1
src/libexpr/nix-meson-build-support
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../nix-meson-build-support
|
|
@ -168,7 +168,7 @@ struct ExprVar : Expr
|
|||
the set stored in the environment that is `level` levels up
|
||||
from the current one.*/
|
||||
Level level;
|
||||
Displacement displ;
|
||||
Displacement displ = 0;
|
||||
|
||||
ExprVar(Symbol name) : name(name) { };
|
||||
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
|
||||
|
@ -242,7 +242,7 @@ struct ExprAttrs : Expr
|
|||
Kind kind;
|
||||
Expr * e;
|
||||
PosIdx pos;
|
||||
Displacement displ; // displacement
|
||||
Displacement displ = 0; // displacement
|
||||
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
|
||||
: kind(kind), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
|
|
|
@ -40,8 +40,8 @@ mkMesonLibrary (finalAttrs: {
|
|||
|
||||
workDir = ./.;
|
||||
fileset = fileset.unions [
|
||||
../../build-utils-meson
|
||||
./build-utils-meson
|
||||
../../nix-meson-build-support
|
||||
./nix-meson-build-support
|
||||
../../.version
|
||||
./.version
|
||||
./meson.build
|
||||
|
|
|
@ -88,6 +88,7 @@ struct ParserState
|
|||
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
||||
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
||||
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
|
||||
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
|
||||
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
|
||||
Expr * stripIndentation(const PosIdx pos,
|
||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
||||
|
@ -120,64 +121,29 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const
|
|||
// Checking attrPath validity.
|
||||
// ===========================
|
||||
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
|
||||
ExprAttrs * nested;
|
||||
if (i->symbol) {
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
|
||||
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||
if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
|
||||
attrs = attrs2;
|
||||
} else
|
||||
nested = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||
if (!nested) {
|
||||
attrPath.erase(i + 1, attrPath.end());
|
||||
dupAttr(attrPath, pos, j->second.pos);
|
||||
}
|
||||
} else {
|
||||
ExprAttrs * nested = new ExprAttrs;
|
||||
nested = new ExprAttrs;
|
||||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
||||
attrs = nested;
|
||||
}
|
||||
} else {
|
||||
ExprAttrs *nested = new ExprAttrs;
|
||||
nested = new ExprAttrs;
|
||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
|
||||
attrs = nested;
|
||||
}
|
||||
attrs = nested;
|
||||
}
|
||||
// Expr insertion.
|
||||
// ==========================
|
||||
if (i->symbol) {
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
// This attr path is already defined. However, if both
|
||||
// e and the expr pointed by the attr path are two attribute sets,
|
||||
// we want to merge them.
|
||||
// Otherwise, throw an error.
|
||||
auto ae = dynamic_cast<ExprAttrs *>(e);
|
||||
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||
if (jAttrs && ae) {
|
||||
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
|
||||
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
|
||||
for (auto & ad : ae->attrs) {
|
||||
auto j2 = jAttrs->attrs.find(ad.first);
|
||||
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
||||
dupAttr(ad.first, j2->second.pos, ad.second.pos);
|
||||
jAttrs->attrs.emplace(ad.first, ad.second);
|
||||
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
|
||||
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
|
||||
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
|
||||
from.displ += jAttrs->inheritFromExprs->size();
|
||||
}
|
||||
}
|
||||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
|
||||
if (ae->inheritFromExprs) {
|
||||
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
|
||||
ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
|
||||
}
|
||||
} else {
|
||||
dupAttr(attrPath, pos, j->second.pos);
|
||||
}
|
||||
} else {
|
||||
// This attr path is not defined. Let's create it.
|
||||
attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos));
|
||||
e->setName(i->symbol);
|
||||
}
|
||||
addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos));
|
||||
} else {
|
||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
|
||||
}
|
||||
|
@ -189,6 +155,60 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Precondition: attrPath is used for error messages and should already contain
|
||||
* symbol as its last element.
|
||||
*/
|
||||
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
|
||||
{
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
// This attr path is already defined. However, if both
|
||||
// e and the expr pointed by the attr path are two attribute sets,
|
||||
// we want to merge them.
|
||||
// Otherwise, throw an error.
|
||||
auto ae = dynamic_cast<ExprAttrs *>(def.e);
|
||||
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||
|
||||
// N.B. In a world in which we are less bound by our past mistakes, we
|
||||
// would also test that jAttrs and ae are not recursive. The effect of
|
||||
// not doing so is that any `rec` marker on ae is discarded, and any
|
||||
// `rec` marker on jAttrs will apply to the attributes in ae.
|
||||
// See https://github.com/NixOS/nix/issues/9020.
|
||||
if (jAttrs && ae) {
|
||||
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
|
||||
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
|
||||
for (auto & ad : ae->attrs) {
|
||||
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
|
||||
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
|
||||
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
|
||||
from.displ += jAttrs->inheritFromExprs->size();
|
||||
}
|
||||
attrPath.emplace_back(AttrName(ad.first));
|
||||
addAttr(jAttrs, attrPath, ad.first, std::move(ad.second));
|
||||
attrPath.pop_back();
|
||||
}
|
||||
ae->attrs.clear();
|
||||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(),
|
||||
std::make_move_iterator(ae->dynamicAttrs.begin()),
|
||||
std::make_move_iterator(ae->dynamicAttrs.end()));
|
||||
ae->dynamicAttrs.clear();
|
||||
if (ae->inheritFromExprs) {
|
||||
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
|
||||
std::make_move_iterator(ae->inheritFromExprs->begin()),
|
||||
std::make_move_iterator(ae->inheritFromExprs->end()));
|
||||
ae->inheritFromExprs = nullptr;
|
||||
}
|
||||
} else {
|
||||
dupAttr(attrPath, def.pos, j->second.pos);
|
||||
}
|
||||
} else {
|
||||
// This attr path is not defined. Let's create it.
|
||||
attrs->attrs.emplace(symbol, def);
|
||||
def.e->setName(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
|
||||
{
|
||||
std::sort(formals->formals.begin(), formals->formals.end(),
|
||||
|
|
|
@ -66,14 +66,12 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
|
|||
ensureValid(b.drvPath->getBaseStorePath());
|
||||
},
|
||||
[&](const NixStringContextElem::Opaque & o) {
|
||||
auto ctxS = store->printStorePath(o.path);
|
||||
ensureValid(o.path);
|
||||
if (maybePathsOut)
|
||||
maybePathsOut->emplace(o.path);
|
||||
},
|
||||
[&](const NixStringContextElem::DrvDeep & d) {
|
||||
/* Treat same as Opaque */
|
||||
auto ctxS = store->printStorePath(d.drvPath);
|
||||
ensureValid(d.drvPath);
|
||||
if (maybePathsOut)
|
||||
maybePathsOut->emplace(d.drvPath);
|
||||
|
@ -121,11 +119,9 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
|
|||
if (store != buildStore) copyClosure(*buildStore, *store, outputsToCopyAndAllow);
|
||||
|
||||
if (isIFD) {
|
||||
for (auto & outputPath : outputsToCopyAndAllow) {
|
||||
/* Add the output of this derivations to the allowed
|
||||
paths. */
|
||||
allowPath(outputPath);
|
||||
}
|
||||
/* Allow access to the output closures of this derivation. */
|
||||
for (auto & outputPath : outputsToCopyAndAllow)
|
||||
allowClosure(outputPath);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -1102,7 +1098,7 @@ static RegisterPrimOp primop_warn({
|
|||
.name = "__warn",
|
||||
.args = {"e1", "e2"},
|
||||
.doc = R"(
|
||||
Evaluate *e1*, which must be a string and print iton standard error as a warning.
|
||||
Evaluate *e1*, which must be a string, and print it on standard error as a warning.
|
||||
Then return *e2*.
|
||||
This function is useful for non-critical situations where attention is advisable.
|
||||
|
||||
|
@ -3261,23 +3257,19 @@ static RegisterPrimOp primop_isList({
|
|||
.fun = prim_isList,
|
||||
});
|
||||
|
||||
static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Value & v)
|
||||
{
|
||||
state.forceList(list, pos, "while evaluating the first argument passed to builtins.elemAt");
|
||||
if (n < 0 || (unsigned int) n >= list.listSize())
|
||||
state.error<EvalError>(
|
||||
"list index %1% is out of bounds",
|
||||
n
|
||||
).atPos(pos).debugThrow();
|
||||
state.forceValue(*list.listElems()[n], pos);
|
||||
v = *list.listElems()[n];
|
||||
}
|
||||
|
||||
/* Return the n-1'th element of a list. */
|
||||
static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
NixInt::Inner elem = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.elemAt").value;
|
||||
elemAt(state, pos, *args[0], elem, v);
|
||||
NixInt::Inner n = state.forceInt(*args[1], pos, "while evaluating the second argument passed to 'builtins.elemAt'").value;
|
||||
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.elemAt'");
|
||||
if (n < 0 || (unsigned int) n >= args[0]->listSize())
|
||||
state.error<EvalError>(
|
||||
"'builtins.elemAt' called with index %d on a list of size %d",
|
||||
n,
|
||||
args[0]->listSize()
|
||||
).atPos(pos).debugThrow();
|
||||
state.forceValue(*args[0]->listElems()[n], pos);
|
||||
v = *args[0]->listElems()[n];
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_elemAt({
|
||||
|
@ -3293,7 +3285,13 @@ static RegisterPrimOp primop_elemAt({
|
|||
/* Return the first element of a list. */
|
||||
static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
elemAt(state, pos, *args[0], 0, v);
|
||||
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.head'");
|
||||
if (args[0]->listSize() == 0)
|
||||
state.error<EvalError>(
|
||||
"'builtins.head' called on an empty list"
|
||||
).atPos(pos).debugThrow();
|
||||
state.forceValue(*args[0]->listElems()[0], pos);
|
||||
v = *args[0]->listElems()[0];
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_head({
|
||||
|
@ -3312,9 +3310,9 @@ static RegisterPrimOp primop_head({
|
|||
don't want to use it! */
|
||||
static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.tail");
|
||||
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.tail'");
|
||||
if (args[0]->listSize() == 0)
|
||||
state.error<EvalError>("'tail' called on an empty list").atPos(pos).debugThrow();
|
||||
state.error<EvalError>("'builtins.tail' called on an empty list").atPos(pos).debugThrow();
|
||||
|
||||
auto list = state.buildList(args[0]->listSize() - 1);
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
|
@ -4385,7 +4383,7 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
|||
|
||||
for (auto i = begin; i != end; ++i) {
|
||||
assert(idx <= 2 * len + 1 - 3);
|
||||
auto match = *i;
|
||||
const auto & match = *i;
|
||||
|
||||
// Add a string for non-matched characters.
|
||||
list[idx++] = mkString(state, match.prefix());
|
||||
|
|
|
@ -132,6 +132,8 @@ static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, V
|
|||
},
|
||||
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
|
||||
/* Reuse original item because we want this to be idempotent. */
|
||||
/* FIXME: Suspicious move out of const. This is actually a copy, so the comment
|
||||
above does not make much sense. */
|
||||
return std::move(c);
|
||||
},
|
||||
}, context.begin()->raw) }),
|
||||
|
|
|
@ -40,7 +40,7 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
|
|||
});
|
||||
}
|
||||
|
||||
auto toPath = *toPathMaybe;
|
||||
const auto & toPath = *toPathMaybe;
|
||||
|
||||
// check and return
|
||||
|
||||
|
|
|
@ -28,8 +28,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
|
||||
auto attrs = state.buildBindings(size);
|
||||
|
||||
for(auto & elem : table)
|
||||
for(auto & elem : table) {
|
||||
forceNoNullByte(elem.first);
|
||||
visit(attrs.alloc(elem.first), elem.second);
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
@ -54,7 +56,11 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
v.mkFloat(toml::get<NixFloat>(t));
|
||||
break;;
|
||||
case toml::value_t::string:
|
||||
v.mkString(toml::get<std::string>(t));
|
||||
{
|
||||
auto s = toml::get<std::string_view>(t);
|
||||
forceNoNullByte(s);
|
||||
v.mkString(s);
|
||||
}
|
||||
break;;
|
||||
case toml::value_t::local_datetime:
|
||||
case toml::value_t::offset_datetime:
|
||||
|
@ -66,7 +72,9 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
attrs.alloc("_type").mkString("timestamp");
|
||||
std::ostringstream s;
|
||||
s << t;
|
||||
attrs.alloc("value").mkString(toView(s));
|
||||
auto str = toView(s);
|
||||
forceNoNullByte(str);
|
||||
attrs.alloc("value").mkString(str);
|
||||
v.mkAttrs(attrs);
|
||||
} else {
|
||||
throw std::runtime_error("Dates and times are not supported");
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
@ -510,4 +510,6 @@ typedef std::shared_ptr<Value *> RootValue;
|
|||
|
||||
RootValue allocRootValue(Value * v);
|
||||
|
||||
void forceNoNullByte(std::string_view s);
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue