#![allow(clippy::result_large_err)]
#![allow(clippy::type_complexity)]
pub mod apply;
mod array_list;
mod bitops;
pub mod builtins;
pub mod callback;
pub mod destructure;
pub mod dynamics;
pub mod env;
pub mod error;
pub mod eval;
pub mod ir_interp;
pub mod loader;
pub mod macros;
mod new;
mod regex;
pub mod special;
pub mod syntax_quote;
pub mod taps;
mod transients;
mod util;
mod virtualize;
pub use callback::invoke;
pub use env::{Env, GlobalEnv};
pub use error::{EvalError, EvalResult};
pub use eval::eval;
pub use loader::load_ns;
use std::cell::RefCell;
use std::sync::Arc;
thread_local! {
static ENV_ROOTS: RefCell<Vec<*const Env>> = const { RefCell::new(Vec::new()) };
static VALUE_ROOTS: RefCell<Vec<(*const cljrs_value::Value, usize)>> =
const { RefCell::new(Vec::new()) };
}
pub struct EnvRootGuard;
impl Drop for EnvRootGuard {
fn drop(&mut self) {
ENV_ROOTS.with(|roots| {
roots.borrow_mut().pop();
});
}
}
pub struct ValueRootGuard {
pushed: bool,
}
impl Drop for ValueRootGuard {
fn drop(&mut self) {
if self.pushed {
VALUE_ROOTS.with(|roots| {
roots.borrow_mut().pop();
});
}
}
}
pub fn push_env_root(env: &Env) -> EnvRootGuard {
ENV_ROOTS.with(|roots| {
roots.borrow_mut().push(env as *const Env);
});
EnvRootGuard
}
pub fn root_value(val: &cljrs_value::Value) -> ValueRootGuard {
VALUE_ROOTS.with(|roots| {
roots
.borrow_mut()
.push((val as *const cljrs_value::Value, 1));
});
ValueRootGuard { pushed: true }
}
pub fn root_values(vals: &[cljrs_value::Value]) -> ValueRootGuard {
if vals.is_empty() {
return ValueRootGuard { pushed: false };
}
VALUE_ROOTS.with(|roots| {
roots.borrow_mut().push((vals.as_ptr(), vals.len()));
});
ValueRootGuard { pushed: true }
}
pub fn gc_safepoint(env: &Env) {
if !cljrs_gc::gc_requested() && !cljrs_gc::CONFIG_CANCELLATION.in_progress() {
return;
}
if cljrs_gc::CONFIG_CANCELLATION.in_progress() {
cljrs_gc::safepoint();
return;
}
if !cljrs_gc::take_gc_request() {
cljrs_gc::safepoint();
return;
}
let Some(_stw_guard) = cljrs_gc::begin_stw() else {
cljrs_gc::safepoint();
return;
};
cljrs_gc::HEAP.collect(|visitor| {
cljrs_gc::HEAP.trace_registered_roots(visitor);
trace_env_roots(env, visitor);
trace_thread_env_roots(visitor);
trace_value_roots(visitor);
dynamics::trace_current(visitor);
taps::trace_roots(visitor);
cljrs_gc::trace_thread_alloc_roots(visitor);
});
}
fn trace_env_roots(env: &Env, visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::Trace;
for frame in &env.frames {
for (_name, val) in &frame.bindings {
val.trace(visitor);
}
}
trace_globals(&env.globals, visitor);
}
fn trace_value_roots(visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::Trace;
VALUE_ROOTS.with(|roots| {
for &(ptr, count) in roots.borrow().iter() {
let slice = unsafe { std::slice::from_raw_parts(ptr, count) };
for val in slice {
val.trace(visitor);
}
}
});
}
fn trace_thread_env_roots(visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::Trace;
ENV_ROOTS.with(|roots| {
for env_ptr in roots.borrow().iter() {
let env = unsafe { &**env_ptr };
for frame in &env.frames {
for (_name, val) in &frame.bindings {
val.trace(visitor);
}
}
}
});
}
fn trace_globals(globals: &GlobalEnv, visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::GcVisitor as _;
let namespaces = globals.namespaces.read().unwrap();
for (_name, ns_ptr) in namespaces.iter() {
visitor.visit(ns_ptr);
}
}
pub fn standard_env_minimal() -> Arc<GlobalEnv> {
let globals = GlobalEnv::new();
builtins::register_all(&globals, "clojure.core");
globals.get_or_create_ns("user");
globals.refer_all("user", "clojure.core");
{
let mut env = Env::new(globals.clone(), "clojure.core");
let src = builtins::BOOTSTRAP_SOURCE;
let mut parser = cljrs_reader::Parser::new(src.to_string(), "<bootstrap>".to_string());
match parser.parse_all() {
Ok(forms) => {
for form in forms {
let _alloc_frame = cljrs_gc::push_alloc_frame();
if let Err(e) = eval::eval(&form, &mut env) {
eprintln!("[bootstrap warning] {}: {:?}", form.span.start, e);
}
}
}
Err(e) => eprintln!("[bootstrap parse error] {:?}", e),
}
}
globals.refer_all("user", "clojure.core");
globals.mark_loaded("clojure.core");
{
let mut env = Env::new(globals.clone(), "user");
special::sync_star_ns(&mut env);
}
globals
}
pub fn standard_env() -> Arc<GlobalEnv> {
let globals = standard_env_minimal();
{
let mut env = Env::new(globals.clone(), "clojure.core");
let src = builtins::CLOJURE_TEST_SOURCE;
let mut parser = cljrs_reader::Parser::new(src.to_string(), "<clojure.test>".to_string());
match parser.parse_all() {
Ok(forms) => {
for form in forms {
let _alloc_frame = cljrs_gc::push_alloc_frame();
if let Err(e) = eval::eval(&form, &mut env) {
eprintln!("[clojure.test warning] {}: {:?}", form.span.start, e);
}
}
}
Err(e) => eprintln!("[clojure.test parse error] {:?}", e),
}
globals.mark_loaded("clojure.test");
}
{
let mut env = Env::new(globals.clone(), "user");
special::sync_star_ns(&mut env);
}
globals
}
pub fn standard_env_with_paths(source_paths: Vec<std::path::PathBuf>) -> Arc<GlobalEnv> {
let globals = standard_env();
globals.set_source_paths(source_paths);
globals
}