1mod arena;
7
8use std::env;
9use std::fmt;
10use std::sync::Arc;
11#[cfg(test)]
12use std::sync::atomic::AtomicU8;
13use std::sync::atomic::AtomicUsize;
14#[cfg(test)]
15use std::sync::atomic::Ordering;
16
17use indexmap::IndexMap;
18use serde::Deserialize;
19use serde::Serialize;
20
21use crate::SourceRange;
22use crate::errors::KclError;
23use crate::execution::KclValue;
24
25pub(crate) const RETURN_NAME: &str = "__return";
27pub(crate) const TYPE_PREFIX: &str = "__ty_";
29pub(crate) const MODULE_PREFIX: &str = "__mod_";
30pub(crate) const SKETCH_PREFIX: &str = "__sketch_";
31
32pub(crate) const KCL_MEMORY_IMPL_ENV_VAR: &str = "KCL_MEMORY_IMPL";
33
34#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Hash, Eq, ts_rs::TS)]
40pub struct EnvironmentRef(pub(crate) usize, pub(crate) usize);
41
42impl EnvironmentRef {
43 pub fn dummy() -> Self {
44 Self(usize::MAX, 0)
45 }
46
47 pub(crate) fn current(index: usize) -> Self {
48 Self(index, usize::MAX)
49 }
50
51 pub(crate) fn at_epoch(index: usize, epoch: usize) -> Self {
52 Self(index, epoch)
53 }
54
55 pub(crate) fn is_regular(&self) -> bool {
56 self.0 < usize::MAX && self.1 > 0
57 }
58
59 pub(crate) fn index(&self) -> usize {
60 self.0
61 }
62
63 pub(crate) fn epoch(&self) -> usize {
64 self.1
65 }
66
67 pub(crate) fn skip_env(&self) -> bool {
68 self.0 == usize::MAX
69 }
70
71 pub fn replace_env(&mut self, old: Self, new: Self) {
73 if self.0 == old.0 {
74 self.0 = new.0;
75 }
76 }
77
78 pub fn replace_env_and_epoch(&mut self, old: Self, new: Self) {
80 if self.0 == old.0 && self.1 == old.1 {
81 self.0 = new.0;
82 self.1 = new.1;
83 }
84 }
85}
86
87#[derive(Debug, Default)]
89pub(crate) struct MemoryStats {
90 env_count: AtomicUsize,
92 epoch_count: AtomicUsize,
94 mutation_count: AtomicUsize,
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub(crate) enum MemoryBackendKind {
100 Arena,
101}
102
103impl MemoryBackendKind {
104 pub(crate) fn from_env() -> Self {
105 #[cfg(test)]
106 if let Some(backend) = Self::test_override() {
107 return backend;
108 }
109
110 match env::var(KCL_MEMORY_IMPL_ENV_VAR) {
111 Ok(value) => Self::parse(&value),
112 Err(env::VarError::NotPresent) => Self::Arena,
113 Err(env::VarError::NotUnicode(value)) => {
114 panic!(
115 "{KCL_MEMORY_IMPL_ENV_VAR} must be valid unicode; got `{}`.",
116 value.to_string_lossy()
117 )
118 }
119 }
120 }
121
122 fn parse(value: &str) -> Self {
123 if value.trim().is_empty() || value.eq_ignore_ascii_case("arena") {
124 return Self::Arena;
125 }
126
127 panic!("Unsupported {KCL_MEMORY_IMPL_ENV_VAR} value `{value}`. Expected `arena`.",);
128 }
129
130 #[cfg(test)]
131 pub(crate) fn all() -> &'static [Self] {
132 &[Self::Arena]
133 }
134
135 #[cfg(test)]
136 pub(crate) fn override_for_test(backend: Self) -> MemoryBackendOverrideGuard {
137 let previous = TEST_BACKEND_OVERRIDE.swap(backend.test_override_value(), Ordering::SeqCst);
138 MemoryBackendOverrideGuard { previous }
139 }
140
141 #[cfg(test)]
142 fn test_override() -> Option<Self> {
143 match TEST_BACKEND_OVERRIDE.load(Ordering::SeqCst) {
144 2 => Some(Self::Arena),
145 _ => None,
146 }
147 }
148
149 #[cfg(test)]
150 fn test_override_value(self) -> u8 {
151 match self {
152 Self::Arena => 2,
153 }
154 }
155}
156
157#[cfg(test)]
158static TEST_BACKEND_OVERRIDE: AtomicU8 = AtomicU8::new(0);
159
160#[cfg(test)]
161pub(crate) struct MemoryBackendOverrideGuard {
162 previous: u8,
163}
164
165#[cfg(test)]
166impl Drop for MemoryBackendOverrideGuard {
167 fn drop(&mut self) {
168 TEST_BACKEND_OVERRIDE.store(self.previous, Ordering::SeqCst);
169 }
170}
171
172#[derive(Debug)]
173enum ProgramMemoryBackend {
174 Arena(Arc<arena::ProgramMemory>),
175}
176
177#[derive(Debug, Clone)]
178enum StackBackend {
179 Arena(arena::Stack),
180}
181
182#[derive(Debug)]
184pub(crate) struct ProgramMemory {
185 backend: ProgramMemoryBackend,
186}
187
188#[derive(Debug, Clone)]
190pub(crate) struct Stack {
191 pub(crate) memory: Arc<ProgramMemory>,
192 backend: StackBackend,
193}
194
195impl fmt::Display for ProgramMemory {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 match &self.backend {
198 ProgramMemoryBackend::Arena(memory) => memory.fmt(f),
199 }
200 }
201}
202
203impl fmt::Display for Stack {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 match &self.backend {
206 StackBackend::Arena(stack) => stack.fmt(f),
207 }
208 }
209}
210
211impl ProgramMemory {
212 #[allow(clippy::new_without_default)]
213 pub fn new() -> Arc<Self> {
214 Self::new_with_backend(MemoryBackendKind::from_env())
215 }
216
217 pub(crate) fn new_with_backend(backend: MemoryBackendKind) -> Arc<Self> {
218 match backend {
219 MemoryBackendKind::Arena => Arc::new(Self {
220 backend: ProgramMemoryBackend::Arena(arena::ProgramMemory::new()),
221 }),
222 }
223 }
224
225 pub fn new_stack(self: Arc<Self>) -> Stack {
226 let backend = match &self.backend {
227 ProgramMemoryBackend::Arena(memory) => StackBackend::Arena(Arc::clone(memory).new_stack()),
228 };
229
230 Stack { memory: self, backend }
231 }
232
233 pub fn set_std(self: &mut Arc<Self>, std: EnvironmentRef) -> Result<(), KclError> {
234 match &self.backend {
235 ProgramMemoryBackend::Arena(memory) => {
236 let mut memory = Arc::clone(memory);
237 memory.set_std(std)
238 }
239 }
240 }
241
242 pub fn requires_std(&self) -> bool {
243 match &self.backend {
244 ProgramMemoryBackend::Arena(memory) => memory.requires_std(),
245 }
246 }
247
248 pub(crate) fn stats(&self) -> &MemoryStats {
249 match &self.backend {
250 ProgramMemoryBackend::Arena(memory) => &memory.stats,
251 }
252 }
253
254 pub fn get_from_owned(
255 &self,
256 var: &str,
257 env_ref: EnvironmentRef,
258 source_range: SourceRange,
259 owner: usize,
260 ) -> Result<KclValue, KclError> {
261 match &self.backend {
262 ProgramMemoryBackend::Arena(memory) => memory.get_from_owned(var, env_ref, source_range, owner),
263 }
264 }
265
266 #[cfg(test)]
267 pub fn get_from_unchecked(&self, var: &str, env_ref: EnvironmentRef) -> Result<KclValue, KclError> {
268 match &self.backend {
269 ProgramMemoryBackend::Arena(memory) => memory.get_from(var, env_ref, SourceRange::default(), 0),
270 }
271 }
272}
273
274impl Stack {
275 pub fn deep_clone(&self) -> Result<Stack, KclError> {
276 match &self.backend {
277 StackBackend::Arena(stack) => {
278 let stack = stack.deep_clone()?;
279 Ok(Stack {
280 memory: Arc::new(ProgramMemory {
281 backend: ProgramMemoryBackend::Arena(Arc::clone(&stack.memory)),
282 }),
283 backend: StackBackend::Arena(stack),
284 })
285 }
286 }
287 }
288
289 #[cfg(test)]
290 pub fn new_for_tests() -> Stack {
291 Self::new_for_tests_with_backend(MemoryBackendKind::from_env())
292 }
293
294 #[cfg(test)]
295 pub(crate) fn new_for_tests_with_backend(backend: MemoryBackendKind) -> Stack {
296 let mut stack = ProgramMemory::new_with_backend(backend).new_stack();
297 stack
298 .push_new_root_env(false)
299 .expect("test stack root environment should be created");
300 let std = stack.current_env_ref();
301 stack
302 .memory
303 .set_std(std)
304 .expect("test standard library prelude should be initialized");
305 stack
306 }
307
308 pub fn current_epoch(&self) -> usize {
309 match &self.backend {
310 StackBackend::Arena(stack) => stack.current_epoch(),
311 }
312 }
313
314 #[cfg(test)]
315 pub(crate) fn current_env_ref(&self) -> EnvironmentRef {
316 match &self.backend {
317 StackBackend::Arena(stack) => stack.current_env_ref(),
318 }
319 }
320
321 pub fn push_new_env_for_call(&mut self, parent: EnvironmentRef) -> Result<(), KclError> {
322 match &mut self.backend {
323 StackBackend::Arena(stack) => stack.push_new_env_for_call(parent),
324 }
325 }
326
327 pub fn push_new_env_for_scope(&mut self) -> Result<(), KclError> {
328 match &mut self.backend {
329 StackBackend::Arena(stack) => stack.push_new_env_for_scope(),
330 }
331 }
332
333 pub fn push_new_root_env(&mut self, include_prelude: bool) -> Result<(), KclError> {
334 match &mut self.backend {
335 StackBackend::Arena(stack) => stack.push_new_root_env(include_prelude),
336 }
337 }
338
339 pub fn restore_env(&mut self, env: EnvironmentRef) -> Result<(), KclError> {
340 match &mut self.backend {
341 StackBackend::Arena(stack) => stack.restore_env(env),
342 }
343 }
344
345 pub fn pop_env(&mut self) -> Result<EnvironmentRef, KclError> {
346 match &mut self.backend {
347 StackBackend::Arena(stack) => stack.pop_env(),
348 }
349 }
350
351 pub fn pop_and_preserve_env(&mut self) -> Result<EnvironmentRef, KclError> {
352 match &mut self.backend {
353 StackBackend::Arena(stack) => stack.pop_and_preserve_env(),
354 }
355 }
356
357 pub fn squash_env(&mut self, old: EnvironmentRef) -> Result<(), KclError> {
358 match &mut self.backend {
359 StackBackend::Arena(stack) => stack.squash_env(old),
360 }
361 }
362
363 pub fn snapshot(&mut self) -> Result<EnvironmentRef, KclError> {
364 match &mut self.backend {
365 StackBackend::Arena(stack) => stack.snapshot(),
366 }
367 }
368
369 pub fn add(&mut self, key: String, value: KclValue, source_range: SourceRange) -> Result<(), KclError> {
370 match &mut self.backend {
371 StackBackend::Arena(stack) => stack.add(key, value, source_range),
372 }
373 }
374
375 pub fn add_recursive_closure(
376 &mut self,
377 key: String,
378 value: KclValue,
379 placeholder_env_ref: EnvironmentRef,
380 source_range: SourceRange,
381 ) -> Result<KclValue, KclError> {
382 match &mut self.backend {
383 StackBackend::Arena(stack) => stack.add_recursive_closure(key, value, placeholder_env_ref, source_range),
384 }
385 }
386
387 pub fn update(&mut self, key: &str, f: impl Fn(&mut KclValue, usize)) -> Result<(), KclError> {
388 match &mut self.backend {
389 StackBackend::Arena(stack) => stack.update(key, f),
390 }
391 }
392
393 pub fn get(&self, var: &str, source_range: SourceRange) -> Result<KclValue, KclError> {
394 match &self.backend {
395 StackBackend::Arena(stack) => stack.get(var, source_range),
396 }
397 }
398
399 pub fn get_owned(&self, var: &str, source_range: SourceRange) -> Result<KclValue, KclError> {
400 match &self.backend {
401 StackBackend::Arena(stack) => stack.get_owned(var, source_range),
402 }
403 }
404
405 pub fn cur_frame_contains(&self, var: &str) -> Result<bool, KclError> {
406 match &self.backend {
407 StackBackend::Arena(stack) => stack.cur_frame_contains(var),
408 }
409 }
410
411 pub fn get_from_call_stack(&self, key: &str, source_range: SourceRange) -> Result<(usize, KclValue), KclError> {
412 match &self.backend {
413 StackBackend::Arena(stack) => stack.get_from_call_stack(key, source_range),
414 }
415 }
416
417 pub fn find_keys_in_current_env(&self, pred: impl Fn(&KclValue) -> bool) -> Result<Vec<String>, KclError> {
418 match &self.backend {
419 StackBackend::Arena(stack) => stack.find_keys_in_current_env(pred),
420 }
421 }
422
423 pub fn find_all_in_current_env(&self) -> Result<Vec<(String, KclValue)>, KclError> {
424 match &self.backend {
425 StackBackend::Arena(stack) => stack.find_all_in_current_env(),
426 }
427 }
428
429 pub fn find_all_in_env(&self, env: EnvironmentRef) -> Result<Vec<(String, KclValue)>, KclError> {
430 match &self.backend {
431 StackBackend::Arena(stack) => stack.find_all_in_env(env),
432 }
433 }
434
435 pub(crate) fn find_all_in_env_owned(&self, env: EnvironmentRef) -> Result<IndexMap<String, KclValue>, KclError> {
436 Ok(self.find_all_in_env(env)?.into_iter().collect())
437 }
438
439 pub(crate) fn find_var_name_in_all_envs(
440 &self,
441 pred: impl Fn(&KclValue) -> bool,
442 ) -> Result<Option<String>, KclError> {
443 match &self.backend {
444 StackBackend::Arena(stack) => stack.find_var_name_in_all_envs(pred),
445 }
446 }
447
448 pub fn walk_call_stack_with<T>(&self, f: impl FnMut(&KclValue) -> Option<T>) -> Result<Vec<T>, KclError> {
449 match &self.backend {
450 StackBackend::Arena(stack) => stack.walk_call_stack_with(f),
451 }
452 }
453}
454
455#[cfg(test)]
456impl PartialEq for Stack {
457 fn eq(&self, other: &Self) -> bool {
458 let vars = self
459 .find_keys_in_current_env(|_| true)
460 .expect("stack equality should enumerate current env");
461 let vars_other = other
462 .find_keys_in_current_env(|_| true)
463 .expect("stack equality should enumerate other current env");
464 if vars != vars_other {
465 return false;
466 }
467
468 vars.iter().all(|key| {
469 self.get(key, SourceRange::default()).unwrap() == other.get(key, SourceRange::default()).unwrap()
470 })
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477
478 #[test]
479 fn parses_arena_backend_name() {
480 assert_eq!(MemoryBackendKind::parse("arena"), MemoryBackendKind::Arena);
481 assert_eq!(MemoryBackendKind::parse("ArEnA"), MemoryBackendKind::Arena);
482 }
483
484 #[test]
485 fn empty_backend_name_uses_arena() {
486 assert_eq!(MemoryBackendKind::parse(""), MemoryBackendKind::Arena);
487 assert_eq!(MemoryBackendKind::parse(" "), MemoryBackendKind::Arena);
488 }
489
490 #[test]
491 #[should_panic(expected = "Unsupported KCL_MEMORY_IMPL value `frozen`. Expected `arena`.")]
492 fn unsupported_backend_name_panics() {
493 MemoryBackendKind::parse("frozen");
494 }
495}