1use crate::{
8 ast::{DefinitionBody, Primitive},
9 cps::{Compile, Cps, codegen::RuntimeFunctionsBuilder},
10 env::{Environment, Global, TopLevelEnvironment},
11 exceptions::{Exception, SourceCache, raise},
12 gc::{Gc, GcInner, Trace, init_gc},
13 hashtables::EqualHashSet,
14 lists::{Pair, list_to_vec},
15 num,
16 ports::{BufferMode, Port, Transcoder},
17 proc::{Application, ContBarrier, ContinuationPtr, FuncPtr, ProcDebugInfo, Procedure, UserPtr},
18 registry::Registry,
19 symbols::Symbol,
20 syntax::{Identifier, Span, Syntax},
21 value::{Cell, UnpackedValue, Value},
22};
23use parking_lot::{MappedRwLockWriteGuard, RwLock, RwLockWriteGuard};
24use scheme_rs_macros::{maybe_async, maybe_await, runtime_fn};
25use std::{
26 collections::{BTreeSet, HashSet},
27 mem::ManuallyDrop,
28 path::Path,
29 sync::Arc,
30};
31
32#[derive(Trace, Clone)]
63pub struct Runtime(pub(crate) Gc<RwLock<RuntimeInner>>);
64
65impl Default for Runtime {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl Runtime {
72 pub fn new() -> Self {
75 let this = Self(Gc::new(RwLock::new(RuntimeInner::new())));
76 let new_registry = Registry::new(&this);
77 this.0.write().registry = new_registry;
78 this
79 }
80
81 #[maybe_async]
83 pub fn run_program(&self, path: &Path) -> Result<Vec<Value>, Exception> {
84 #[cfg(not(feature = "async"))]
85 use std::fs::File;
86
87 #[cfg(feature = "tokio")]
88 use tokio::fs::File;
89
90 let progm = TopLevelEnvironment::new_program(self, path);
91 let env = Environment::Top(progm.clone());
92
93 let mut form = {
94 let port = Port::new(
95 path.display(),
96 maybe_await!(File::open(path)).map_err(Exception::io_error)?,
97 BufferMode::Block,
98 Some(Transcoder::native()),
99 );
100 let file_name = path.file_name().unwrap().to_str().unwrap_or("<unknown>");
101 let span = Span::new(file_name);
102 maybe_await!(port.all_sexprs(span)).map_err(Exception::from)?
103 };
104
105 form.add_scope(progm.scope());
106
107 let add_rnrs_import = if let Some(first_form) = form.car()
109 && let Some(Syntax::Identifier { ident, .. }) = first_form.car()
110 && let Some(binding) = ident.resolve()
111 {
112 env.lookup_primitive(binding) != Some(Primitive::Import)
113 } else {
114 true
115 };
116
117 if add_rnrs_import {
119 maybe_await!(env.import("(library (rnrs))".parse()?))?;
120 }
121
122 let body = maybe_await!(DefinitionBody::parse_lib_body(self, &form, &env))?;
123 let compiled = body.compile_top_level();
124 let closure = maybe_await!(self.compile_expr(compiled));
125
126 maybe_await!(Application::new(closure, Vec::new()).eval(&mut ContBarrier::default()))
127 }
128
129 #[cfg(not(feature = "async"))]
131 #[track_caller]
132 pub fn def_lib(&self, lib: &str) -> Result<(), Exception> {
133 use std::panic::Location;
134
135 self.get_registry()
136 .def_lib(self, lib, Location::caller().file())
137 }
138
139 #[cfg(feature = "async")]
141 pub async fn def_lib(&self, lib: &str) -> Result<(), Exception> {
142 use std::panic::Location;
143
144 self.get_registry()
145 .def_lib(self, lib, Location::caller().file())
146 .await
147 }
148
149 pub(crate) fn get_registry(&self) -> Registry {
150 self.0.read().registry.clone()
151 }
152
153 #[maybe_async]
154 pub(crate) fn compile_expr(&self, expr: Cps) -> Procedure {
155 let (completion_tx, completion_rx) = completion();
156 let task = CompilationTask {
157 completion_tx,
158 compilation_unit: expr,
159 runtime: self.clone(),
160 };
161 let sender = { self.0.read().compilation_buffer_tx.clone() };
162 let _ = maybe_await!(sender.send(task));
163 maybe_await!(recv_procedure(completion_rx))
165 }
166
167 pub(crate) unsafe fn from_raw_inc_rc(rt: *mut GcInner<RwLock<RuntimeInner>>) -> Self {
168 unsafe { Self(Gc::from_raw_inc_rc(rt)) }
169 }
170
171 pub fn source_cache(&self) -> MappedRwLockWriteGuard<'_, SourceCache> {
172 RwLockWriteGuard::map(self.0.write(), |inner| &mut inner.source_cache)
173 }
174}
175
176#[allow(unused)]
177#[cfg(not(feature = "async"))]
178fn read_to_string(path: &Path) -> std::io::Result<String> {
179 std::fs::read_to_string(path)
180}
181
182#[allow(unused)]
183#[cfg(feature = "tokio")]
184async fn read_to_string(path: &Path) -> std::io::Result<String> {
185 tokio::fs::read_to_string(path).await
186}
187
188#[cfg(not(feature = "async"))]
189type CompilationBufferTx = std::sync::mpsc::SyncSender<CompilationTask>;
190#[cfg(not(feature = "async"))]
191type CompilationBufferRx = std::sync::mpsc::Receiver<CompilationTask>;
192
193#[cfg(feature = "async")]
194type CompilationBufferTx = tokio::sync::mpsc::Sender<CompilationTask>;
195#[cfg(feature = "async")]
196type CompilationBufferRx = tokio::sync::mpsc::Receiver<CompilationTask>;
197
198#[derive(Trace)]
199pub(crate) struct RuntimeInner {
200 pub(crate) registry: Registry,
202 compilation_buffer_tx: CompilationBufferTx,
204 pub(crate) constants_pool: EqualHashSet,
205 pub(crate) globals_pool: HashSet<Global>,
206 pub(crate) debug_info: DebugInfo,
207 pub(crate) source_cache: SourceCache,
208}
209
210impl Default for RuntimeInner {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216const MAX_COMPILATION_TASKS: usize = 5; #[cfg(not(feature = "async"))]
219fn compilation_buffer() -> (CompilationBufferTx, CompilationBufferRx) {
220 std::sync::mpsc::sync_channel(MAX_COMPILATION_TASKS)
221}
222
223#[cfg(feature = "async")]
224fn compilation_buffer() -> (CompilationBufferTx, CompilationBufferRx) {
225 tokio::sync::mpsc::channel(MAX_COMPILATION_TASKS)
226}
227
228impl RuntimeInner {
229 fn new() -> Self {
230 init_gc();
232 let (compilation_buffer_tx, compilation_buffer_rx) = compilation_buffer();
233 std::thread::spawn(move || compilation_task(compilation_buffer_rx));
234 RuntimeInner {
235 registry: Registry::empty(),
236 compilation_buffer_tx,
237 constants_pool: EqualHashSet::new(),
238 globals_pool: HashSet::new(),
239 debug_info: DebugInfo::default(),
240 source_cache: SourceCache::default(),
241 }
242 }
243}
244
245#[derive(Trace, Clone, Debug, Default)]
246pub(crate) struct DebugInfo {
247 stored_func_info: Vec<Arc<ProcDebugInfo>>,
249}
250
251impl DebugInfo {
252 pub fn store_func_info(&mut self, debug_info: Arc<ProcDebugInfo>) {
253 self.stored_func_info.push(debug_info);
254 }
255}
256
257#[cfg(not(feature = "async"))]
258type CompletionTx = std::sync::mpsc::SyncSender<Procedure>;
259#[cfg(not(feature = "async"))]
260type CompletionRx = std::sync::mpsc::Receiver<Procedure>;
261
262#[cfg(feature = "async")]
263type CompletionTx = tokio::sync::oneshot::Sender<Procedure>;
264#[cfg(feature = "async")]
265type CompletionRx = tokio::sync::oneshot::Receiver<Procedure>;
266
267#[cfg(not(feature = "async"))]
268fn completion() -> (CompletionTx, CompletionRx) {
269 std::sync::mpsc::sync_channel(1)
270}
271
272#[cfg(feature = "async")]
273fn completion() -> (CompletionTx, CompletionRx) {
274 tokio::sync::oneshot::channel()
275}
276
277#[cfg(not(feature = "async"))]
278fn recv_procedure(rx: CompletionRx) -> Procedure {
279 rx.recv().unwrap()
280}
281
282#[cfg(feature = "async")]
283async fn recv_procedure(rx: CompletionRx) -> Procedure {
284 rx.await.unwrap()
285}
286
287struct CompilationTask {
288 compilation_unit: Cps,
289 completion_tx: CompletionTx,
290 runtime: Runtime,
295}
296
297#[cfg(not(feature = "async"))]
298fn recv_compilation_task(rx: &mut CompilationBufferRx) -> Option<CompilationTask> {
299 rx.recv().ok()
300}
301
302#[cfg(feature = "async")]
303fn recv_compilation_task(rx: &mut CompilationBufferRx) -> Option<CompilationTask> {
304 rx.blocking_recv()
305}
306
307fn compilation_task(mut compilation_queue_rx: CompilationBufferRx) {
308 use cranelift::prelude::*;
309 use cranelift_jit::{JITBuilder, JITModule};
310
311 let mut flag_builder = settings::builder();
312 flag_builder.set("use_colocated_libcalls", "false").unwrap();
313 flag_builder.set("is_pic", "false").unwrap();
315 let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
316 panic!("host machine is not supported: {msg}");
317 });
318 let isa = isa_builder
319 .finish(settings::Flags::new(flag_builder))
320 .unwrap();
321
322 let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
323
324 for runtime_fn in inventory::iter::<RuntimeFn> {
325 (runtime_fn.install_symbol)(&mut jit_builder);
326 }
327
328 let mut module = JITModule::new(jit_builder);
329 let mut runtime_funcs_builder = RuntimeFunctionsBuilder::default();
330
331 for runtime_fn in inventory::iter::<RuntimeFn> {
332 (runtime_fn.install_decl)(&mut runtime_funcs_builder, &mut module);
333 }
334
335 let runtime_funcs = runtime_funcs_builder.build().unwrap();
336
337 let mut debug_info = DebugInfo::default();
341
342 while let Some(task) = recv_compilation_task(&mut compilation_queue_rx) {
343 let CompilationTask {
344 completion_tx,
345 compilation_unit,
346 runtime,
347 } = task;
348
349 let proc =
350 compilation_unit.into_procedure(runtime, &runtime_funcs, &mut module, &mut debug_info);
351
352 let _ = completion_tx.send(proc);
353 }
354
355 unsafe {
357 module.free_memory();
358 }
359}
360
361pub(crate) struct RuntimeFn {
362 install_decl:
363 for<'a> fn(&'a mut RuntimeFunctionsBuilder, module: &'a mut cranelift_jit::JITModule),
364 install_symbol: for<'a> fn(&'a mut cranelift_jit::JITBuilder),
365}
366
367impl RuntimeFn {
368 pub(crate) const fn new(
369 install_decl: for<'a> fn(
370 &'a mut RuntimeFunctionsBuilder,
371 module: &'a mut cranelift_jit::JITModule,
372 ),
373 install_symbol: for<'a> fn(&'a mut cranelift_jit::JITBuilder),
374 ) -> Self {
375 Self {
376 install_decl,
377 install_symbol,
378 }
379 }
380}
381
382inventory::collect!(RuntimeFn);
383
384unsafe fn arc_from_ptr<T>(ptr: *const T) -> Option<Arc<T>> {
385 unsafe {
386 if ptr.is_null() {
387 return None;
388 }
389 Arc::increment_strong_count(ptr);
390 Some(Arc::from_raw(ptr))
391 }
392}
393
394#[runtime_fn]
396unsafe extern "C" fn alloc_cell() -> *const () {
397 Value::into_raw(Value::from(Cell(Gc::new(RwLock::new(Value::undefined())))))
398}
399
400#[runtime_fn]
402unsafe extern "C" fn read_cell(cell: *const ()) -> *const () {
403 unsafe {
404 let cell = Value::from_raw(cell);
405 let cell: Cell = cell.try_into().unwrap();
406 let cell = ManuallyDrop::new(cell);
409 let cell_read = cell.0.read();
410 Value::as_raw(&cell_read)
411 }
412}
413
414#[runtime_fn]
416unsafe extern "C" fn dropv(val: *const *const (), num_drops: u32) {
417 unsafe {
418 for i in 0..num_drops {
419 drop(Value::from_raw(val.add(i as usize).read()));
420 }
421 }
422}
423
424#[runtime_fn]
426unsafe extern "C" fn apply(
427 runtime: *mut GcInner<RwLock<RuntimeInner>>,
428 op: *const (),
429 args: *const *const (),
430 num_args: u32,
431 barrier: *mut ContBarrier,
432) -> *mut Application {
433 unsafe {
434 let args: Vec<_> = (0..num_args)
435 .map(|i| Value::from_raw_inc_rc(args.add(i as usize).read()))
436 .collect();
437
438 let op = match Value::from_raw_inc_rc(op).unpack() {
439 UnpackedValue::Procedure(op) => op,
440 x => {
441 let raised = raise(
442 Runtime::from_raw_inc_rc(runtime),
443 Exception::invalid_operator(x.type_name()).into(),
444 barrier.as_mut().unwrap_unchecked(),
445 );
446 return Box::into_raw(Box::new(raised));
447 }
448 };
449
450 let app = Application::new(op, args);
451
452 Box::into_raw(Box::new(app))
453 }
454}
455
456#[runtime_fn]
458unsafe extern "C" fn get_frame(op: *const (), span: *const ()) -> *const () {
459 unsafe {
460 let op = Value::from_raw_inc_rc(op);
461 let Some(op) = op.cast_to_scheme_type::<Procedure>() else {
462 return Value::into_raw(Value::null());
463 };
464 let span = Value::from_raw_inc_rc(span);
465 let span = span.cast_to_rust_type::<Span>().unwrap();
466 let frame = Syntax::Identifier {
467 ident: Identifier {
468 sym: op
469 .get_debug_info()
470 .map_or_else(|| Symbol::intern("<lambda>"), |dbg| dbg.name),
471 scopes: BTreeSet::new(),
472 },
473 span: span.as_ref().clone(),
474 };
475 Value::into_raw(Value::from(frame))
476 }
477}
478
479#[runtime_fn]
481unsafe extern "C" fn set_continuation_mark(
482 tag: *const (),
483 val: *const (),
484 barrier: *mut ContBarrier,
485) {
486 unsafe {
487 let tag = Value::from_raw_inc_rc(tag);
488 let val = Value::from_raw_inc_rc(val);
489 barrier
490 .as_mut()
491 .unwrap()
492 .set_continuation_mark(tag.cast_to_scheme_type().unwrap(), val);
493 }
494}
495
496#[runtime_fn]
498pub(crate) unsafe extern "C" fn halt(args: *const ()) -> *mut Application {
499 unsafe {
500 let args = ManuallyDrop::new(Value::from_raw(args));
502 let mut flattened = Vec::new();
503 list_to_vec(&args, &mut flattened);
504 let app = Application::halt_ok(flattened);
505 Box::into_raw(Box::new(app))
506 }
507}
508
509#[runtime_fn]
511unsafe extern "C" fn store(from: *const (), to: *const ()) {
512 unsafe {
513 let from = Value::from_raw_inc_rc(from);
516 let to: ManuallyDrop<Cell> = ManuallyDrop::new(Value::from_raw(to).try_into().unwrap());
517 *to.0.write() = from;
518 }
519}
520
521#[runtime_fn]
523unsafe extern "C" fn car(val: *const (), error: *mut Value) -> *const () {
524 unsafe {
525 let val = ManuallyDrop::new(Value::from_raw(val));
526 match val.try_to_scheme_type::<Pair>() {
527 Ok(pair) => Value::into_raw(pair.car()),
528 Err(condition) => {
529 error.write(condition.into());
530 Value::into_raw(Value::undefined())
531 }
532 }
533 }
534}
535
536#[runtime_fn]
538unsafe extern "C" fn cdr(val: *const (), error: *mut Value) -> *const () {
539 unsafe {
540 let val = ManuallyDrop::new(Value::from_raw(val));
541 match val.try_to_scheme_type::<Pair>() {
542 Ok(pair) => Value::into_raw(pair.cdr()),
543 Err(condition) => {
544 error.write(condition.into());
545 Value::into_raw(Value::undefined())
546 }
547 }
548 }
549}
550
551#[runtime_fn]
553unsafe extern "C" fn cons(car: *const (), cdr: *const ()) -> *const () {
554 unsafe {
555 let car = Value::from_raw_inc_rc(car);
556 let cdr = Value::from_raw_inc_rc(cdr);
557 Value::into_raw(Value::from(Pair::mutable(car, cdr)))
558 }
559}
560
561#[runtime_fn]
563unsafe extern "C" fn list(vals: *const *const (), num_vals: u32) -> *const () {
564 let mut list = Value::null();
565 unsafe {
566 for i in (0..num_vals).rev() {
567 list = Value::from(Pair::mutable(
568 Value::from_raw_inc_rc(vals.add(i as usize).read()),
569 list,
570 ));
571 }
572 }
573 Value::into_raw(list)
574}
575
576#[runtime_fn]
578unsafe extern "C" fn make_continuation(
579 runtime: *mut GcInner<RwLock<RuntimeInner>>,
580 fn_ptr: ContinuationPtr,
581 env: *const *const (),
582 num_envs: u32,
583 num_required_args: u32,
584 variadic: bool,
585 barrier: *mut ContBarrier,
586) -> *const () {
587 unsafe {
588 let env: Vec<_> = (0..num_envs)
590 .map(|i| Value::from_raw_inc_rc(env.add(i as usize).read()))
591 .collect();
592
593 let proc = barrier.as_mut().unwrap().new_k(
594 Runtime::from_raw_inc_rc(runtime),
595 env,
596 fn_ptr,
597 num_required_args as usize,
598 variadic,
599 );
600
601 Value::into_raw(Value::from(proc))
602 }
603}
604
605#[runtime_fn]
607unsafe extern "C" fn make_user(
608 runtime: *mut GcInner<RwLock<RuntimeInner>>,
609 fn_ptr: UserPtr,
610 env: *const *const (),
611 num_envs: u32,
612 num_required_args: u32,
613 variadic: bool,
614 debug_info: *const ProcDebugInfo,
615) -> *const () {
616 unsafe {
617 let env: Vec<_> = (0..num_envs)
619 .map(|i| Value::from_raw_inc_rc(env.add(i as usize).read()))
620 .collect();
621
622 let proc = Procedure::with_debug_info(
623 Runtime::from_raw_inc_rc(runtime),
624 env,
625 FuncPtr::User(fn_ptr),
626 num_required_args as usize,
627 variadic,
628 arc_from_ptr(debug_info),
629 );
630
631 Value::into_raw(Value::from(proc))
632 }
633}
634
635#[runtime_fn]
637unsafe extern "C" fn error_unbound_variable(symbol: u32) -> *const () {
638 let sym = Symbol(symbol);
639 let condition = Exception::error(format!("undefined variable {sym}"));
640 Value::into_raw(Value::from(condition))
641}
642
643#[runtime_fn]
644unsafe extern "C" fn add(vals: *const *const (), num_vals: u32, error: *mut Value) -> *const () {
645 unsafe {
646 let vals: Vec<_> = (0..num_vals)
647 .map(|i| Value::from_raw_inc_rc(vals.add(i as usize).read()))
649 .collect();
650 match num::add_prim(&vals) {
651 Ok(num) => Value::into_raw(Value::from(num)),
652 Err(condition) => {
653 error.write(condition.into());
654 Value::into_raw(Value::undefined())
655 }
656 }
657 }
658}
659
660#[runtime_fn]
661unsafe extern "C" fn sub(vals: *const *const (), num_vals: u32, error: *mut Value) -> *const () {
662 unsafe {
663 let vals: Vec<_> = (0..num_vals)
664 .map(|i| Value::from_raw_inc_rc(vals.add(i as usize).read()))
665 .collect();
666 match num::sub_prim(&vals[0], &vals[1..]) {
667 Ok(num) => Value::into_raw(Value::from(num)),
668 Err(condition) => {
669 error.write(condition.into());
670 Value::into_raw(Value::undefined())
671 }
672 }
673 }
674}
675
676#[runtime_fn]
677unsafe extern "C" fn mul(vals: *const *const (), num_vals: u32, error: *mut Value) -> *const () {
678 unsafe {
679 let vals: Vec<_> = (0..num_vals)
680 .map(|i| Value::from_raw_inc_rc(vals.add(i as usize).read()))
681 .collect();
682 match num::mul_prim(&vals) {
683 Ok(num) => Value::into_raw(Value::from(num)),
684 Err(condition) => {
685 error.write(condition.into());
686 Value::into_raw(Value::undefined())
687 }
688 }
689 }
690}
691
692#[runtime_fn]
693unsafe extern "C" fn div(vals: *const *const (), num_vals: u32, error: *mut Value) -> *const () {
694 unsafe {
695 let vals: Vec<_> = (0..num_vals)
696 .map(|i| Value::from_raw_inc_rc(vals.add(i as usize).read()))
697 .collect();
698 match num::div_prim(&vals[0], &vals[1..]) {
699 Ok(num) => Value::into_raw(Value::from(num)),
700 Err(condition) => {
701 error.write(condition.into());
702 Value::into_raw(Value::undefined())
703 }
704 }
705 }
706}
707
708macro_rules! define_comparison_fn {
709 ( $name:ident, $prim:ident ) => {
710 #[runtime_fn]
711 unsafe extern "C" fn $name(
712 vals: *const *const (),
713 num_vals: u32,
714 error: *mut Value,
715 ) -> *const () {
716 unsafe {
717 let vals: Vec<_> = (0..num_vals)
718 .map(|i| Value::from_raw_inc_rc(vals.add(i as usize).read()))
719 .collect();
720 match num::$prim(&vals) {
721 Ok(res) => Value::into_raw(Value::from(res)),
722 Err(condition) => {
723 error.write(condition.into());
724 Value::into_raw(Value::undefined())
725 }
726 }
727 }
728 }
729 };
730}
731
732define_comparison_fn!(equal, equal_prim);
733define_comparison_fn!(greater, greater_prim);
734define_comparison_fn!(greater_equal, greater_equal_prim);
735define_comparison_fn!(lesser, lesser_prim);
736define_comparison_fn!(lesser_equal, lesser_equal_prim);