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