fix: Require mutable Context, as it should be

This spreads out transitively to many places and requires that
we use `check_call!` instead of `check_one_call` in a number of
places.

(cherry picked from commit 6bc31dcf206518a7be7f0ac6e773d5dfe25531ea)
This commit is contained in:
Robert Hensing 2024-06-15 12:40:45 +02:00
parent 226639939f
commit a6dbf17778
5 changed files with 143 additions and 129 deletions

View file

@ -6,8 +6,8 @@ use nix_c_raw as raw;
use nix_store::path::StorePath; use nix_store::path::StorePath;
use nix_store::store::Store; use nix_store::store::Store;
use nix_util::context::Context; use nix_util::context::Context;
use nix_util::result_string_init;
use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data}; use nix_util::string_return::{callback_get_result_string, callback_get_result_string_data};
use nix_util::{check_call, check_call_opt_key, result_string_init};
use std::ffi::{c_char, CString}; use std::ffi::{c_char, CString};
use std::os::raw::c_uint; use std::os::raw::c_uint;
use std::ptr::{null, null_mut, NonNull}; use std::ptr::{null, null_mut, NonNull};
@ -45,7 +45,7 @@ pub struct EvalState {
} }
impl EvalState { impl EvalState {
pub fn new<'a>(store: Store, lookup_path: impl IntoIterator<Item = &'a str>) -> Result<Self> { pub fn new<'a>(store: Store, lookup_path: impl IntoIterator<Item = &'a str>) -> Result<Self> {
let context = Context::new(); let mut context = Context::new();
// this intermediate value must be here and must not be moved // this intermediate value must be here and must not be moved
// because it owns the data the `*const c_char` pointers point to. // because it owns the data the `*const c_char` pointers point to.
@ -96,49 +96,43 @@ impl EvalState {
/// use nix_expr::value::Value; /// use nix_expr::value::Value;
/// ///
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let es = EvalState::new(Store::open("auto", [])?, [])?; /// # let mut es = EvalState::new(Store::open("auto", [])?, [])?;
/// let v: Value = es.eval_from_string("42", ".")?; /// let v: Value = es.eval_from_string("42", ".")?;
/// assert_eq!(es.require_int(&v)?, 42); /// assert_eq!(es.require_int(&v)?, 42);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[doc(alias = "nix_expr_eval_from_string")] #[doc(alias = "nix_expr_eval_from_string")]
pub fn eval_from_string(&self, expr: &str, path: &str) -> Result<Value> { pub fn eval_from_string(&mut self, expr: &str, path: &str) -> Result<Value> {
let expr_ptr = let expr_ptr =
CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?; CString::new(expr).with_context(|| "eval_from_string: expr contains null byte")?;
let path_ptr = let path_ptr =
CString::new(path).with_context(|| "eval_from_string: path contains null byte")?; CString::new(path).with_context(|| "eval_from_string: path contains null byte")?;
unsafe { unsafe {
let value = self.new_value_uninitialized()?; let value = self.new_value_uninitialized()?;
self.context.check_one_call(|ctx_ptr| { check_call!(raw::expr_eval_from_string[
raw::expr_eval_from_string( self.context,
ctx_ptr, self.raw_ptr(),
self.raw_ptr(), expr_ptr.as_ptr(),
expr_ptr.as_ptr(), path_ptr.as_ptr(),
path_ptr.as_ptr(), value.raw_ptr()
value.raw_ptr(), ])?;
)
})?;
Ok(value) Ok(value)
} }
} }
/// Try turn any Value into a Value that isn't a Thunk. /// Try turn any Value into a Value that isn't a Thunk.
pub fn force(&self, v: &Value) -> Result<()> { pub fn force(&mut self, v: &Value) -> Result<()> {
unsafe { unsafe { check_call!(raw::value_force[self.context, self.raw_ptr(), v.raw_ptr()]) }?;
self.context.check_one_call(|ctx_ptr| {
raw::value_force(ctx_ptr, self.raw_ptr(), v.raw_ptr());
})?;
}
Ok(()) Ok(())
} }
pub fn value_type_unforced(&self, value: &Value) -> Option<ValueType> { pub fn value_type_unforced(&mut self, value: &Value) -> Option<ValueType> {
let r = self let r = self
.context .context
.check_one_call(|ctx_ptr| unsafe { raw::get_type(ctx_ptr, value.raw_ptr()) }); .check_one_call(|ctx_ptr| unsafe { raw::get_type(ctx_ptr, value.raw_ptr()) });
// .unwrap(): no reason for this to fail, as it does not evaluate // .unwrap(): no reason for this to fail, as it does not evaluate
ValueType::from_raw(r.unwrap()) ValueType::from_raw(r.unwrap())
} }
pub fn value_type(&self, value: &Value) -> Result<ValueType> { pub fn value_type(&mut self, value: &Value) -> Result<ValueType> {
match self.value_type_unforced(value) { match self.value_type_unforced(value) {
Some(a) => Ok(a), Some(a) => Ok(a),
None => { None => {
@ -152,7 +146,7 @@ impl EvalState {
} }
} }
} }
pub fn require_int(&self, v: &Value) -> Result<Int> { pub fn require_int(&mut self, v: &Value) -> Result<Int> {
let t = self.value_type(v)?; let t = self.value_type(v)?;
if t != ValueType::Int { if t != ValueType::Int {
bail!("expected an int, but got a {:?}", t); bail!("expected an int, but got a {:?}", t);
@ -162,7 +156,7 @@ impl EvalState {
} }
/// Evaluate, and require that the value is an attrset. /// Evaluate, and require that the value is an attrset.
/// Returns a list of the keys in the attrset. /// Returns a list of the keys in the attrset.
pub fn require_attrs_names(&self, v: &Value) -> Result<Vec<String>> { pub fn require_attrs_names(&mut self, v: &Value) -> Result<Vec<String>> {
let t = self.value_type(v)?; let t = self.value_type(v)?;
if t != ValueType::AttrSet { if t != ValueType::AttrSet {
bail!("expected an attrset, but got a {:?}", t); bail!("expected an attrset, but got a {:?}", t);
@ -172,9 +166,14 @@ impl EvalState {
})?; })?;
let mut attrs = Vec::with_capacity(n); let mut attrs = Vec::with_capacity(n);
for i in 0..n { for i in 0..n {
let cstr_ptr: *const i8 = self.context.check_one_call(|ctx_ptr| unsafe { let cstr_ptr: *const i8 = unsafe {
raw::get_attr_name_byidx(ctx_ptr, v.raw_ptr(), self.raw_ptr(), i as c_uint) check_call!(raw::get_attr_name_byidx[
})?; self.context,
v.raw_ptr(),
self.raw_ptr(),
i as c_uint
])
}?;
let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) }; let cstr = unsafe { std::ffi::CStr::from_ptr(cstr_ptr) };
let s = cstr let s = cstr
.to_str() .to_str()
@ -185,16 +184,21 @@ impl EvalState {
} }
/// Evaluate, require that the value is an attrset, and select an attribute by name. /// Evaluate, require that the value is an attrset, and select an attribute by name.
pub fn require_attrs_select(&self, v: &Value, attr_name: &str) -> Result<Value> { pub fn require_attrs_select(&mut self, v: &Value, attr_name: &str) -> Result<Value> {
let t = self.value_type(v)?; let t = self.value_type(v)?;
if t != ValueType::AttrSet { if t != ValueType::AttrSet {
bail!("expected an attrset, but got a {:?}", t); bail!("expected an attrset, but got a {:?}", t);
} }
let attr_name = CString::new(attr_name) let attr_name = CString::new(attr_name)
.with_context(|| "require_attrs_select: attrName contains null byte")?; .with_context(|| "require_attrs_select: attrName contains null byte")?;
let v2 = self.context.check_one_call(|ctx_ptr| unsafe { let v2 = unsafe {
raw::get_attr_byname(ctx_ptr, v.raw_ptr(), self.raw_ptr(), attr_name.as_ptr()) check_call!(raw::get_attr_byname[
})?; self.context,
v.raw_ptr(),
self.raw_ptr(),
attr_name.as_ptr()
])
}?;
Ok(Value::new(v2)) Ok(Value::new(v2))
} }
@ -205,22 +209,31 @@ impl EvalState {
/// Return `Ok(None)` if the attribute is not present. /// Return `Ok(None)` if the attribute is not present.
/// ///
/// Return `Ok(Some(value))` if the attribute is present. /// Return `Ok(Some(value))` if the attribute is present.
pub fn require_attrs_select_opt(&self, v: &Value, attr_name: &str) -> Result<Option<Value>> { pub fn require_attrs_select_opt(
&mut self,
v: &Value,
attr_name: &str,
) -> Result<Option<Value>> {
let t = self.value_type(v)?; let t = self.value_type(v)?;
if t != ValueType::AttrSet { if t != ValueType::AttrSet {
bail!("expected an attrset, but got a {:?}", t); bail!("expected an attrset, but got a {:?}", t);
} }
let attr_name = CString::new(attr_name) let attr_name = CString::new(attr_name)
.with_context(|| "require_attrs_select_opt: attrName contains null byte")?; .with_context(|| "require_attrs_select_opt: attrName contains null byte")?;
let v2 = self.context.check_one_call_or_key_none(|ctx_ptr| unsafe { let v2 = unsafe {
raw::get_attr_byname(ctx_ptr, v.raw_ptr(), self.raw_ptr(), attr_name.as_ptr()) check_call_opt_key!(raw::get_attr_byname[
})?; self.context,
v.raw_ptr(),
self.raw_ptr(),
attr_name.as_ptr()
])
}?;
Ok(v2.map(Value::new)) Ok(v2.map(Value::new))
} }
/// Create a new value containing the passed string. /// Create a new value containing the passed string.
/// Returns a string value without any string context. /// Returns a string value without any string context.
pub fn new_value_str(&self, s: &str) -> Result<Value> { pub fn new_value_str(&mut self, s: &str) -> Result<Value> {
let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?; let s = CString::new(s).with_context(|| "new_value_str: contains null byte")?;
let v = unsafe { let v = unsafe {
let value = self.new_value_uninitialized()?; let value = self.new_value_uninitialized()?;
@ -231,7 +244,7 @@ impl EvalState {
Ok(v) Ok(v)
} }
pub fn new_value_int(&self, i: Int) -> Result<Value> { pub fn new_value_int(&mut self, i: Int) -> Result<Value> {
let v = unsafe { let v = unsafe {
let value = self.new_value_uninitialized()?; let value = self.new_value_uninitialized()?;
self.context self.context
@ -242,7 +255,7 @@ impl EvalState {
} }
/// Not exposed, because the caller must always explicitly handle the context or not accept one at all. /// Not exposed, because the caller must always explicitly handle the context or not accept one at all.
fn get_string(&self, value: &Value) -> Result<String> { fn get_string(&mut self, value: &Value) -> Result<String> {
let mut r = result_string_init!(); let mut r = result_string_init!();
unsafe { unsafe {
self.context.check_one_call(|ctx_ptr| { self.context.check_one_call(|ctx_ptr| {
@ -257,7 +270,7 @@ impl EvalState {
r r
} }
/// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty /// NOTE: this will be replaced by two methods, one that also returns the context, and one that checks that the context is empty
pub fn require_string(&self, value: &Value) -> Result<String> { pub fn require_string(&mut self, value: &Value) -> Result<String> {
let t = self.value_type(value)?; let t = self.value_type(value)?;
if t != ValueType::String { if t != ValueType::String {
bail!("expected a string, but got a {:?}", t); bail!("expected a string, but got a {:?}", t);
@ -265,7 +278,7 @@ impl EvalState {
self.get_string(value) self.get_string(value)
} }
pub fn realise_string( pub fn realise_string(
&self, &mut self,
value: &Value, value: &Value,
is_import_from_derivation: bool, is_import_from_derivation: bool,
) -> Result<RealisedString> { ) -> Result<RealisedString> {
@ -274,14 +287,14 @@ impl EvalState {
bail!("expected a string, but got a {:?}", t); bail!("expected a string, but got a {:?}", t);
} }
let rs = self.context.check_one_call(|ctx_ptr| unsafe { let rs = unsafe {
raw::string_realise( check_call!(raw::string_realise[
ctx_ptr, self.context,
self.raw_ptr(), self.raw_ptr(),
value.raw_ptr(), value.raw_ptr(),
is_import_from_derivation, is_import_from_derivation
) ])
})?; }?;
let s = unsafe { let s = unsafe {
let start = raw::realised_string_get_buffer_start(rs) as *const u8; let start = raw::realised_string_get_buffer_start(rs) as *const u8;
@ -312,37 +325,38 @@ impl EvalState {
/// Eagerly apply a function to an argument. /// Eagerly apply a function to an argument.
/// ///
/// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`]. /// For a lazy version, see [`new_value_apply`][`EvalState::new_value_apply`].
pub fn call(&self, f: Value, a: Value) -> Result<Value> { pub fn call(&mut self, f: Value, a: Value) -> Result<Value> {
let value = self.new_value_uninitialized()?;
unsafe { unsafe {
let value = self.new_value_uninitialized()?; check_call!(raw::value_call[
self.context.check_one_call(|ctx_ptr| { self.context,
raw::value_call( self.raw_ptr(),
ctx_ptr, f.raw_ptr(),
self.raw_ptr(), a.raw_ptr(),
f.raw_ptr(), value.raw_ptr()
a.raw_ptr(), ])
value.raw_ptr(), }?;
) Ok(value)
})?;
Ok(value)
}
} }
/// Apply a function to an argument, but don't evaluate the result just yet. /// Apply a function to an argument, but don't evaluate the result just yet.
/// ///
/// For an eager version, see [`call`][`EvalState::call`]. /// For an eager version, see [`call`][`EvalState::call`].
pub fn new_value_apply(&self, f: &Value, a: &Value) -> Result<Value> { pub fn new_value_apply(&mut self, f: &Value, a: &Value) -> Result<Value> {
let value = self.new_value_uninitialized()?; let value = self.new_value_uninitialized()?;
self.context.check_one_call(|ctx_ptr| unsafe { unsafe {
raw::init_apply(ctx_ptr, value.raw_ptr(), f.raw_ptr(), a.raw_ptr()); check_call!(raw::init_apply[
value self.context,
}) value.raw_ptr(),
f.raw_ptr(),
a.raw_ptr()
])
}?;
Ok(value)
} }
fn new_value_uninitialized(&self) -> Result<Value> { fn new_value_uninitialized(&mut self) -> Result<Value> {
let value = self let value = unsafe { check_call!(raw::alloc_value[self.context, self.raw_ptr()]) }?;
.context
.check_one_call(|ctx_ptr| unsafe { raw::alloc_value(ctx_ptr, self.raw_ptr()) })?;
Ok(Value::new(value)) Ok(Value::new(value))
} }
} }
@ -447,10 +461,10 @@ mod tests {
writeln!(test_file0, "{integer0}").unwrap(); writeln!(test_file0, "{integer0}").unwrap();
writeln!(test_file1, "{integer1}").unwrap(); writeln!(test_file1, "{integer1}").unwrap();
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let es = EvalState::new(Store::open("auto", HashMap::new()).unwrap(), []).unwrap(); let mut es = EvalState::new(Store::open("auto", HashMap::new()).unwrap(), []).unwrap();
assert!(es.eval_from_string(import_expression, "<test>").is_err()); assert!(es.eval_from_string(import_expression, "<test>").is_err());
let es = EvalState::new( let mut es = EvalState::new(
Store::open("auto", HashMap::new()).unwrap(), Store::open("auto", HashMap::new()).unwrap(),
[ [
format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(), format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(),
@ -458,9 +472,8 @@ mod tests {
], ],
) )
.unwrap(); .unwrap();
let v = es let ie = &es.eval_from_string(import_expression, "<test>").unwrap();
.require_int(&es.eval_from_string(import_expression, "<test>").unwrap()) let v = es.require_int(ie).unwrap();
.unwrap();
assert_eq!(v, integer0 + integer1); assert_eq!(v, integer0 + integer1);
}) })
.unwrap(); .unwrap();
@ -472,7 +485,7 @@ mod tests {
fn eval_state_eval_from_string() { fn eval_state_eval_from_string() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("1", "<test>").unwrap(); let v = es.eval_from_string("1", "<test>").unwrap();
let v2 = v.clone(); let v2 = v.clone();
es.force(&v).unwrap(); es.force(&v).unwrap();
@ -489,7 +502,7 @@ mod tests {
fn eval_state_value_bool() { fn eval_state_value_bool() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("true", "<test>").unwrap(); let v = es.eval_from_string("true", "<test>").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
@ -502,7 +515,7 @@ mod tests {
fn eval_state_value_int() { fn eval_state_value_int() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("1", "<test>").unwrap(); let v = es.eval_from_string("1", "<test>").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let t = es.value_type(&v).unwrap(); let t = es.value_type(&v).unwrap();
@ -517,7 +530,7 @@ mod tests {
fn eval_state_require_int_forces_thunk() { fn eval_state_require_int_forces_thunk() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let f = es.eval_from_string("x: x + 1", "<test>").unwrap(); let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
let a = es.eval_from_string("2", "<test>").unwrap(); let a = es.eval_from_string("2", "<test>").unwrap();
let v = es.new_value_apply(&f, &a).unwrap(); let v = es.new_value_apply(&f, &a).unwrap();
@ -530,7 +543,7 @@ mod tests {
} }
/// A helper that turns an expression into a thunk. /// A helper that turns an expression into a thunk.
fn make_thunk(es: &EvalState, expr: &str) -> Value { fn make_thunk(es: &mut EvalState, expr: &str) -> Value {
// This would be silly in real code, but it works for the current Nix implementation. // This would be silly in real code, but it works for the current Nix implementation.
// A Nix implementation that applies the identity function eagerly would be a valid // A Nix implementation that applies the identity function eagerly would be a valid
// Nix implementation, but annoying because we'll have to change this helper to do // Nix implementation, but annoying because we'll have to change this helper to do
@ -544,8 +557,8 @@ mod tests {
fn make_thunk_helper_works() { fn make_thunk_helper_works() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = make_thunk(&es, "1"); let v = make_thunk(&mut es, "1");
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
assert!(t == None); assert!(t == None);
}) })
@ -556,7 +569,7 @@ mod tests {
fn eval_state_value_attrs_names_empty() { fn eval_state_value_attrs_names_empty() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("{ }", "<test>").unwrap(); let v = es.eval_from_string("{ }", "<test>").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
@ -571,8 +584,8 @@ mod tests {
fn eval_state_require_attrs_names_forces_thunk() { fn eval_state_require_attrs_names_forces_thunk() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = make_thunk(&es, "{ a = 1; b = 2; }"); let v = make_thunk(&mut es, "{ a = 1; b = 2; }");
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
assert!(t == None); assert!(t == None);
let attrs = es.require_attrs_names(&v).unwrap(); let attrs = es.require_attrs_names(&v).unwrap();
@ -585,7 +598,7 @@ mod tests {
fn eval_state_require_attrs_names_bad_type() { fn eval_state_require_attrs_names_bad_type() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("1", "<test>").unwrap(); let v = es.eval_from_string("1", "<test>").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let r = es.require_attrs_names(&v); let r = es.require_attrs_names(&v);
@ -602,7 +615,7 @@ mod tests {
fn eval_state_value_attrs_names_example() { fn eval_state_value_attrs_names_example() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#; let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#;
let v = es.eval_from_string(expr, "<test>").unwrap(); let v = es.eval_from_string(expr, "<test>").unwrap();
let attrs = es.require_attrs_names(&v).unwrap(); let attrs = es.require_attrs_names(&v).unwrap();
@ -617,7 +630,7 @@ mod tests {
fn eval_state_require_attrs_select() { fn eval_state_require_attrs_select() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let expr = r#"{ a = "aye"; b = "bee"; }"#; let expr = r#"{ a = "aye"; b = "bee"; }"#;
let v = es.eval_from_string(expr, "<test>").unwrap(); let v = es.eval_from_string(expr, "<test>").unwrap();
let a = es.require_attrs_select(&v, "a").unwrap(); let a = es.require_attrs_select(&v, "a").unwrap();
@ -644,9 +657,9 @@ mod tests {
fn eval_state_require_attrs_select_forces_thunk() { fn eval_state_require_attrs_select_forces_thunk() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let expr = r#"{ a = "aye"; b = "bee"; }"#; let expr = r#"{ a = "aye"; b = "bee"; }"#;
let v = make_thunk(&es, expr); let v = make_thunk(&mut es, expr);
assert!(es.value_type_unforced(&v).is_none()); assert!(es.value_type_unforced(&v).is_none());
let r = es.require_attrs_select(&v, "a"); let r = es.require_attrs_select(&v, "a");
assert!(r.is_ok()); assert!(r.is_ok());
@ -658,7 +671,7 @@ mod tests {
fn eval_state_require_attrs_select_error() { fn eval_state_require_attrs_select_error() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let expr = r#"{ a = throw "oh no the error"; }"#; let expr = r#"{ a = throw "oh no the error"; }"#;
let v = es.eval_from_string(expr, "<test>").unwrap(); let v = es.eval_from_string(expr, "<test>").unwrap();
let r = es.require_attrs_select(&v, "a"); let r = es.require_attrs_select(&v, "a");
@ -679,7 +692,7 @@ mod tests {
fn eval_state_require_attrs_select_opt() { fn eval_state_require_attrs_select_opt() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let expr = r#"{ a = "aye"; b = "bee"; }"#; let expr = r#"{ a = "aye"; b = "bee"; }"#;
let v = es.eval_from_string(expr, "<test>").unwrap(); let v = es.eval_from_string(expr, "<test>").unwrap();
let a = es.require_attrs_select_opt(&v, "a").unwrap().unwrap(); let a = es.require_attrs_select_opt(&v, "a").unwrap().unwrap();
@ -696,9 +709,9 @@ mod tests {
fn eval_state_require_attrs_select_opt_forces_thunk() { fn eval_state_require_attrs_select_opt_forces_thunk() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let expr = r#"{ a = "aye"; b = "bee"; }"#; let expr = r#"{ a = "aye"; b = "bee"; }"#;
let v = make_thunk(&es, expr); let v = make_thunk(&mut es, expr);
assert!(es.value_type_unforced(&v).is_none()); assert!(es.value_type_unforced(&v).is_none());
let r = es.require_attrs_select_opt(&v, "a"); let r = es.require_attrs_select_opt(&v, "a");
assert!(r.is_ok()); assert!(r.is_ok());
@ -710,7 +723,7 @@ mod tests {
fn eval_state_require_attrs_select_opt_error() { fn eval_state_require_attrs_select_opt_error() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let expr = r#"{ a = throw "oh no the error"; }"#; let expr = r#"{ a = throw "oh no the error"; }"#;
let v = es.eval_from_string(expr, "<test>").unwrap(); let v = es.eval_from_string(expr, "<test>").unwrap();
let r = es.require_attrs_select_opt(&v, "a"); let r = es.require_attrs_select_opt(&v, "a");
@ -731,7 +744,7 @@ mod tests {
fn eval_state_value_string() { fn eval_state_value_string() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("\"hello\"", "<test>").unwrap(); let v = es.eval_from_string("\"hello\"", "<test>").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
@ -746,8 +759,8 @@ mod tests {
fn eval_state_value_string_forces_thunk() { fn eval_state_value_string_forces_thunk() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = make_thunk(&es, "\"hello\""); let v = make_thunk(&mut es, "\"hello\"");
assert!(es.value_type_unforced(&v).is_none()); assert!(es.value_type_unforced(&v).is_none());
let s = es.require_string(&v).unwrap(); let s = es.require_string(&v).unwrap();
assert!(s == "hello"); assert!(s == "hello");
@ -759,7 +772,7 @@ mod tests {
fn eval_state_value_string_unexpected_bool() { fn eval_state_value_string_unexpected_bool() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("true", "<test>").unwrap(); let v = es.eval_from_string("true", "<test>").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let r = es.require_string(&v); let r = es.require_string(&v);
@ -777,7 +790,7 @@ mod tests {
fn eval_state_value_string_unexpected_path_value() { fn eval_state_value_string_unexpected_path_value() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("/foo", "<test>").unwrap(); let v = es.eval_from_string("/foo", "<test>").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let r = es.require_string(&v); let r = es.require_string(&v);
@ -794,7 +807,7 @@ mod tests {
fn eval_state_value_string_bad_utf() { fn eval_state_value_string_bad_utf() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es let v = es
.eval_from_string("builtins.substring 0 1 \"ü\"", "<test>") .eval_from_string("builtins.substring 0 1 \"ü\"", "<test>")
.unwrap(); .unwrap();
@ -815,7 +828,7 @@ mod tests {
fn eval_state_value_string_unexpected_context() { fn eval_state_value_string_unexpected_context() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es let v = es
.eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "<test>") .eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "<test>")
.unwrap(); .unwrap();
@ -834,7 +847,7 @@ mod tests {
fn eval_state_new_string() { fn eval_state_new_string() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.new_value_str("hello").unwrap(); let v = es.new_value_str("hello").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
@ -849,7 +862,7 @@ mod tests {
fn eval_state_new_string_empty() { fn eval_state_new_string_empty() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.new_value_str("").unwrap(); let v = es.new_value_str("").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
@ -864,7 +877,7 @@ mod tests {
fn eval_state_new_string_invalid() { fn eval_state_new_string_invalid() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let r = es.new_value_str("hell\0no"); let r = es.new_value_str("hell\0no");
match r { match r {
Ok(_) => panic!("expected an error"), Ok(_) => panic!("expected an error"),
@ -883,7 +896,7 @@ mod tests {
fn eval_state_new_int() { fn eval_state_new_int() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.new_value_int(42).unwrap(); let v = es.new_value_int(42).unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
@ -898,7 +911,7 @@ mod tests {
fn eval_state_value_attrset() { fn eval_state_value_attrset() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es.eval_from_string("{ }", "<test>").unwrap(); let v = es.eval_from_string("{ }", "<test>").unwrap();
es.force(&v).unwrap(); es.force(&v).unwrap();
let t = es.value_type_unforced(&v); let t = es.value_type_unforced(&v);
@ -911,7 +924,7 @@ mod tests {
fn eval_state_value_list() { fn eval_state_value_list() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let v = es let v = es
.eval_from_string("[ ]", "<test>") .eval_from_string("[ ]", "<test>")
.unwrap(); .unwrap();
@ -926,7 +939,7 @@ mod tests {
fn eval_state_realise_string() { fn eval_state_realise_string() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let expr = r#" let expr = r#"
'' ''
a derivation output: ${ a derivation output: ${
@ -973,7 +986,7 @@ mod tests {
fn eval_state_call() { fn eval_state_call() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let f = es.eval_from_string("x: x + 1", "<test>").unwrap(); let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
let a = es.eval_from_string("2", "<test>").unwrap(); let a = es.eval_from_string("2", "<test>").unwrap();
let v = es.call(f, a).unwrap(); let v = es.call(f, a).unwrap();
@ -990,7 +1003,7 @@ mod tests {
fn eval_state_apply() { fn eval_state_apply() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
// This is a function that takes two arguments. // This is a function that takes two arguments.
let f = es.eval_from_string("x: x + 1", "<test>").unwrap(); let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
let a = es.eval_from_string("2", "<test>").unwrap(); let a = es.eval_from_string("2", "<test>").unwrap();
@ -1009,7 +1022,7 @@ mod tests {
fn eval_state_call_fail_body() { fn eval_state_call_fail_body() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let f = es.eval_from_string("x: x + 1", "<test>").unwrap(); let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
let a = es.eval_from_string("true", "<test>").unwrap(); let a = es.eval_from_string("true", "<test>").unwrap();
let r = es.call(f, a); let r = es.call(f, a);
@ -1030,7 +1043,7 @@ mod tests {
fn eval_state_apply_fail_body() { fn eval_state_apply_fail_body() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let f = es.eval_from_string("x: x + 1", "<test>").unwrap(); let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
let a = es.eval_from_string("true", "<test>").unwrap(); let a = es.eval_from_string("true", "<test>").unwrap();
// Lazy => no error // Lazy => no error
@ -1055,7 +1068,7 @@ mod tests {
fn eval_state_call_fail_args() { fn eval_state_call_fail_args() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap(); let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
let a = es.eval_from_string("{}", "<test>").unwrap(); let a = es.eval_from_string("{}", "<test>").unwrap();
let r = es.call(f, a); let r = es.call(f, a);
@ -1077,7 +1090,7 @@ mod tests {
fn eval_state_apply_fail_args_lazy() { fn eval_state_apply_fail_args_lazy() {
gc_registering_current_thread(|| { gc_registering_current_thread(|| {
let store = Store::open("auto", HashMap::new()).unwrap(); let store = Store::open("auto", HashMap::new()).unwrap();
let es = EvalState::new(store, []).unwrap(); let mut es = EvalState::new(store, []).unwrap();
let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap(); let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
let a = es.eval_from_string("{}", "<test>").unwrap(); let a = es.eval_from_string("{}", "<test>").unwrap();
// Lazy => no error // Lazy => no error
@ -1107,7 +1120,7 @@ mod tests {
let log = tempfile::tempdir().unwrap(); let log = tempfile::tempdir().unwrap();
let log_path = log.path().to_str().unwrap(); let log_path = log.path().to_str().unwrap();
let es = EvalState::new( let mut es = EvalState::new(
Store::open( Store::open(
"local", "local",
HashMap::from([ HashMap::from([

View file

@ -73,7 +73,7 @@ impl Drop for Value {
} }
impl Clone for Value { impl Clone for Value {
fn clone(&self) -> Self { fn clone(&self) -> Self {
let context = Context::new(); let mut context = Context::new();
context context
.check_one_call(|ctx_ptr| unsafe { raw::gc_incref(ctx_ptr, self.inner.as_ptr()) }) .check_one_call(|ctx_ptr| unsafe { raw::gc_incref(ctx_ptr, self.inner.as_ptr()) })
.unwrap(); .unwrap();

View file

@ -52,7 +52,7 @@ impl Store {
} }
} }
let context: Context = Context::new(); let mut context: Context = Context::new();
let uri_ptr = CString::new(url)?; let uri_ptr = CString::new(url)?;
@ -93,7 +93,7 @@ impl Store {
self.inner.ptr() self.inner.ptr()
} }
pub fn get_uri(&self) -> Result<String> { pub fn get_uri(&mut self) -> Result<String> {
let mut r = result_string_init!(); let mut r = result_string_init!();
self.context.check_one_call(|ctx_ptr| unsafe { self.context.check_one_call(|ctx_ptr| unsafe {
raw::store_get_uri( raw::store_get_uri(
@ -127,7 +127,7 @@ mod tests {
#[test] #[test]
fn get_uri() { fn get_uri() {
let store = Store::open("auto", HashMap::new()).unwrap(); let mut store = Store::open("auto", HashMap::new()).unwrap();
let uri = store.get_uri().unwrap(); let uri = store.get_uri().unwrap();
assert!(!uri.is_empty()); assert!(!uri.is_empty());
// must be ascii // must be ascii
@ -139,7 +139,7 @@ mod tests {
#[test] #[test]
#[ignore] // Needs network access #[ignore] // Needs network access
fn get_uri_nixos_cache() { fn get_uri_nixos_cache() {
let store = Store::open("https://cache.nixos.org/", HashMap::new()).unwrap(); let mut store = Store::open("https://cache.nixos.org/", HashMap::new()).unwrap();
let uri = store.get_uri().unwrap(); let uri = store.get_uri().unwrap();
assert_eq!(uri, "https://cache.nixos.org"); assert_eq!(uri, "https://cache.nixos.org");
} }

View file

@ -47,7 +47,7 @@ impl Context {
Ok(()) Ok(())
} }
fn clear(&self) { pub fn clear(&mut self) {
unsafe { unsafe {
raw::set_err_msg( raw::set_err_msg(
self.inner.as_ptr(), self.inner.as_ptr(),
@ -57,7 +57,7 @@ impl Context {
} }
} }
pub fn check_err_and_clear(&self) -> Result<()> { pub fn check_err_and_clear(&mut self) -> Result<()> {
let r = self.check_err(); let r = self.check_err();
if r.is_err() { if r.is_err() {
self.clear(); self.clear();
@ -68,14 +68,14 @@ impl Context {
/// Run the function, and check the error, then reset the error. /// Run the function, and check the error, then reset the error.
/// Make at most one call to a Nix function in `f`. /// Make at most one call to a Nix function in `f`.
/// Do not use if the context isn't fresh or cleared (e.g. with `check_err_and_clear`). /// Do not use if the context isn't fresh or cleared (e.g. with `check_err_and_clear`).
pub fn check_one_call<T, F: FnOnce(*mut raw::c_context) -> T>(&self, f: F) -> Result<T> { pub fn check_one_call<T, F: FnOnce(*mut raw::c_context) -> T>(&mut self, f: F) -> Result<T> {
let t = f(self.ptr()); let t = f(self.ptr());
self.check_err_and_clear()?; self.check_err_and_clear()?;
Ok(t) Ok(t)
} }
pub fn check_one_call_or_key_none<T, F: FnOnce(*mut raw::c_context) -> T>( pub fn check_one_call_or_key_none<T, F: FnOnce(*mut raw::c_context) -> T>(
&self, &mut self,
f: F, f: F,
) -> Result<Option<T>> { ) -> Result<Option<T>> {
let t = f(self.ptr()); let t = f(self.ptr());

View file

@ -7,7 +7,7 @@ use crate::{
}; };
pub fn set(key: &str, value: &str) -> Result<()> { pub fn set(key: &str, value: &str) -> Result<()> {
let ctx = context::Context::new(); let mut ctx = context::Context::new();
let key = std::ffi::CString::new(key)?; let key = std::ffi::CString::new(key)?;
let value = std::ffi::CString::new(value)?; let value = std::ffi::CString::new(value)?;
ctx.check_one_call(|ctx_ptr| unsafe { ctx.check_one_call(|ctx_ptr| unsafe {
@ -16,7 +16,7 @@ pub fn set(key: &str, value: &str) -> Result<()> {
} }
pub fn get(key: &str) -> Result<String> { pub fn get(key: &str) -> Result<String> {
let ctx = context::Context::new(); let mut ctx = context::Context::new();
let key = std::ffi::CString::new(key)?; let key = std::ffi::CString::new(key)?;
let mut r: Result<String> = result_string_init!(); let mut r: Result<String> = result_string_init!();
ctx.check_one_call(|ctx_ptr| unsafe { ctx.check_one_call(|ctx_ptr| unsafe {
@ -32,15 +32,16 @@ pub fn get(key: &str) -> Result<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::check_call;
use super::*; use super::*;
#[ctor::ctor] #[ctor::ctor]
fn setup() { fn setup() {
let ctx = context::Context::new(); let mut ctx = context::Context::new();
ctx.check_one_call(|ctx_ptr| unsafe { unsafe {
nix_c_raw::libstore_init(ctx_ptr); check_call!(raw::libstore_init[ctx]).unwrap();
}) }
.unwrap();
} }
#[test] #[test]