cel_runtime/
prepared_segment.rs1use std::marker::PhantomData;
12
13use crate::container_execution::{ArgumentPlan, ResultPlan};
14use crate::{DynSegment, ValueRepr};
15
16pub struct PreparedRawSegment {
20 body: DynSegment,
21 result: ResultPlan,
22 arity: usize,
23}
24
25impl PreparedRawSegment {
26 pub fn new(
44 body: DynSegment,
45 arguments: &[crate::ValueType],
46 result: &crate::ValueType,
47 ) -> anyhow::Result<Self> {
48 body.validate_independent_body()?;
49 for (index, read) in &body.argument_reads {
50 let expected = arguments
51 .get(*index)
52 .ok_or_else(|| anyhow::anyhow!("undeclared argument {index}"))?;
53 anyhow::ensure!(
54 expected.same_shape(read),
55 "argument {index} representation mismatch"
56 );
57 anyhow::ensure!(
58 read.can_clone(),
59 "argument {index} clone capability unavailable"
60 );
61 }
62 anyhow::ensure!(
63 body.result_type()?.same_shape(result),
64 "result representation mismatch"
65 );
66 let result = ResultPlan::new(0, body.result_type()?.clone())?;
67 Ok(Self {
68 body,
69 result,
70 arity: arguments.len(),
71 })
72 }
73
74 pub unsafe fn call(&mut self, arguments: &[*const u8]) -> anyhow::Result<crate::OwnedValue> {
87 debug_assert_eq!(arguments.len(), self.arity);
88 let mut output = unsafe { self.body.execute_raw(arguments)? };
89 Ok(unsafe { self.result.take(&mut output) })
90 }
91}
92
93pub struct PreparedSegment<A: ValueRepr, R: ValueRepr> {
97 body: DynSegment,
98 arguments: ArgumentPlan,
99 pointers: Vec<*const u8>,
100 result: ResultPlan,
101 witness: PhantomData<fn(A, R)>,
102}
103
104impl<A: ValueRepr, R: ValueRepr> PreparedSegment<A, R> {
105 pub(crate) fn new(body: DynSegment) -> anyhow::Result<Self> {
112 body.validate_independent_body()?;
113 let args = A::value_type();
114 let fields = args
115 .tuple_elements()
116 .ok_or_else(|| anyhow::anyhow!("prepared arguments must be a canonical list"))?;
117 for (index, read) in &body.argument_reads {
118 let expected = fields
119 .get(*index)
120 .ok_or_else(|| anyhow::anyhow!("undeclared argument {index}"))?;
121 anyhow::ensure!(
122 expected.value_type.same_shape(read),
123 "argument {index} representation mismatch"
124 );
125 anyhow::ensure!(
126 read.can_clone(),
127 "argument {index} clone capability unavailable"
128 );
129 }
130 body.result_type()?.validate::<R>()?;
131 let result = ResultPlan::new(0, body.result_type()?.clone())?;
132 let pointers = vec![std::ptr::null(); fields.len()];
133 Ok(Self {
134 body,
135 arguments: ArgumentPlan::borrowed(args)?,
136 pointers,
137 result,
138 witness: PhantomData,
139 })
140 }
141
142 pub fn call(&mut self, args: &A::Value) -> anyhow::Result<R::Value> {
152 let base = std::ptr::from_ref(args).cast::<u8>();
153 unsafe {
156 self.arguments.bind_pointers(base, &mut self.pointers);
157 let mut output = self.body.execute_raw(&self.pointers)?;
158 Ok(self.result.take_typed::<R::Value>(&mut output))
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use crate::{Array, ArrayOf, Cons, IntoCStackList, Leaf, Nil, ValueType};
167
168 #[test]
170 fn trusted_calls_never_compare_descriptors() {
171 type Pair = Cons<Leaf<i32>, Cons<Leaf<f64>, Nil>>;
172 type Argument = ArrayOf<Pair>;
173 type Args = Cons<Argument, Nil>;
174 let mut body = DynSegment::new::<()>();
175 body.push_argument(0, Argument::value_type().with_clone::<Argument>().unwrap())
176 .unwrap();
177 let mut body = body.prepare::<Args, Argument>().unwrap();
178 let input = (Array::from_vec(vec![(1i32, 2f64).into_c_stack_list()]),).into_c_stack_list();
179 crate::value_type::SHAPE_COMPARISONS.with(|count| count.set(0));
180 let first = body.call(&input).unwrap();
181 let second = body.call(&input).unwrap();
182 crate::value_type::SHAPE_COMPARISONS.with(|count| assert_eq!(count.get(), 0));
183 assert_ne!(first.as_slice().as_ptr(), second.as_slice().as_ptr());
184 assert_ne!(input.1.as_slice().as_ptr(), first.as_slice().as_ptr());
185 }
186
187 #[test]
189 fn erased_prepared_calls_never_compare_descriptors() {
190 type Pair = Cons<Leaf<i32>, Cons<ArrayOf<Leaf<f64>>, Nil>>;
191 type Argument = ArrayOf<Pair>;
192 let descriptor = Argument::value_type()
193 .with_clone::<Argument>()
194 .unwrap()
195 .with_equality::<Argument>()
196 .unwrap();
197 let mut body = DynSegment::new::<()>();
198 body.push_argument(0, descriptor.clone()).unwrap();
199 let mut body =
200 PreparedRawSegment::new(body, std::slice::from_ref(&descriptor), &descriptor).unwrap();
201 let input = Array::from_vec(vec![(1, Array::from_vec(vec![2.0])).into_c_stack_list()]);
202 let pointer = std::ptr::from_ref(&input).cast::<u8>();
203 crate::value_type::SHAPE_COMPARISONS.with(|count| count.set(0));
204 let first = unsafe { body.call(&[pointer]) }.unwrap();
206 let second = unsafe { body.call(&[pointer]) }.unwrap();
207 assert!(unsafe { first.eq_bound(&second) });
209 crate::value_type::SHAPE_COMPARISONS.with(|count| assert_eq!(count.get(), 0));
210 let first = first.try_into_typed::<Argument>().unwrap();
211 let second = second.try_into_typed::<Argument>().unwrap();
212 assert_ne!(input.as_slice().as_ptr(), first.as_slice().as_ptr());
213 assert_ne!(first.as_slice().as_ptr(), second.as_slice().as_ptr());
214 assert_ne!(
215 first.as_slice()[0].0.1.as_slice().as_ptr(),
216 second.as_slice()[0].0.1.as_slice().as_ptr()
217 );
218 }
219
220 #[test]
222 fn erased_preparation_checks_complete_signature_before_effects() {
223 let mut body = DynSegment::new::<()>();
224 body.push_arg::<i32>(0);
225 assert!(
226 PreparedRawSegment::new(body, &[ValueType::leaf::<u32>()], &ValueType::leaf::<i32>())
227 .is_err()
228 );
229 let mut body = DynSegment::new::<()>();
230 body.push_arg::<i32>(0);
231 assert!(PreparedRawSegment::new(body, &[], &ValueType::leaf::<i32>()).is_err());
232 let mut body = DynSegment::new::<()>();
233 body.just(1i32);
234 assert!(PreparedRawSegment::new(body, &[], &ValueType::leaf::<u32>()).is_err());
235 let body = DynSegment::new::<(i32,)>();
236 assert!(PreparedRawSegment::new(body, &[], &ValueType::leaf::<i32>()).is_err());
237 }
238
239 #[test]
241 fn preparation_rejects_every_boundary_mismatch() {
242 let mut wrong_result = DynSegment::new::<()>();
243 wrong_result.just(1i32);
244 assert!(wrong_result.prepare::<Nil, Leaf<u32>>().is_err());
245
246 let mut wrong_arg = DynSegment::new::<()>();
247 wrong_arg.push_arg::<i32>(0);
248 assert!(
249 wrong_arg
250 .prepare::<Cons<Leaf<u32>, Nil>, Leaf<i32>>()
251 .is_err()
252 );
253
254 let mut undeclared = DynSegment::new::<()>();
255 undeclared.push_arg::<i32>(1);
256 assert!(
257 undeclared
258 .prepare::<Cons<Leaf<i32>, Nil>, Leaf<i32>>()
259 .is_err()
260 );
261
262 let mut nonlist = DynSegment::new::<()>();
263 nonlist.just(1i32);
264 assert!(nonlist.prepare::<Leaf<()>, Leaf<i32>>().is_err());
265
266 let preloaded = DynSegment::new::<(i32,)>();
267 assert!(
268 preloaded
269 .prepare::<Cons<Leaf<i32>, Nil>, Leaf<i32>>()
270 .is_err()
271 );
272
273 let mut nonclone = DynSegment::new::<()>();
274 assert!(
275 nonclone
276 .push_argument(0, ValueType::leaf::<String>())
277 .is_err()
278 );
279 assert!(nonclone.peek_stack_infos(0).is_empty());
280 }
281}