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 "System/getenv",
98 "clojure.rust.process/run",
101 ];
102 if DENIED.contains(&name) {
103 Err(forbidden(name))
104 } else {
105 Ok(())
106 }
107}
108
109pub fn check_special(name: &str) -> EvalResult<()> {
111 if !transaction_policy_active() {
112 return Ok(());
113 }
114 const DENIED: &[&str] = &[
115 ".",
116 "ns",
117 "require",
118 "in-ns",
119 "alias",
120 "load-file",
121 "with-out-str",
122 "await",
123 ];
124 if DENIED.contains(&name) {
125 Err(forbidden(name))
126 } else {
127 Ok(())
128 }
129}
130
131pub fn check_versioned_lookup() -> EvalResult<()> {
132 if transaction_policy_active() {
133 Err(forbidden("versioned namespace lookup"))
134 } else {
135 Ok(())
136 }
137}