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