Skip to content

Commit 9e92064

Browse files
Generate context variable for type-directed targets (#908)
Signed-off-by: John Kastner <jkastner@amazon.com>
1 parent 81a551a commit 9e92064

File tree

1 file changed

+69
-28
lines changed

1 file changed

+69
-28
lines changed

cedar-policy-generators/src/expr.rs

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,7 @@ impl ExprGenerator<'_> {
284284
match target_type {
285285
Type::Bool => {
286286
if max_depth == 0 || u.len() < 10 {
287-
// no recursion allowed, so, just do a literal
288-
Ok(ast::Expr::val(u.arbitrary::<bool>()?))
287+
self.generate_expr_for_type_structurally_recursive(target_type, u)
289288
} else {
290289
gen!(u,
291290
// bool literal
@@ -534,10 +533,7 @@ impl ExprGenerator<'_> {
534533
}
535534
Type::Long => {
536535
if max_depth == 0 || u.len() < 10 {
537-
// no recursion allowed, so, just do a literal
538-
Ok(ast::Expr::val(
539-
self.constant_pool.arbitrary_int_constant(u)?,
540-
))
536+
self.generate_expr_for_type_structurally_recursive(target_type, u)
541537
} else {
542538
gen!(u,
543539
// int literal. weighted highly because all the other choices
@@ -605,10 +601,7 @@ impl ExprGenerator<'_> {
605601
}
606602
Type::String => {
607603
if max_depth == 0 || u.len() < 10 {
608-
// no recursion allowed, so, just do a literal
609-
Ok(ast::Expr::val(
610-
self.constant_pool.arbitrary_string_constant(u)?,
611-
))
604+
self.generate_expr_for_type_structurally_recursive(target_type, u)
612605
} else {
613606
gen!(u,
614607
// string literal. weighted highly because all the other choices
@@ -631,8 +624,7 @@ impl ExprGenerator<'_> {
631624
}
632625
Type::Set(target_element_ty) => {
633626
if max_depth == 0 || u.len() < 10 {
634-
// no recursion allowed, so, just do empty-set
635-
Ok(ast::Expr::set(vec![]))
627+
self.generate_expr_for_type_structurally_recursive(target_type, u)
636628
} else {
637629
gen!(u,
638630
// set literal
@@ -666,8 +658,7 @@ impl ExprGenerator<'_> {
666658
}
667659
Type::Record(m) => {
668660
if max_depth == 0 || u.len() < 10 {
669-
// no recursion allowed
670-
Err(Error::TooDeep)
661+
self.generate_expr_for_type_structurally_recursive(target_type, u)
671662
} else {
672663
gen!(u,
673664
// record literal
@@ -689,17 +680,15 @@ impl ExprGenerator<'_> {
689680
// getting an attr (on a record) with type record
690681
3 => self.generate_get_record_attr_for_type(target_type, max_depth, u),
691682
// getting an entity tag with type record
692-
3 => self.generate_get_tag_for_type(target_type, max_depth, u))
683+
3 => self.generate_get_tag_for_type(target_type, max_depth, u),
684+
// `context`
685+
1 => Ok(ast::Expr::var(ast::Var::Context)))
693686
}
694687
}
695688
Type::Entity(ety) => {
696689
if max_depth == 0 || u.len() < 10 {
697690
// no recursion allowed, so, just do `principal`, `action`, or `resource`
698-
Ok(ast::Expr::var(*u.choose(&[
699-
ast::Var::Principal,
700-
ast::Var::Action,
701-
ast::Var::Resource,
702-
])?))
691+
self.generate_expr_for_type_structurally_recursive(target_type, u)
703692
} else {
704693
gen!(u,
705694
// UID literal, that exists
@@ -729,14 +718,7 @@ impl ExprGenerator<'_> {
729718
return Err(Error::ExtensionsDisabled);
730719
};
731720
if max_depth == 0 || u.len() < 10 {
732-
// no recursion allowed, so, just call the constructor
733-
// Invariant (MethodStyleArgs), Function Style, no worries
734-
self.arbitrary_ext_constructor_call_for_type(
735-
target_type,
736-
ast::Expr::val,
737-
ast::Expr::call_extension_fn,
738-
u,
739-
)
721+
self.generate_expr_for_type_structurally_recursive(target_type, u)
740722
} else {
741723
gen!(u,
742724
// if-then-else expression, where both arms are extension types
@@ -755,6 +737,65 @@ impl ExprGenerator<'_> {
755737
}
756738
}
757739

740+
/// Get an arbitrary expression of a given type conforming to the schema by
741+
/// structural recursion on the type. This function is guaranteed to
742+
/// terminate regardless of the bytes provided by `u`.
743+
///
744+
/// To ensure this, it may call it's self recursively, but only on a type
745+
/// obtained from destructing `target_type`. It may not call `generate_expr_for_type`.
746+
fn generate_expr_for_type_structurally_recursive(
747+
&self,
748+
target_type: &Type,
749+
u: &mut Unstructured<'_>,
750+
) -> Result<ast::Expr> {
751+
match target_type {
752+
Type::Bool => Ok(ast::Expr::val(u.arbitrary::<bool>()?)),
753+
Type::Long => Ok(ast::Expr::val(
754+
self.constant_pool.arbitrary_int_constant(u)?,
755+
)),
756+
Type::String => Ok(ast::Expr::val(
757+
self.constant_pool.arbitrary_string_constant(u)?,
758+
)),
759+
Type::Set(el_ty) => {
760+
let mut l = Vec::new();
761+
u.arbitrary_loop(Some(0), Some(self.settings.max_width as u32), |u| {
762+
l.push(self.generate_expr_for_type_structurally_recursive(el_ty, u)?);
763+
Ok(std::ops::ControlFlow::Continue(()))
764+
})?;
765+
Ok(ast::Expr::set(l))
766+
}
767+
Type::Record(m) => match u.int_in_range(0..=3)? {
768+
0 => Ok(ast::Expr::var(ast::Var::Context)),
769+
_ => {
770+
let r = m
771+
.iter()
772+
.filter_map(|(a, qt)| {
773+
qt.required.then(|| {
774+
self.generate_expr_for_type_structurally_recursive(&qt.ty, u)
775+
.map(|e| (a.clone(), e))
776+
})
777+
})
778+
.collect::<Result<IndexMap<_, _>>>()?;
779+
Ok(ast::Expr::record(r)
780+
.expect("can't have duplicate keys because `r` was already a HashMap"))
781+
}
782+
},
783+
Type::Entity(ety) => match u.int_in_range(0..=3)? {
784+
0 => Ok(ast::Expr::var(ast::Var::Principal)),
785+
1 => Ok(ast::Expr::var(ast::Var::Action)),
786+
2 => Ok(ast::Expr::var(ast::Var::Resource)),
787+
_ => Ok(ast::Expr::val(self.arbitrary_uid_with_type(ety, u)?)),
788+
},
789+
Type::IPAddr | Type::Decimal | Type::DateTime | Type::Duration => self
790+
.arbitrary_ext_constructor_call_for_type(
791+
target_type,
792+
ast::Expr::val,
793+
ast::Expr::call_extension_fn,
794+
u,
795+
),
796+
}
797+
}
798+
758799
fn generate_ite_for_type(
759800
&self,
760801
target_type: &Type,

0 commit comments

Comments
 (0)