cljrs_runtime/env/
policy.rs1use std::cell::Cell;
4
5use crate::env::error::{EvalError, EvalResult};
6
7thread_local! {
8 static TRANSACTION_DEPTH: Cell<usize> = const { Cell::new(0) };
9 static TRANSACTION_GENSYM: Cell<u64> = const { Cell::new(0) };
10}
11
12#[must_use = "dropping the guard removes the transaction execution policy"]
14pub struct TransactionPolicyGuard;
15
16impl TransactionPolicyGuard {
17 pub fn install() -> Self {
18 TRANSACTION_DEPTH.with(|depth| {
19 if depth.get() == 0 {
20 TRANSACTION_GENSYM.with(|counter| counter.set(0));
21 }
22 depth.set(depth.get() + 1);
23 });
24 Self
25 }
26}
27
28impl Drop for TransactionPolicyGuard {
29 fn drop(&mut self) {
30 TRANSACTION_DEPTH.with(|depth| depth.set(depth.get().saturating_sub(1)));
31 }
32}
33
34pub fn transaction_policy_active() -> bool {
35 TRANSACTION_DEPTH.with(|depth| depth.get() != 0)
36}
37
38pub fn next_transaction_gensym() -> Option<u64> {
41 if !transaction_policy_active() {
42 return None;
43 }
44 Some(TRANSACTION_GENSYM.with(|counter| {
45 let current = counter.get();
46 counter.set(current.wrapping_add(1));
47 current
48 }))
49}
50
51fn forbidden(operation: &str) -> EvalError {
52 EvalError::ForbiddenEffect(operation.to_string())
53}
54
55pub fn check_native(name: &str) -> EvalResult<()> {
61 if !transaction_policy_active() {
62 return Ok(());
63 }
64 const DENIED: &[&str] = &[
65 "print",
66 "println",
67 "pr",
68 "prn",
69 "printf",
70 "newline",
71 "flush",
72 "spit",
73 "slurp",
74 "close",
75 "nanotime",
76 "sleep",
77 "rand",
78 "rand-int",
79 "random-sample",
80 "shuffle",
81 "random-uuid",
82 "gensym",
83 "add-tap",
84 "remove-tap",
85 "tap>",
86 "shared-atom",
87 "promise",
88 "deliver",
89 "send",
90 "send-off",
91 "new",
92 "Exception.",
93 "push-precision!",
94 "pop-precision!",
95 ];
96 if DENIED.contains(&name) {
97 Err(forbidden(name))
98 } else {
99 Ok(())
100 }
101}
102
103pub fn check_special(name: &str) -> EvalResult<()> {
105 if !transaction_policy_active() {
106 return Ok(());
107 }
108 const DENIED: &[&str] = &[
109 ".",
110 "ns",
111 "require",
112 "in-ns",
113 "alias",
114 "load-file",
115 "with-out-str",
116 "await",
117 ];
118 if DENIED.contains(&name) {
119 Err(forbidden(name))
120 } else {
121 Ok(())
122 }
123}
124
125pub fn check_versioned_lookup() -> EvalResult<()> {
126 if transaction_policy_active() {
127 Err(forbidden("versioned namespace lookup"))
128 } else {
129 Ok(())
130 }
131}