Compare commits
2 Commits
42d4188ebc
...
af04dd9f58
| Author | SHA1 | Date | |
|---|---|---|---|
|
af04dd9f58
|
|||
|
42f8eef0a8
|
@@ -207,6 +207,12 @@ pub struct RealisedString {
|
||||
#[clippy::has_significant_drop]
|
||||
struct EvalStateRef {
|
||||
eval_state: NonNull<raw::EvalState>,
|
||||
/// Whether we should call [`raw::state_free`] once this `EvalStateRef` is [`Drop`]ped.
|
||||
///
|
||||
/// The reason this exists is because we might construct non-owning [`EvalState`]s by way of
|
||||
/// using [`EvalState::from_raw`], in which case we do not want to call [`raw::state_free`] once
|
||||
/// the constructed [`EvalState`] goes out of scope.
|
||||
should_free: bool,
|
||||
}
|
||||
|
||||
unsafe impl Send for EvalStateRef {}
|
||||
@@ -224,8 +230,10 @@ impl EvalStateRef {
|
||||
|
||||
impl Drop for EvalStateRef {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
raw::state_free(self.eval_state.as_ptr());
|
||||
if self.should_free {
|
||||
unsafe {
|
||||
raw::state_free(self.eval_state.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,8 +357,8 @@ impl EvalStateBuilder {
|
||||
eval_state: NonNull::new(eval_state).unwrap_or_else(|| {
|
||||
panic!("nix_state_create returned a null pointer without an error")
|
||||
}),
|
||||
should_free: true,
|
||||
},
|
||||
store: self.store.clone(),
|
||||
})
|
||||
}
|
||||
/// Returns a raw pointer to the underlying eval state builder.
|
||||
@@ -375,7 +383,6 @@ impl EvalStateBuilder {
|
||||
#[clippy::has_significant_drop]
|
||||
pub struct EvalState {
|
||||
eval_state: EvalStateRef,
|
||||
store: Store,
|
||||
}
|
||||
|
||||
unsafe impl Send for EvalState {}
|
||||
@@ -391,6 +398,16 @@ impl EvalState {
|
||||
.build()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn from_raw(ptr: *mut raw::EvalState) -> Self {
|
||||
Self {
|
||||
eval_state: EvalStateRef {
|
||||
eval_state: NonNull::new(ptr)
|
||||
.expect("tried to construct a new `EvalState` from null pointer"),
|
||||
should_free: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the raw Nix C API EvalState.
|
||||
///
|
||||
/// # Safety
|
||||
@@ -400,11 +417,6 @@ impl EvalState {
|
||||
self.eval_state.as_ptr()
|
||||
}
|
||||
|
||||
/// Returns a reference to the Store that's used for instantiation, import from derivation, etc.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
/// Parses and evaluates a Nix expression `expr`.
|
||||
///
|
||||
/// Expressions can contain relative paths such as `./.` that are resolved relative to the given `path`.
|
||||
@@ -885,7 +897,6 @@ impl EvalState {
|
||||
err: e,
|
||||
})?;
|
||||
let primop = primop::PrimOp::new(
|
||||
self,
|
||||
primop::PrimOpMeta {
|
||||
// name is observable in stack traces, ie if the thunk returns Err
|
||||
name: name.as_c_str(),
|
||||
@@ -897,7 +908,7 @@ impl EvalState {
|
||||
Box::new(move |eval_state, _dummy: &[Value; 1]| f(eval_state)),
|
||||
)?;
|
||||
|
||||
let p = primop.new_value()?;
|
||||
let p = primop.new_value(self)?;
|
||||
self.new_value_apply(&p, &p)
|
||||
}
|
||||
|
||||
@@ -1106,24 +1117,6 @@ impl EvalState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [function][`ValueType::Function`] Nix value implemented by a Rust function.
|
||||
///
|
||||
/// This is also known as a "primop" in Nix, short for primitive operation.
|
||||
/// Most of the `builtins.*` values are examples of primops, but this function
|
||||
/// does not affect `builtins`.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This function is deprecated and has been replaced by
|
||||
/// [`PrimOp::new_value`](crate::primop::PrimOp::new_value).
|
||||
#[doc(alias = "make_primop")]
|
||||
#[doc(alias = "create_function")]
|
||||
#[doc(alias = "builtin")]
|
||||
#[deprecated = "use `PrimOp::new_value` instead"]
|
||||
pub fn new_value_primop(primop: primop::PrimOp) -> Result<Value> {
|
||||
primop.new_value()
|
||||
}
|
||||
|
||||
/// Creates a new [attribute set][`ValueType::AttrSet`] Nix value from an iterator of name-value pairs.
|
||||
///
|
||||
/// Accepts any iterator that yields `(String, Value)` pairs and has an exact size.
|
||||
@@ -2294,7 +2287,6 @@ mod tests {
|
||||
let bias_control = bias.clone();
|
||||
|
||||
let primop = primop::PrimOp::new(
|
||||
&mut es,
|
||||
primop::PrimOpMeta {
|
||||
name: cstr!("testFunction"),
|
||||
args: [cstr!("a"), cstr!("b")],
|
||||
@@ -2309,7 +2301,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let f = primop.new_value().unwrap();
|
||||
let f = primop.new_value(&mut es).unwrap();
|
||||
|
||||
{
|
||||
*bias_control.lock().unwrap() = 10;
|
||||
@@ -2335,7 +2327,6 @@ mod tests {
|
||||
let f = {
|
||||
let es: &mut EvalState = &mut es;
|
||||
let prim = primop::PrimOp::new(
|
||||
es,
|
||||
primop::PrimOpMeta {
|
||||
name: cstr!("throwingTestFunction"),
|
||||
args: [cstr!("arg")],
|
||||
@@ -2350,7 +2341,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
prim.new_value()
|
||||
prim.new_value(es)
|
||||
}
|
||||
.unwrap();
|
||||
let a = es.new_value_int(2).unwrap();
|
||||
@@ -2434,7 +2425,6 @@ mod tests {
|
||||
let store = Store::open(None, []).unwrap();
|
||||
let mut es = EvalState::new(store, []).unwrap();
|
||||
let primop = primop::PrimOp::new(
|
||||
&mut es,
|
||||
primop::PrimOpMeta {
|
||||
name: cstr!("frobnicate"),
|
||||
doc: cstr!("Frobnicates widgets"),
|
||||
@@ -2447,7 +2437,7 @@ mod tests {
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
let f = primop.new_value().unwrap();
|
||||
let f = primop.new_value(&mut es).unwrap();
|
||||
let a = es.new_value_int(2).unwrap();
|
||||
let b = es.new_value_int(3).unwrap();
|
||||
let fa = es.call(f, a).unwrap();
|
||||
@@ -2467,7 +2457,6 @@ mod tests {
|
||||
let store = Store::open(None, []).unwrap();
|
||||
let mut es = EvalState::new(store, []).unwrap();
|
||||
let primop = primop::PrimOp::new(
|
||||
&mut es,
|
||||
primop::PrimOpMeta {
|
||||
name: cstr!("frobnicate"),
|
||||
doc: cstr!("Frobnicates widgets"),
|
||||
@@ -2480,7 +2469,7 @@ mod tests {
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
let f = primop.new_value().unwrap();
|
||||
let f = primop.new_value(&mut es).unwrap();
|
||||
let a = es.new_value_int(0).unwrap();
|
||||
match es.call(f, a) {
|
||||
Ok(_) => panic!("expected an error"),
|
||||
|
||||
@@ -28,12 +28,11 @@ pub struct PrimOpMeta<'a, const N: usize> {
|
||||
pub args: [&'a CStr; N],
|
||||
}
|
||||
|
||||
pub struct PrimOp<'a> {
|
||||
pub struct PrimOp {
|
||||
ptr: *mut raw::PrimOp,
|
||||
eval_state: &'a mut EvalState,
|
||||
}
|
||||
|
||||
impl Drop for PrimOp<'_> {
|
||||
impl Drop for PrimOp {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
raw::gc_decref(null_mut(), self.ptr as *mut c_void);
|
||||
@@ -41,17 +40,16 @@ impl Drop for PrimOp<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PrimOp<'a> {
|
||||
impl PrimOp {
|
||||
/// Create a new primop with the given metadata and implementation.
|
||||
///
|
||||
/// When `f` returns an `Err`, the error is propagated to the Nix evaluator.
|
||||
/// To return a [recoverable error](RecoverableError), include it in the
|
||||
/// error chain (e.g. `Err(RecoverableError::new("...").into())`).
|
||||
pub fn new<const N: usize>(
|
||||
eval_state: &'a mut EvalState,
|
||||
meta: PrimOpMeta<N>,
|
||||
f: Box<dyn Fn(&mut EvalState, &[Value; N]) -> Result<Value, Box<dyn Error>>>,
|
||||
) -> Result<PrimOp<'a>, EvalStateError> {
|
||||
) -> Result<PrimOp, EvalStateError> {
|
||||
assert!(N != 0);
|
||||
|
||||
let mut args = Vec::new();
|
||||
@@ -68,7 +66,6 @@ impl<'a> PrimOp<'a> {
|
||||
let user_data = Box::leak(Box::new(PrimOpContext {
|
||||
arity: N,
|
||||
function: Box::new(move |eval_state, args| f(eval_state, args.try_into().unwrap())),
|
||||
eval_state,
|
||||
}));
|
||||
user_data as *const PrimOpContext as *mut c_void
|
||||
};
|
||||
@@ -85,10 +82,7 @@ impl<'a> PrimOp<'a> {
|
||||
))?
|
||||
};
|
||||
|
||||
Ok(PrimOp {
|
||||
ptr: op,
|
||||
eval_state,
|
||||
})
|
||||
Ok(PrimOp { ptr: op })
|
||||
}
|
||||
|
||||
/// Creates a new [`function`](crate::value::ValueType::Function) Nix value implemented by a Rust function.
|
||||
@@ -98,37 +92,40 @@ impl<'a> PrimOp<'a> {
|
||||
/// does not affect `builtins`.
|
||||
#[doc(alias = "make_primop")]
|
||||
#[doc(alias = "create_function")]
|
||||
#[doc(alias = "builtin")]
|
||||
pub fn new_value(mut self) -> Result<Value, EvalStateError> {
|
||||
self.with_state_and_ptr(|ptr, this| {
|
||||
let value = this.new_value_uninitialized()?;
|
||||
let mut ctx = Context::new();
|
||||
unsafe {
|
||||
check_call!(raw::init_primop(&mut ctx, value.raw_ptr(), ptr))?;
|
||||
};
|
||||
Ok(value)
|
||||
})
|
||||
pub fn new_value(self, eval_state: &mut EvalState) -> Result<Value, EvalStateError> {
|
||||
let value = eval_state.new_value_uninitialized()?;
|
||||
let mut ctx = Context::new();
|
||||
unsafe {
|
||||
check_call!(raw::init_primop(&mut ctx, value.raw_ptr(), self.ptr))?;
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub(crate) fn with_state_and_ptr<F, T>(&mut self, f: F) -> T
|
||||
where
|
||||
F: Fn(*mut raw::PrimOp, &mut EvalState) -> T,
|
||||
{
|
||||
f(self.ptr, self.eval_state)
|
||||
/// Creates a new [`function`](crate::value::ValueType::Function) Nix value implemented by a
|
||||
/// Rust function, and adds it to the global `builtins` attribute set.
|
||||
///
|
||||
/// Note that this `PrimOp` will only be accessible by [`EvalState`]s that are created _after_
|
||||
/// this is called.
|
||||
#[doc(alias = "builtin")]
|
||||
pub fn new_builtin(self) -> Result<(), EvalStateError> {
|
||||
let mut ctx = Context::new();
|
||||
unsafe {
|
||||
check_call!(raw::register_primop(&mut ctx, self.ptr))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The user_data for our Nix primops
|
||||
struct PrimOpContext<'a> {
|
||||
struct PrimOpContext {
|
||||
arity: usize,
|
||||
function: Box<dyn Fn(&mut EvalState, &[Value]) -> Result<Value, Box<dyn Error>>>,
|
||||
eval_state: &'a mut EvalState,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn function_adapter(
|
||||
user_data: *mut ::std::os::raw::c_void,
|
||||
context_out: *mut raw_util::c_context,
|
||||
_state: *mut raw::EvalState,
|
||||
state: *mut raw::EvalState,
|
||||
args: *mut *mut raw::Value,
|
||||
ret: *mut raw::Value,
|
||||
) {
|
||||
@@ -140,7 +137,7 @@ unsafe extern "C" fn function_adapter(
|
||||
.collect();
|
||||
let args_slice = args_vec.as_slice();
|
||||
|
||||
let r = primop_info.function.as_ref()(primop_info.eval_state, args_slice);
|
||||
let r = primop_info.function.as_ref()(&mut unsafe { EvalState::from_raw(state) }, args_slice);
|
||||
|
||||
match r {
|
||||
Ok(v) => unsafe {
|
||||
|
||||
Reference in New Issue
Block a user