mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2025-02-16 15:17:18 +02:00
C API: Fix nix_c_primop_wrapper for strict initializers
https://github.com/NixOS/nix/pull/10555 added a check requiring that output parameters always have an uninitialized Value as argument. Unfortunately the output parameter of the primop callback received a thunk instead. See the comment for implementation considerations.
This commit is contained in:
parent
2c42e7b8d9
commit
a942a34469
2 changed files with 56 additions and 3 deletions
|
@ -73,10 +73,28 @@ 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, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v)
|
||||||
{
|
{
|
||||||
nix_c_context ctx;
|
nix_c_context ctx;
|
||||||
f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &v);
|
|
||||||
/* TODO: In the future, this should throw different errors depending on the error code */
|
// v currently has a thunk, but the C API initializers require an uninitialized value.
|
||||||
if (ctx.last_err_code != NIX_OK)
|
//
|
||||||
|
// We can't destroy the thunk, because that makes it impossible to retry,
|
||||||
|
// which is needed for tryEval and for evaluation drivers that evaluate more
|
||||||
|
// than one value (e.g. an attrset with two derivations, both of which
|
||||||
|
// reference v).
|
||||||
|
//
|
||||||
|
// Instead we create a temporary value, and then assign the result to v.
|
||||||
|
// This does not give the primop definition access to the thunk, but that's
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &vTmp);
|
||||||
|
|
||||||
|
if (ctx.last_err_code != NIX_OK) {
|
||||||
|
/* TODO: Throw different errors depending on the error code */
|
||||||
state.error<nix::EvalError>("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow();
|
state.error<nix::EvalError>("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
v = vTmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimOp * nix_alloc_primop(
|
PrimOp * nix_alloc_primop(
|
||||||
|
|
|
@ -191,4 +191,39 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context)
|
||||||
nix_realised_string_free(r);
|
nix_realised_string_free(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char * SAMPLE_USER_DATA = "whatever";
|
||||||
|
|
||||||
|
static void primop_square(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret)
|
||||||
|
{
|
||||||
|
assert(context);
|
||||||
|
assert(state);
|
||||||
|
assert(user_data == SAMPLE_USER_DATA);
|
||||||
|
auto i = nix_get_int(context, args[0]);
|
||||||
|
nix_init_int(context, ret, i * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_expr_primop)
|
||||||
|
{
|
||||||
|
PrimOp * primop =
|
||||||
|
nix_alloc_primop(ctx, primop_square, 1, "square", nullptr, "square an integer", (void *) SAMPLE_USER_DATA);
|
||||||
|
assert_ctx_ok();
|
||||||
|
Value * primopValue = nix_alloc_value(ctx, state);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_init_primop(ctx, primopValue, primop);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
Value * three = nix_alloc_value(ctx, state);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_init_int(ctx, three, 3);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
Value * result = nix_alloc_value(ctx, state);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_value_call(ctx, state, primopValue, three, result);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
auto r = nix_get_int(ctx, result);
|
||||||
|
ASSERT_EQ(9, r);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nixC
|
} // namespace nixC
|
||||||
|
|
Loading…
Add table
Reference in a new issue