• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 17772690385

16 Sep 2025 04:36PM UTC coverage: 43.331% (+0.04%) from 43.289%
17772690385

Pull #519

github

web-flow
Merge 98d4fd22c into 3c10433b9
Pull Request #519: fix a bunch more clippy lints

56 of 123 new or added lines in 30 files covered. (45.53%)

8 existing lines in 3 files now uncovered.

12416 of 28654 relevant lines covered (43.33%)

2985398.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.29
/crates/steel-core/src/steel_vm/engine.rs
1
#![allow(unused)]
2

3
use super::{
4
    builtin::{BuiltInModule, FunctionSignatureMetadata},
5
    primitives::{register_builtin_modules, CONSTANTS},
6
    vm::{threads::ThreadHandle, SteelThread, Synchronizer, ThreadStateController},
7
};
8

9
#[cfg(feature = "dylibs")]
10
use super::{ffi::FFIModule, ffi::FFIWrappedModule};
11

12
#[cfg(feature = "dylibs")]
13
use super::dylib::DylibContainers;
14

15
use crate::{
16
    compiler::{
17
        compiler::{Compiler, SerializableCompiler},
18
        map::SymbolMap,
19
        modules::{
20
            intern_modules, path_to_module_name, CompiledModule, SourceModuleResolver,
21
            MANGLER_PREFIX, PRELUDE_WITHOUT_BASE,
22
        },
23
        program::{Executable, RawProgramWithSymbols, SerializableRawProgramWithSymbols},
24
    },
25
    containers::RegisterValue,
26
    core::{
27
        instructions::{pretty_print_dense_instructions, DenseInstruction, Instruction},
28
        labels::Expr,
29
    },
30
    gc::{
31
        shared::StandardShared,
32
        unsafe_erased_pointers::{
33
            BorrowedObject, CustomReference, OpaqueReferenceNursery, ReadOnlyBorrowedObject,
34
            ReferenceMarker,
35
        },
36
        Gc, Shared,
37
    },
38
    parser::{
39
        ast::ExprKind,
40
        expander::SteelMacro,
41
        interner::{get_interner, take_interner, InternedString},
42
        kernel::{fresh_kernel_image, Kernel},
43
        parser::{ParseError, Parser, Sources, SYNTAX_OBJECT_ID},
44
    },
45
    rerrs::{back_trace, back_trace_to_string},
46
    rvals::{
47
        AsRefMutSteelVal, AsRefSteelVal as _, FromSteelVal, IntoSteelVal, MaybeSendSyncStatic,
48
        Result, SteelString, SteelVal,
49
    },
50
    steel_vm::register_fn::RegisterFn,
51
    stop, throw,
52
    values::{
53
        closed::GlobalSlotRecycler,
54
        functions::{BoxedDynFunction, ByteCodeLambda},
55
    },
56
    SteelErr,
57
};
58
use std::{
59
    borrow::Cow,
60
    cell::{Cell, RefCell},
61
    collections::{HashMap, HashSet},
62
    path::PathBuf,
63
    rc::Rc,
64
    sync::{
65
        atomic::{AtomicBool, AtomicUsize, Ordering},
66
        Arc, Mutex,
67
    },
68
};
69

70
use crate::values::HashMap as ImmutableHashMap;
71
use fxhash::{FxBuildHasher, FxHashMap};
72
use lasso::ThreadedRodeo;
73
use once_cell::sync::{Lazy, OnceCell};
74
use parking_lot::{
75
    MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
76
};
77
use serde::{Deserialize, Serialize};
78
use steel_gen::OpCode;
79
use steel_parser::{
80
    ast::List,
81
    parser::{SourceId, SyntaxObject},
82
    tokens::{IntLiteral, TokenType},
83
};
84

85
use crate::parser::ast::IteratorExtensions;
86

87
thread_local! {
88
    static KERNEL_BIN_FILE: Cell<Option<&'static [u8]>> = const { Cell::new(None) };
89
}
90

91
// Install the binary file to be used during bootup
92
// pub fn install_bin_file(bin: &'static [u8]) {
93
//     KERNEL_BIN_FILE.with(|x| x.set(Some(bin)));
94
// }
95

96
#[cfg(not(feature = "sync"))]
97
pub trait ModuleResolver {
98
    fn resolve(&self, name: &str) -> Option<BuiltInModule>;
99
    fn names(&self) -> Vec<String>;
100
}
101

102
#[cfg(feature = "sync")]
103
pub trait ModuleResolver: MaybeSendSyncStatic {
104
    fn resolve(&self, name: &str) -> Option<BuiltInModule>;
105
    fn names(&self) -> Vec<String>;
106
}
107

108
#[derive(Clone, Default)]
109
pub struct ModuleContainer {
110
    modules: Arc<RwLock<HashMap<Shared<str>, BuiltInModule>>>,
111
    // For modules that don't exist in memory. This could be useful for a world
112
    // in which a builtin module exists BUT we'd like to resolve the module for
113
    // inference purposes.
114
    unresolved_modules: Arc<RwLock<Option<StandardShared<dyn ModuleResolver>>>>,
115
}
116

117
impl ModuleContainer {
118
    pub fn with_expected_capacity() -> Self {
8✔
119
        Self {
120
            modules: Arc::new(RwLock::new(HashMap::with_capacity(48))),
24✔
121
            unresolved_modules: Default::default(),
8✔
122
        }
123
    }
124

125
    pub fn insert(&mut self, key: Shared<str>, value: BuiltInModule) {
323✔
126
        self.modules.write().insert(key, value);
969✔
127
    }
128

129
    pub fn get_doc(&self, key: &str) -> Option<String> {
×
130
        for module in self.modules.read().values() {
×
131
            let maybe_doc = module.get_documentation(key);
×
132

133
            if maybe_doc.is_some() {
×
134
                return maybe_doc;
×
135
            }
136
        }
137

138
        None
×
139
    }
140

141
    pub fn get_metadata_by_name(&self, key: &str) -> Option<FunctionSignatureMetadata> {
×
142
        for module in self.modules.read().values() {
×
143
            let maybe_meta = module.search_by_name(key);
×
144

145
            if maybe_meta.is_some() {
×
146
                return maybe_meta;
×
147
            }
148
        }
149

150
        None
×
151
    }
152

153
    pub fn get(&self, key: &str) -> Option<BuiltInModule> {
1,490✔
154
        let mut guard = self.modules.write();
2,980✔
155
        let found = guard.get(key).cloned();
5,960✔
156

157
        found.or_else(|| {
2,980✔
158
            let res = self
×
159
                .unresolved_modules
×
160
                .read()
×
161
                .as_ref()
×
162
                .and_then(|x| x.resolve(key));
×
163

164
            if let Some(res) = res {
×
165
                // Define the module now.
166
                guard.insert(Shared::from(key), res.clone());
167

168
                Some(res)
169
            } else {
170
                None
×
171
            }
172
        })
173
    }
174

175
    pub fn inner(&self) -> RwLockReadGuard<'_, HashMap<Shared<str>, BuiltInModule>> {
×
176
        self.modules.read()
×
177
    }
178

179
    pub(crate) fn inner_mut(
1✔
180
        &mut self,
181
    ) -> RwLockWriteGuard<'_, HashMap<Shared<str>, BuiltInModule>> {
182
        self.modules.write()
1✔
183
    }
184

185
    pub fn with_resolver<T: ModuleResolver + 'static>(&mut self, resolver: T) {
×
186
        *self.unresolved_modules.write() = Some(StandardShared::new(resolver));
×
187
    }
188
}
189

190
#[derive(Debug)]
191
pub struct EngineStatistics {
192
    pub rooted_count: usize,
193
    pub constants_count: usize,
194
    pub sources_size: usize,
195
}
196

197
#[derive(Debug, Clone, Copy)]
198
pub struct GlobalCheckpoint {
199
    symbol_map_offset: usize,
200
    globals_offset: usize,
201
}
202

203
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
204
pub struct EngineId(pub(crate) usize);
205

206
impl EngineId {
207
    pub fn new() -> Self {
784✔
208
        static ENGINE_ID: AtomicUsize = AtomicUsize::new(0);
209
        let id = ENGINE_ID.fetch_add(1, Ordering::Relaxed);
3,136✔
210
        Self(id)
784✔
211
    }
212

213
    pub fn as_usize(self) -> usize {
×
214
        self.0
×
215
    }
216
}
217

218
/// Handle to a steel engine. This contains a main entrypoint thread, alongside
219
/// the compiler and all of the state necessary to keep a VM instance alive and
220
/// well.
221
pub struct Engine {
222
    pub(crate) virtual_machine: SteelThread,
223
    // TODO: Just put this, and all the other things,
224
    // inside the `SteelThread` - The compiler probably
225
    // still... needs to be shared, but thats fine.
226
    // pub(crate) compiler: Arc<RwLock<Compiler>>,
227
    modules: ModuleContainer,
228
    #[cfg(feature = "dylibs")]
229
    dylibs: DylibContainers,
230
    pub(crate) id: EngineId,
231
}
232

233
impl Engine {
234
    pub fn enter_safepoint<T, F: FnMut() -> T>(&mut self, mut thunk: F) -> T {
×
235
        let mut res = None;
×
236

237
        self.virtual_machine.enter_safepoint(|_| {
×
238
            res = Some((thunk)());
×
239
            Ok(SteelVal::Void)
×
240
        });
241

242
        res.unwrap()
×
243
    }
244
}
245

246
impl Clone for Engine {
247
    fn clone(&self) -> Self {
756✔
248
        let mut virtual_machine = self.virtual_machine.clone();
2,268✔
249

250
        // Set up child
251
        virtual_machine.synchronizer = Synchronizer::new();
1,512✔
252

253
        let weak_ctx = Arc::downgrade(&virtual_machine.synchronizer.ctx);
2,268✔
254

255
        // Get a handle to the current thread?
256
        let handle = ThreadHandle {
257
            handle: Mutex::new(None),
2,268✔
258
            thread: std::thread::current(),
1,512✔
259
            thread_state_manager: virtual_machine.synchronizer.state.clone(),
1,512✔
260
            forked_thread_handle: None,
261
        }
262
        .into_steelval()
263
        .unwrap();
264

265
        // TODO: Entering safepoint should happen often
266
        // for the main thread?
267
        virtual_machine
1,512✔
268
            .synchronizer
1,512✔
269
            .threads
1,512✔
270
            .lock()
271
            .unwrap()
272
            .push(super::vm::ThreadContext {
1,512✔
273
                ctx: weak_ctx,
756✔
274
                handle,
756✔
275
            });
276

277
        let compiler = Arc::new(RwLock::new(self.virtual_machine.compiler.write().clone()));
3,024✔
278

279
        // virtual_machine.compiler = Some(Arc::downgrade(&compiler));
280

281
        Self {
282
            virtual_machine,
283
            // compiler,
284
            modules: self.modules.clone(),
2,268✔
285
            #[cfg(feature = "dylibs")]
286
            dylibs: self.dylibs.clone(),
1,512✔
287
            id: EngineId::new(),
756✔
288
        }
289
    }
290
}
291

292
impl Default for Engine {
293
    fn default() -> Self {
×
294
        Self::new()
×
295
    }
296
}
297

298
// Pre-parsed ASTs along with the global state to set before we start any further processing
299
// #[derive(Serialize, Deserialize)]
300
// struct BootstrapImage {
301
//     interner: Arc<ThreadedRodeo<lasso::Spur, FxBuildHasher>>,
302
//     syntax_object_id: usize,
303
//     sources: Sources,
304
//     programs: Vec<Vec<ExprKind>>,
305
// }
306

307
// Pre compiled programs along with the global state to set before we start any further processing
308
struct StartupBootstrapImage {
309
    syntax_object_id: usize,
310
    function_id: usize,
311
    sources: Sources,
312
    pre_kernel_programs: Vec<SerializableRawProgramWithSymbols>,
313
    post_kernel_programs: Vec<SerializableRawProgramWithSymbols>,
314
    kernel: Option<KernelImage>,
315
    compiler: Option<SerializableCompiler>,
316
}
317

318
struct KernelImage {
319
    // Kernel macros
320
    compiler: SerializableCompiler,
321
    sources: Sources,
322
    kernel_source: SerializableRawProgramWithSymbols,
323
}
324

325
#[derive(Serialize, Deserialize)]
326
pub struct NonInteractiveProgramImage {
327
    sources: Sources,
328
    program: SerializableRawProgramWithSymbols,
329
}
330

331
impl NonInteractiveProgramImage {
332
    pub fn write_bytes_to_file(&self, out: &PathBuf) {
×
333
        let mut f = std::fs::File::create(out).unwrap();
×
334
        bincode::serialize_into(&mut f, self).unwrap();
×
335
    }
336

337
    pub fn as_bytes(&self) -> Vec<u8> {
×
338
        let mut out = Vec::new();
×
339
        bincode::serialize_into(&mut out, self).unwrap();
×
340
        out
×
341
    }
342

343
    pub fn from_bytes(bytes: &[u8]) -> Self {
×
NEW
344
        bincode::deserialize(bytes).unwrap()
×
345
    }
346
}
347

348
// fn steel_create_bootstrap() {
349
//     Engine::create_bootstrap_from_programs("src/boot/bootstrap.bin".into());
350
// }
351

352
pub struct LifetimeGuard<'a> {
353
    engine: &'a mut Engine,
354
}
355

356
impl<'a> Drop for LifetimeGuard<'a> {
357
    fn drop(&mut self) {
3✔
358
        crate::gc::unsafe_erased_pointers::OpaqueReferenceNursery::free_all();
3✔
359
    }
360
}
361

362
impl<'a> LifetimeGuard<'a> {
363
    pub fn with_immutable_reference<
×
364
        'b: 'a,
365
        T: CustomReference + ReferenceMarker<'b, Static = EXT> + 'b,
366
        EXT: CustomReference + 'static,
367
    >(
368
        self,
369
        obj: &'a T,
370
    ) -> Self {
371
        assert_eq!(
×
372
            crate::gc::unsafe_erased_pointers::type_id::<T>(),
×
373
            std::any::TypeId::of::<EXT>()
×
374
        );
375

376
        crate::gc::unsafe_erased_pointers::OpaqueReferenceNursery::allocate_ro_object::<T, EXT>(
377
            obj,
×
378
        );
379

380
        self
×
381
    }
382

383
    pub fn with_mut_reference<
×
384
        'b: 'a,
385
        T: CustomReference + ReferenceMarker<'b, Static = EXT> + 'b,
386
        EXT: CustomReference + 'static,
387
    >(
388
        self,
389
        obj: &'a mut T,
390
    ) -> Self {
391
        assert_eq!(
×
392
            crate::gc::unsafe_erased_pointers::type_id::<T>(),
×
393
            std::any::TypeId::of::<EXT>()
×
394
        );
395
        crate::gc::unsafe_erased_pointers::OpaqueReferenceNursery::allocate_rw_object::<T, EXT>(
396
            obj,
×
397
        );
398

399
        self
×
400
    }
401

402
    pub fn consume<T>(self, mut thunk: impl FnMut(&mut Engine, Vec<SteelVal>) -> T) -> T {
3✔
403
        let values =
3✔
404
            crate::gc::unsafe_erased_pointers::OpaqueReferenceNursery::drain_weak_references_to_steelvals();
3✔
405

406
        thunk(self.engine, values)
6✔
407
    }
408

409
    pub fn consume_once<T>(self, mut thunk: impl FnOnce(&mut Engine, Vec<SteelVal>) -> T) -> T {
×
410
        let values =
×
411
            crate::gc::unsafe_erased_pointers::OpaqueReferenceNursery::drain_weak_references_to_steelvals();
×
412

413
        thunk(self.engine, values)
×
414
    }
415
}
416

417
impl RegisterValue for Engine {
418
    fn register_value_inner(&mut self, name: &str, value: SteelVal) -> &mut Self {
948✔
419
        let idx = self.virtual_machine.compiler.write().register(name);
2,844✔
420
        self.virtual_machine.insert_binding(idx, value);
3,792✔
421
        self
948✔
422
    }
423
}
424

425
#[steel_derive::function(name = "#%get-dylib")]
426
pub fn load_module_noop(target: &crate::rvals::SteelString) -> crate::rvals::Result<SteelVal> {
×
427
    stop!(Generic => "This engine has not been given the capability to load dylibs")
×
428
}
429

430
macro_rules! time {
431
    ($target:expr, $label:expr, $e:expr) => {{
432
        #[cfg(feature = "profiling")]
433
        let now = std::time::Instant::now();
434

435
        let e = $e;
436

437
        #[cfg(feature = "profiling")]
438
        log::debug!(target: $target, "{}: {:?}", $label, now.elapsed());
439

440
        e
441
    }};
442
}
443

444
static STATIC_DEFAULT_PRELUDE_MACROS: OnceCell<Arc<FxHashMap<InternedString, SteelMacro>>> =
445
    OnceCell::new();
446

447
static STATIC_DEFAULT_PRELUDE_MACROS_SANDBOX: OnceCell<Arc<FxHashMap<InternedString, SteelMacro>>> =
448
    OnceCell::new();
449

450
pub(crate) fn set_default_prelude_macros(
8✔
451
    prelude_macros: FxHashMap<InternedString, SteelMacro>,
452
    sandbox: bool,
453
) {
454
    if cfg!(feature = "sync") {
455
        if sandbox {
12✔
456
            STATIC_DEFAULT_PRELUDE_MACROS_SANDBOX
8✔
457
                .set(Arc::new(prelude_macros))
12✔
458
                .unwrap();
459
        } else {
460
            STATIC_DEFAULT_PRELUDE_MACROS
4✔
461
                .set(Arc::new(prelude_macros))
4✔
462
                .unwrap();
463
        }
464
    } else {
465
        DEFAULT_PRELUDE_MACROS.with(|x| {
×
466
            let mut guard = x.borrow_mut();
×
467
            *guard = Arc::new(prelude_macros);
×
468
        })
469
    }
470
}
471

472
pub(crate) fn default_prelude_macros() -> Arc<FxHashMap<InternedString, SteelMacro>> {
13,101✔
473
    if cfg!(feature = "sync") {
474
        STATIC_DEFAULT_PRELUDE_MACROS.get().cloned().unwrap_or(
52,404✔
475
            STATIC_DEFAULT_PRELUDE_MACROS_SANDBOX
13,101✔
476
                .get()
13,101✔
477
                .cloned()
13,101✔
478
                .unwrap_or_default(),
13,101✔
479
        )
480
    } else {
481
        DEFAULT_PRELUDE_MACROS.with(|x| x.borrow().clone())
×
482
    }
483
}
484

485
thread_local! {
486
    // TODO: Replace this with a once cell?
487
    pub(crate) static DEFAULT_PRELUDE_MACROS: RefCell<Arc<FxHashMap<InternedString, SteelMacro>>> = RefCell::new(Arc::new(HashMap::default()));
488
}
489

490
impl Engine {
491
    pub fn engine_id(&self) -> EngineId {
×
492
        self.virtual_machine.id
×
493
    }
494

495
    #[cfg(not(feature = "sync"))]
496
    pub(crate) fn deep_clone(&self) -> Self {
497
        let mut engine = self.clone();
498

499
        let compiler_copy = engine.virtual_machine.compiler.read().clone();
500
        engine.virtual_machine.compiler = Arc::new(RwLock::new(compiler_copy));
501

502
        let constant_map = engine
503
            .virtual_machine
504
            .compiler
505
            .read()
506
            .constant_map
507
            .deep_clone();
508
        engine.virtual_machine.compiler.write().constant_map = constant_map;
509

510
        let heap_copy = Arc::new(Mutex::new(
511
            engine.virtual_machine.heap.lock().unwrap().clone(),
512
        ));
513

514
        engine.virtual_machine.heap = heap_copy;
515
        engine
516
    }
517

518
    #[cfg(feature = "sync")]
519
    pub(crate) fn deep_clone(&self) -> Self {
378✔
520
        let mut engine = self.clone();
1,134✔
521
        engine.virtual_machine.global_env = engine.virtual_machine.global_env.deep_clone();
1,134✔
522

523
        let mut compiler_copy = engine.virtual_machine.compiler.read().clone();
756✔
524

525
        engine.virtual_machine.compiler = Arc::new(RwLock::new(compiler_copy));
1,512✔
526

527
        let constant_map = engine
756✔
528
            .virtual_machine
378✔
529
            .compiler
378✔
530
            .read()
378✔
531
            .constant_map
378✔
532
            .deep_clone();
533
        engine.virtual_machine.compiler.write().constant_map = constant_map;
756✔
534

535
        let heap_copy = Arc::new(Mutex::new(
1,134✔
536
            engine.virtual_machine.heap.lock().unwrap().clone(),
378✔
537
        ));
538

539
        engine.virtual_machine.heap = heap_copy;
756✔
540
        engine
378✔
541
    }
542

543
    /// Function to access a kernel level execution environment
544
    /// Has access to primitives and syntax rules, but will not defer to a child
545
    /// kernel in the compiler
546
    pub(crate) fn new_kernel(sandbox: bool) -> Self {
8✔
547
        log::debug!(target:"kernel", "Instantiating a new kernel");
8✔
548
        #[cfg(feature = "profiling")]
549
        let mut total_time = std::time::Instant::now();
550
        #[cfg(feature = "profiling")]
551
        let mut now = std::time::Instant::now();
552
        let sources = Sources::new();
16✔
553
        let modules = ModuleContainer::with_expected_capacity();
16✔
554

555
        let compiler = Arc::new(RwLock::new(Compiler::default_without_kernel(
32✔
556
            sources,
8✔
557
            modules.clone(),
16✔
558
        )));
559

560
        let mut vm = Engine {
561
            virtual_machine: SteelThread::new(compiler),
24✔
562
            modules,
563
            #[cfg(feature = "dylibs")]
564
            dylibs: DylibContainers::new(),
8✔
565
            id: EngineId::new(),
8✔
566
        };
567

568
        time!(
8✔
569
            "engine-creation",
570
            "Registering builtin modules",
571
            register_builtin_modules(&mut vm, sandbox)
24✔
572
        );
573

574
        time!(
8✔
575
            "engine-creation",
576
            "Loading the ALL_MODULES prelude code",
577
            vm.compile_and_run_raw_program(crate::steel_vm::primitives::ALL_MODULES)
24✔
578
                .expect("loading ALL_MODULES failed")
16✔
579
        );
580

581
        // log::debug!(target: "kernel", "Registered modules in the kernel!: {:?}", now.elapsed());
582

583
        #[cfg(feature = "profiling")]
584
        let mut now = std::time::Instant::now();
585

586
        let core_libraries = [crate::stdlib::PRELUDE];
16✔
587

588
        for core in core_libraries.into_iter() {
24✔
589
            if let Err(e) = vm.compile_and_run_raw_program(core) {
16✔
590
                vm.raise_error(e);
591
                panic!("Loading the standard library failed");
592
            }
593
        }
594

595
        // Initialize the global macro environment with the default one. This way
596
        // values won't leak when top level macros are defined - and modules can clone from
597
        // this to begin seeding their environment.
598

599
        set_default_prelude_macros(vm.in_scope_macros().clone(), sandbox);
8✔
600

601
        #[cfg(feature = "profiling")]
602
        log::debug!(target: "kernel", "Loaded prelude in the kernel!: {:?}", now.elapsed());
603

604
        #[cfg(feature = "profiling")]
605
        log::debug!(target: "pipeline_time", "Total kernel loading time: {:?}", total_time.elapsed());
606

607
        vm
608
    }
609

610
    /// Registers a source module resolvers. This differs from `register_module_resolver` which deals
611
    /// specifically with builtin modules, e.g. `BuiltInModule`. This will allow you to embed
612
    /// source modules that can be referenced via `(require ...)` at the scheme level.
613
    ///
614
    /// This is a more flexible version of the api provided by `register_steel_module`, which allows
615
    /// you to directly register a module with the name, and the associated text for that module.
616
    pub fn register_source_module_resolver<T: SourceModuleResolver + 'static>(
×
617
        &mut self,
618
        resolver: T,
619
    ) {
620
        self.virtual_machine
×
621
            .compiler
×
622
            .write()
623
            .register_source_module_resolver(resolver);
×
624
    }
625

626
    /// Register a module resolver. This is used for creating references to modules
627
    /// that don't exist within the compiler. Without this, you wouldn't be able to
628
    /// pre-compile or analyze code that is run within another host application, without
629
    /// exposing some kind of compiler from that hosts runtime. The requirement then
630
    /// is to expose some kind of module artifact that we can then consume.
631
    pub fn register_module_resolver<T: ModuleResolver + 'static>(&mut self, resolver: T) {
×
632
        for name in resolver.names() {
×
633
            self.register_value(&format!("%-builtin-module-{}", name), SteelVal::Void);
×
634
        }
635

636
        self.modules.with_resolver(resolver);
×
637
    }
638

639
    pub fn builtin_modules(&self) -> &ModuleContainer {
×
640
        &self.modules
×
641
    }
642

643
    #[doc(hidden)]
644
    pub fn disallow_dylib_loading(&mut self) -> &mut Self {
1✔
645
        let mut module = self.modules.inner_mut();
3✔
646

647
        // TODO: This should actually just clone the whole module, and then add this definition
648
        // in. That way it has its own unique module loader.
649
        if let Some(builtin_module) = module.get_mut("steel/meta") {
3✔
650
            builtin_module.register_native_fn_definition(LOAD_MODULE_NOOP_DEFINITION);
651
        }
652

653
        drop(module);
2✔
654

655
        self
1✔
656
    }
657

658
    /// Function to access a kernel level execution environment
659
    /// Has access to primitives and syntax rules, but will not defer to a child
660
    /// kernel in the compiler
661
    pub(crate) fn new_bootstrap_kernel(sandbox: bool) -> Self {
8✔
662
        // If the interner has already been initialized, it most likely means that either:
663
        // 1) Tests are being run
664
        // 2) The parser was used in a standalone fashion, somewhere, which invalidates the bootstrap
665
        //    process
666
        //
667
        // There are a few solutions to this - one would probably be to not use a static interner,
668
        // however given that its a huge chore to pass around the interner everywhere there are strings,
669
        // its probably inevitable we have that.
670
        if get_interner().is_some() {
16✔
671
            return Engine::new_kernel(sandbox);
10✔
672
        }
673

674
        intern_modules();
675

676
        if matches!(option_env!("STEEL_BOOTSTRAP"), Some("false") | None) {
×
677
            let mut vm = Engine::new_kernel(sandbox);
678

679
            // let sources = vm.sources.clone();
680

681
            // vm.register_fn("report-error!", move |error: SteelErr| {
682
            //     raise_error(&sources, error);
683
            // });
684

685
            return vm;
686
        }
687

688
        log::debug!(target:"kernel", "Instantiating a new kernel");
×
689

690
        // let sources = Sources::new();
691
        // let modules = ModuleContainer::default();
692

693
        // let compiler = Arc::new(RwLock::new(Compiler::default_without_kernel(
694
        //     sources.clone(),
695
        //     modules.clone(),
696
        // )));
697

698
        // // TODO: Pass compiler down if we want eval!
699
        // let mut vm = Engine {
700
        //     virtual_machine: SteelThread::new(sources.clone(), compiler),
701
        //     modules,
702
        //     sources,
703
        //     #[cfg(feature = "dylibs")]
704
        //     dylibs: DylibContainers::new(),
705
        //     id: EngineId::new(),
706
        // };
707

708
        // if let Some(programs) = Engine::load_from_bootstrap(&mut vm) {
709
        //     register_builtin_modules(&mut vm, sandbox);
710

711
        //     for program in programs {
712
        //         vm.compiler.write().constant_map = program.constant_map.clone();
713
        //         vm.virtual_machine.constant_map = program.constant_map.clone();
714

715
        //         vm.run_raw_program(program).unwrap();
716
        //     }
717

718
        //     log::debug!(target: "kernel", "Loaded prelude in the kernel!");
719

720
        //     let sources = vm.sources.clone();
721

722
        //     vm.register_fn("report-error!", move |error: SteelErr| {
723
        //         raise_error(&sources, error);
724
        //     });
725

726
        //     vm
727
        // } else {
728
        let mut vm = Engine::new_kernel(sandbox);
×
729

730
        // let sources = vm.sources.clone();
731

732
        // vm.register_fn("report-error!", move |error: SteelErr| {
733
        //     raise_error(&sources, error);
734
        // });
735

736
        vm
×
737
        // }
738
    }
739

740
    // fn load_from_bootstrap(vm: &mut Engine) -> Option<Vec<RawProgramWithSymbols>> {
741
    //     if matches!(option_env!("STEEL_BOOTSTRAP"), Some("false") | None) {
742
    //         return None;
743
    //     } else {
744
    //         println!("LOADING A KERNEL FROM THE BIN FILE");
745
    //     }
746

747
    //     let bootstrap: StartupBootstrapImage =
748
    //         bincode::deserialize(KERNEL_BIN_FILE.with(|x| x.get())?).unwrap();
749

750
    //     // Set the syntax object id to be AFTER the previous items have been parsed
751
    //     SYNTAX_OBJECT_ID.store(
752
    //         bootstrap.syntax_object_id,
753
    //         std::sync::atomic::Ordering::Relaxed,
754
    //     );
755

756
    //     crate::compiler::code_gen::FUNCTION_ID
757
    //         .store(bootstrap.function_id, std::sync::atomic::Ordering::Relaxed);
758

759
    //     vm.sources = bootstrap.sources;
760
    //     // vm.compiler.macro_env = bootstrap.macros;
761

762
    //     todo!();
763

764
    //     Some(
765
    //         bootstrap
766
    //             .pre_kernel_programs
767
    //             .into_iter()
768
    //             .map(SerializableRawProgramWithSymbols::into_raw_program)
769
    //             .collect(),
770
    //     )
771
    // }
772

773
    /// Creates a statically linked program ready to deserialize
774
    pub fn create_non_interactive_program_image<E: AsRef<str> + Into<Cow<'static, str>>>(
×
775
        expr: E,
776
        path: PathBuf,
777
    ) -> Result<NonInteractiveProgramImage> {
778
        let mut engine = Engine::new();
×
779

780
        engine
×
781
            .emit_raw_program(expr, path)?
×
782
            .into_serializable_program()
783
            .map(|program| NonInteractiveProgramImage {
×
784
                sources: engine.virtual_machine.compiler.read().sources.clone(),
×
785
                program,
×
786
            })
787
    }
788

789
    // Execute from a statically linked non interactive program
790
    pub fn execute_non_interactive_program_image(program: &'static [u8]) -> Result<()> {
×
791
        // This _has_ to match the as the creation of the program above.
792
        // So, engine first, then non interactive program.
793
        let mut engine = Engine::new();
×
794
        let program = crate::steel_vm::engine::NonInteractiveProgramImage::from_bytes(program);
×
795

796
        engine.virtual_machine.compiler.write().sources = program.sources;
×
797

798
        // TODO: The constant map needs to be brought back as well. Install it here.
799
        // it needs to get installed in the VM and the compiler. Lets just try that now.
800

801
        let raw_program = SerializableRawProgramWithSymbols::into_raw_program(program.program);
×
802

803
        engine.virtual_machine.constant_map = raw_program.constant_map.clone();
×
804
        engine.virtual_machine.compiler.write().constant_map = raw_program.constant_map.clone();
×
805

806
        let results = engine.run_raw_program(raw_program);
×
807

808
        if let Err(e) = results {
×
809
            raise_error(&engine.virtual_machine.compiler.read().sources, e);
×
810
        }
811

812
        Ok(())
×
813
    }
814

815
    // Create kernel bootstrap
816
    // pub fn create_kernel_bootstrap_from_programs(output_path: PathBuf) {
817
    //     let sources = Sources::new();
818

819
    //     let mut vm = Engine {
820
    //         virtual_machine: SteelThread::new(sources.clone()),
821
    //         compiler: Arc::new(RwLock::new(Compiler::default())),
822
    //         constants: None,
823
    //         modules: ModuleContainer::default(),
824
    //         sources,
825
    //         #[cfg(feature = "dylibs")]
826
    //         dylibs: DylibContainers::new(),
827
    //         id: EngineId::new(),
828
    //     };
829

830
    //     register_builtin_modules(&mut vm, false);
831

832
    //     let mut programs = Vec::new();
833

834
    //     let bootstrap_sources = [
835
    //         crate::steel_vm::primitives::ALL_MODULES,
836
    //         crate::stdlib::PRELUDE,
837
    //     ];
838

839
    //     for source in bootstrap_sources {
840
    //         let raw_program = vm.emit_raw_program_no_path(source).unwrap();
841
    //         programs.push(raw_program.clone());
842
    //         vm.run_raw_program(raw_program).unwrap();
843
    //     }
844

845
    //     // Grab the last value of the offset
846
    //     let syntax_object_id = SYNTAX_OBJECT_ID.load(std::sync::atomic::Ordering::Relaxed);
847
    //     let function_id =
848
    //         crate::compiler::code_gen::FUNCTION_ID.load(std::sync::atomic::Ordering::Relaxed);
849

850
    //     let bootstrap = StartupBootstrapImage {
851
    //         syntax_object_id,
852
    //         function_id,
853
    //         sources: vm.sources,
854
    //         pre_kernel_programs: programs
855
    //             .into_iter()
856
    //             .map(RawProgramWithSymbols::into_serializable_program)
857
    //             .collect::<Result<_>>()
858
    //             .unwrap(),
859
    //         // macros: vm.compiler.macro_env,
860
    //         post_kernel_programs: Vec::new(),
861
    //         kernel: None,
862
    //         compiler: None,
863
    //     };
864

865
    //     // Encode to something implementing `Write`
866
    //     let mut f = std::fs::File::create(output_path).unwrap();
867
    //     bincode::serialize_into(&mut f, &bootstrap).unwrap();
868
    // }
869

870
    // pub fn create_new_engine_from_bootstrap(output_path: PathBuf) {
871
    //     let sources = Sources::new();
872
    //     let mut vm = Engine {
873
    //         virtual_machine: SteelThread::new(sources.clone()),
874
    //         compiler: Arc::new(RwLock::new(Compiler::default())),
875
    //         constants: None,
876
    //         modules: ModuleContainer::default(),
877
    //         sources,
878
    //         #[cfg(feature = "dylibs")]
879
    //         dylibs: DylibContainers::new(),
880
    //         id: EngineId::new(),
881
    //     };
882

883
    //     register_builtin_modules(&mut vm, false);
884

885
    //     let mut pre_kernel_programs = Vec::new();
886

887
    //     let bootstrap_sources = [
888
    //         crate::steel_vm::primitives::ALL_MODULES,
889
    //         crate::stdlib::PRELUDE,
890
    //     ];
891

892
    //     for source in bootstrap_sources {
893
    //         let raw_program = vm.emit_raw_program_no_path(source).unwrap();
894
    //         pre_kernel_programs.push(raw_program.clone());
895
    //         vm.run_raw_program(raw_program).unwrap();
896
    //     }
897

898
    //     // This will be our new top level engine
899
    //     let mut top_level_engine = vm.clone();
900

901
    //     let sources = vm.sources.clone();
902

903
    //     vm.register_fn("report-error!", move |error: SteelErr| {
904
    //         raise_error(&sources, error);
905
    //     });
906

907
    //     let (kernel, kernel_program) = Kernel::bootstrap(vm);
908

909
    //     // Create kernel for the compiler for the top level vm
910
    //     top_level_engine.compiler.write().kernel = Some(kernel);
911

912
    //     let builtin_modules =
913
    //         ["(require \"#%private/steel/contract\" (for-syntax \"#%private/steel/contract\"))"];
914

915
    //     let mut post_kernel_programs = Vec::new();
916

917
    //     for source in builtin_modules {
918
    //         let raw_program = top_level_engine.emit_raw_program_no_path(source).unwrap();
919
    //         post_kernel_programs.push(raw_program.clone());
920
    //         top_level_engine.run_raw_program(raw_program).unwrap();
921
    //     }
922

923
    //     // Grab the last value of the offset
924
    //     let syntax_object_id = SYNTAX_OBJECT_ID.load(std::sync::atomic::Ordering::Relaxed);
925
    //     let function_id =
926
    //         crate::compiler::code_gen::FUNCTION_ID.load(std::sync::atomic::Ordering::Relaxed);
927

928
    //     let kernel_sources = top_level_engine
929
    //         .compiler
930
    //         .write()
931
    //         .kernel
932
    //         .as_ref()
933
    //         .unwrap()
934
    //         .engine
935
    //         .sources
936
    //         .clone();
937
    //     let bootstrap = StartupBootstrapImage {
938
    //         syntax_object_id,
939
    //         function_id,
940
    //         sources: top_level_engine.sources,
941
    //         pre_kernel_programs: pre_kernel_programs
942
    //             .into_iter()
943
    //             .map(RawProgramWithSymbols::into_serializable_program)
944
    //             .collect::<Result<_>>()
945
    //             .unwrap(),
946
    //         post_kernel_programs: post_kernel_programs
947
    //             .into_iter()
948
    //             .map(RawProgramWithSymbols::into_serializable_program)
949
    //             .collect::<Result<_>>()
950
    //             .unwrap(),
951
    //         kernel: Some(KernelImage {
952
    //             compiler: top_level_engine
953
    //                 .compiler
954
    //                 .write()
955
    //                 .kernel
956
    //                 .take()
957
    //                 .unwrap()
958
    //                 .engine
959
    //                 .compiler
960
    //                 .write()
961
    //                 .into_serializable_compiler()
962
    //                 .unwrap(),
963
    //             sources: kernel_sources,
964
    //             kernel_source: kernel_program.into_serializable_program().unwrap(),
965
    //         }),
966
    //         compiler: Some(
967
    //             top_level_engine
968
    //                 .compiler
969
    //                 .write()
970
    //                 .into_serializable_compiler()
971
    //                 .unwrap(),
972
    //         ),
973
    //     };
974

975
    //     // Encode to something implementing `Write`
976
    //     let mut f = std::fs::File::create(output_path).unwrap();
977
    //     bincode::serialize_into(&mut f, &bootstrap).unwrap();
978
    // }
979

980
    // pub fn top_level_load_from_bootstrap(bin: &[u8]) -> Engine {
981
    //     let bootstrap: StartupBootstrapImage = bincode::deserialize(bin).unwrap();
982

983
    //     let sources = Sources::new();
984
    //     // This is going to be the kernel
985
    //     let mut vm = Engine {
986
    //         virtual_machine: SteelThread::new(sources.clone()),
987
    //         compiler: Arc::new(RwLock::new(Compiler::default())),
988
    //         constants: None,
989
    //         modules: ModuleContainer::default(),
990
    //         sources,
991
    //         #[cfg(feature = "dylibs")]
992
    //         dylibs: DylibContainers::new(),
993
    //         id: EngineId::new(),
994
    //     };
995

996
    //     // Register the modules
997
    //     register_builtin_modules(&mut vm, false);
998

999
    //     // Set the syntax object id to be AFTER the previous items have been parsed
1000
    //     SYNTAX_OBJECT_ID.store(
1001
    //         bootstrap.syntax_object_id,
1002
    //         std::sync::atomic::Ordering::Relaxed,
1003
    //     );
1004

1005
    //     crate::compiler::code_gen::FUNCTION_ID
1006
    //         .store(bootstrap.function_id, std::sync::atomic::Ordering::Relaxed);
1007

1008
    //     let bootstrap_kernel = bootstrap.kernel.unwrap();
1009

1010
    //     vm.sources = bootstrap_kernel.sources;
1011
    //     vm.compiler = Arc::new(RwLock::new(bootstrap_kernel.compiler.into_compiler()));
1012

1013
    //     // TODO: Only need to bring around the last constant map
1014
    //     for program in bootstrap
1015
    //         .pre_kernel_programs
1016
    //         .into_iter()
1017
    //         .map(SerializableRawProgramWithSymbols::into_raw_program)
1018
    //     {
1019
    //         vm.compiler.write().constant_map = program.constant_map.clone();
1020
    //         vm.virtual_machine.constant_map = program.constant_map.clone();
1021

1022
    //         vm.run_raw_program(program).unwrap();
1023
    //     }
1024

1025
    //     log::debug!(target: "kernel", "Loaded prelude in the kernel!");
1026

1027
    //     let sources = vm.sources.clone();
1028

1029
    //     vm.register_fn("report-error!", move |error: SteelErr| {
1030
    //         raise_error(&sources, error);
1031
    //     });
1032

1033
    //     // Now we're going to set up the top level environment
1034
    //     let mut kernel = Kernel::initialize_post_bootstrap(vm.clone());
1035

1036
    //     kernel
1037
    //         .engine
1038
    //         .run_raw_program(bootstrap_kernel.kernel_source.into_raw_program())
1039
    //         .unwrap();
1040

1041
    //     vm.sources = bootstrap.sources;
1042
    //     vm.compiler = Arc::new(RwLock::new(bootstrap.compiler.unwrap().into_compiler()));
1043
    //     vm.compiler.write().kernel = Some(kernel);
1044

1045
    //     for program in bootstrap
1046
    //         .post_kernel_programs
1047
    //         .into_iter()
1048
    //         .map(SerializableRawProgramWithSymbols::into_raw_program)
1049
    //     {
1050
    //         vm.compiler.write().constant_map = program.constant_map.clone();
1051
    //         vm.virtual_machine.constant_map = program.constant_map.clone();
1052

1053
    //         vm.run_raw_program(program).unwrap();
1054
    //     }
1055

1056
    //     vm
1057
    // }
1058

1059
    // fn create_bootstrap() {
1060
    //     let sources = Sources::new();
1061

1062
    //     let mut vm = Engine {
1063
    //         virtual_machine: SteelThread::new(sources.clone()),
1064
    //         compiler: Arc::new(RwLock::new(Compiler::default())),
1065
    //         constants: None,
1066
    //         modules: ModuleContainer::default(),
1067
    //         sources,
1068
    //         #[cfg(feature = "dylibs")]
1069
    //         dylibs: DylibContainers::new(),
1070
    //         id: EngineId::new(),
1071
    //     };
1072

1073
    //     register_builtin_modules(&mut vm, false);
1074

1075
    //     let mut asts = Vec::new();
1076

1077
    //     let bootstrap_sources = [
1078
    //         crate::steel_vm::primitives::ALL_MODULES,
1079
    //         crate::stdlib::PRELUDE,
1080
    //     ];
1081

1082
    //     for source in bootstrap_sources {
1083
    //         let id = vm.sources.add_source(source.to_string(), None);
1084

1085
    //         // Could fail here
1086
    //         let parsed: Vec<ExprKind> = Parser::new(source, Some(id))
1087
    //             .collect::<std::result::Result<_, _>>()
1088
    //             .unwrap();
1089

1090
    //         asts.push(parsed.clone());
1091

1092
    //         vm.run_raw_program_from_exprs(parsed).unwrap();
1093
    //     }
1094

1095
    //     // Grab the last value of the offset
1096
    //     let syntax_object_id = SYNTAX_OBJECT_ID.load(std::sync::atomic::Ordering::Relaxed);
1097

1098
    //     let bootstrap = BootstrapImage {
1099
    //         interner: take_interner(),
1100
    //         syntax_object_id,
1101
    //         sources: vm.sources,
1102
    //         programs: asts,
1103
    //     };
1104

1105
    //     // Encode to something implementing `Write`
1106
    //     let mut f = std::fs::File::create("src/boot/bootstrap.bin").unwrap();
1107
    //     bincode::serialize_into(&mut f, &bootstrap).unwrap();
1108
    // }
1109

1110
    /// Instantiates a raw engine instance. Includes no primitives or prelude.
1111
    ///
1112
    /// # Examples
1113
    ///
1114
    /// ```
1115
    /// # extern crate steel;
1116
    /// # use steel::steel_vm::engine::Engine;
1117
    /// let mut vm = Engine::new_raw();
1118
    /// assert!(vm.run("(+ 1 2 3").is_err()); // + is a free identifier
1119
    /// ```
1120
    pub fn new_raw() -> Self {
×
1121
        let sources = Sources::new();
×
1122
        let modules = ModuleContainer::default();
×
1123

1124
        let compiler = Arc::new(RwLock::new(Compiler::default_with_kernel(
×
1125
            sources,
×
1126
            modules.clone(),
×
1127
        )));
1128

1129
        Engine {
1130
            virtual_machine: SteelThread::new(compiler),
×
1131
            modules,
1132
            #[cfg(feature = "dylibs")]
1133
            dylibs: DylibContainers::new(),
×
1134
            id: EngineId::new(),
×
1135
        }
1136
    }
1137

1138
    pub fn report_engine_stats(&self) -> EngineStatistics {
×
1139
        EngineStatistics {
1140
            rooted_count: self.globals().len(),
×
1141
            constants_count: self.virtual_machine.compiler.read().constant_map.len(),
×
1142
            sources_size: self.virtual_machine.compiler.read().sources.size_in_bytes(),
×
1143
        }
1144
    }
1145

1146
    /// Registers a steel module. This is a more streamlined version of the
1147
    /// api provided by `register_source_module_resolver`.
1148
    pub fn register_steel_module(&mut self, module_name: String, text: String) {
×
1149
        self.virtual_machine
×
1150
            .compiler
×
1151
            .write()
1152
            .register_builtin(module_name, text);
×
1153
    }
1154

1155
    /// Instantiates a new engine instance with all primitive functions enabled.
1156
    /// This excludes the prelude and contract files.
1157
    ///
1158
    /// # Examples
1159
    ///
1160
    /// ```
1161
    /// # extern crate steel;
1162
    /// # use steel::steel_vm::engine::Engine;
1163
    /// let mut vm = Engine::new_base();
1164
    /// // map is found in the prelude, so this will fail
1165
    /// assert!(vm.run(r#"(map (lambda (x) 10) (list 1 2 3 4 5))"#).is_err());
1166
    /// ```
1167
    #[inline]
1168
    pub fn new_base() -> Self {
×
1169
        let mut vm = Engine::new_raw();
×
1170
        // Embed any primitives that we want to use
1171
        register_builtin_modules(&mut vm, false);
×
1172

1173
        vm.compile_and_run_raw_program(crate::steel_vm::primitives::ALL_MODULES)
×
1174
            .unwrap();
1175

1176
        vm
×
1177
    }
1178

1179
    /// Turn contracts on in the VM
1180
    pub fn with_contracts(&mut self, contracts: bool) -> &mut Self {
×
1181
        self.virtual_machine.with_contracts(contracts);
×
1182
        self
×
1183
    }
1184

1185
    #[inline]
1186
    pub fn new_sandboxed() -> Self {
1✔
1187
        let mut engine = fresh_kernel_image(true);
2✔
1188

1189
        engine.virtual_machine.compiler.write().kernel = Some(Kernel::new());
2✔
1190

1191
        #[cfg(feature = "profiling")]
1192
        let now = std::time::Instant::now();
×
1193

1194
        if let Err(e) = engine.run(PRELUDE_WITHOUT_BASE) {
2✔
1195
            raise_error(&engine.virtual_machine.compiler.read().sources, e);
×
1196
            panic!("This shouldn't happen!");
×
1197
        }
1198

1199
        #[cfg(feature = "profiling")]
1200
        log::info!(target: "engine-creation", "Engine Creation: {:?}", now.elapsed());
×
1201

1202
        // Block dylib loading for sandboxed instances
1203
        engine.disallow_dylib_loading();
2✔
1204

1205
        engine
1✔
1206
    }
1207

1208
    /// Call the print method within the VM
1209
    pub fn call_printing_method_in_context(&mut self, argument: SteelVal) -> Result<SteelVal> {
×
1210
        let function = self.extract_value("displayln")?;
×
1211
        self.call_function_with_args(function, vec![argument])
×
1212
    }
1213

1214
    /// Internal API for calling a function directly
1215
    pub fn call_function_with_args(
×
1216
        &mut self,
1217
        function: SteelVal,
1218
        arguments: Vec<SteelVal>,
1219
    ) -> Result<SteelVal> {
1220
        let constant_map = self.virtual_machine.compiler.read().constant_map.clone();
×
1221

1222
        self.virtual_machine
×
1223
            .call_function(constant_map, function, arguments)
×
1224
    }
1225

1226
    pub fn call_function_with_args_from_mut_slice(
1,499✔
1227
        &mut self,
1228
        function: SteelVal,
1229
        arguments: &mut [SteelVal],
1230
    ) -> Result<SteelVal> {
1231
        let constant_map = self.virtual_machine.compiler.read().constant_map.clone();
4,497✔
1232

1233
        self.virtual_machine
1,499✔
1234
            .call_function_from_mut_slice(constant_map, function, arguments)
5,996✔
1235
    }
1236

1237
    /// Call a function by name directly within the target environment
1238
    pub fn call_function_by_name_with_args(
×
1239
        &mut self,
1240
        function: &str,
1241
        arguments: Vec<SteelVal>,
1242
    ) -> Result<SteelVal> {
1243
        let constant_map = self.virtual_machine.compiler.read().constant_map.clone();
×
1244
        self.extract_value(function).and_then(|function| {
×
1245
            self.virtual_machine
×
1246
                .call_function(constant_map, function, arguments)
×
1247
        })
1248
    }
1249

1250
    pub fn call_function_by_name_with_args_from_mut_slice(
×
1251
        &mut self,
1252
        function: &str,
1253
        arguments: &mut [SteelVal],
1254
    ) -> Result<SteelVal> {
1255
        let constant_map = self.virtual_machine.compiler.read().constant_map.clone();
×
1256

1257
        self.extract_value(function).and_then(|function| {
×
1258
            self.virtual_machine
×
1259
                .call_function_from_mut_slice(constant_map, function, arguments)
×
1260
        })
1261
    }
1262

1263
    /// Nothing fancy, just run it
1264
    pub fn run<E: AsRef<str> + Into<Cow<'static, str>>>(
576✔
1265
        &mut self,
1266
        input: E,
1267
    ) -> Result<Vec<SteelVal>> {
1268
        self.compile_and_run_raw_program(input)
1,728✔
1269
    }
1270

1271
    pub fn with_immutable_reference<
1✔
1272
        'a,
1273
        'b: 'a,
1274
        T: CustomReference + ReferenceMarker<'b, Static = EXT> + 'b,
1275
        EXT: CustomReference + 'static,
1276
    >(
1277
        &'a mut self,
1278
        obj: &'a T,
1279
    ) -> LifetimeGuard<'a> {
1280
        assert_eq!(
1✔
1281
            crate::gc::unsafe_erased_pointers::type_id::<T>(),
1✔
1282
            std::any::TypeId::of::<EXT>()
1✔
1283
        );
1284

1285
        crate::gc::unsafe_erased_pointers::OpaqueReferenceNursery::allocate_ro_object::<T, EXT>(
1286
            obj,
1✔
1287
        );
1288

1289
        LifetimeGuard { engine: self }
1290
    }
1291

1292
    pub fn with_mut_reference<
2✔
1293
        'a,
1294
        'b: 'a,
1295
        T: CustomReference + ReferenceMarker<'b, Static = EXT> + 'b,
1296
        EXT: CustomReference + 'static,
1297
    >(
1298
        &'a mut self,
1299
        obj: &'a mut T,
1300
    ) -> LifetimeGuard<'a> {
1301
        assert_eq!(
2✔
1302
            crate::gc::unsafe_erased_pointers::type_id::<T>(),
2✔
1303
            std::any::TypeId::of::<EXT>()
2✔
1304
        );
1305

1306
        crate::gc::unsafe_erased_pointers::OpaqueReferenceNursery::allocate_rw_object::<T, EXT>(
1307
            obj,
2✔
1308
        );
1309

1310
        LifetimeGuard { engine: self }
1311
    }
1312

1313
    // Tie the lifetime of this object to the scope of this execution
1314
    pub fn run_with_reference<
2✔
1315
        'a,
1316
        'b: 'a,
1317
        T: CustomReference + ReferenceMarker<'b, Static = EXT> + 'b,
1318
        EXT: CustomReference + 'static,
1319
    >(
1320
        &'a mut self,
1321
        obj: &'a mut T,
1322
        bind_to: &'a str,
1323
        script: &'a str,
1324
    ) -> Result<SteelVal> {
1325
        self.with_mut_reference(obj).consume(move |engine, args| {
10✔
1326
            let mut args = args.into_iter();
6✔
1327

1328
            engine.update_value(bind_to, args.next().unwrap());
12✔
1329

1330
            let res = engine.compile_and_run_raw_program(Cow::Owned(script.to_owned()));
8✔
1331

1332
            engine.update_value(bind_to, SteelVal::Void);
8✔
1333

1334
            res.map(|x| x.into_iter().next().unwrap_or(SteelVal::Void))
12✔
1335
        })
1336
    }
1337

1338
    pub fn run_with_reference_from_path<
×
1339
        'a,
1340
        'b: 'a,
1341
        T: CustomReference + ReferenceMarker<'b, Static = EXT> + 'b,
1342
        EXT: CustomReference + 'static,
1343
    >(
1344
        &'a mut self,
1345
        obj: &'a mut T,
1346
        bind_to: &'a str,
1347
        script: &'a str,
1348
        path: PathBuf,
1349
    ) -> Result<SteelVal> {
1350
        self.with_mut_reference(obj).consume(move |engine, args| {
×
1351
            let mut args = args.into_iter();
×
1352

1353
            engine.update_value(bind_to, args.next().unwrap());
×
1354

1355
            let res = engine
×
1356
                .compile_and_run_raw_program_with_path(Cow::Owned(script.to_owned()), path.clone());
×
1357

1358
            engine.update_value(bind_to, SteelVal::Void);
×
1359

1360
            res.map(|x| x.into_iter().next().unwrap_or(SteelVal::Void))
×
1361
        })
1362
    }
1363

1364
    pub fn run_thunk_with_reference<
×
1365
        'a,
1366
        'b: 'a,
1367
        T: CustomReference + ReferenceMarker<'b, Static = EXT> + 'b,
1368
        EXT: CustomReference + 'static,
1369
    >(
1370
        &'a mut self,
1371
        obj: &'a mut T,
1372
        mut thunk: impl FnMut(&mut Engine, SteelVal) -> Result<SteelVal>,
1373
    ) -> Result<SteelVal> {
1374
        self.with_mut_reference(obj).consume(|engine, args| {
×
1375
            let mut args = args.into_iter();
×
1376

1377
            thunk(engine, args.next().unwrap_or(SteelVal::Void))
×
1378
        })
1379
    }
1380

1381
    pub fn run_thunk_with_ro_reference<
1✔
1382
        'a,
1383
        'b: 'a,
1384
        T: CustomReference + ReferenceMarker<'b, Static = EXT> + 'b,
1385
        EXT: CustomReference + 'static,
1386
    >(
1387
        &'a mut self,
1388
        obj: &'a T,
1389
        mut thunk: impl FnMut(&mut Engine, SteelVal) -> Result<SteelVal>,
1390
    ) -> Result<SteelVal> {
1391
        self.with_immutable_reference(obj).consume(|engine, args| {
5✔
1392
            let mut args = args.into_iter();
3✔
1393

1394
            thunk(engine, args.next().unwrap_or(SteelVal::Void))
5✔
1395
        })
1396
    }
1397

1398
    /// Instantiates a new engine instance with all the primitive functions enabled.
1399
    /// This is the most general engine entry point, and includes both the contract and
1400
    /// prelude files in the root.
1401
    ///
1402
    /// # Examples
1403
    ///
1404
    /// ```
1405
    /// # extern crate steel;
1406
    /// # use steel::steel_vm::engine::Engine;
1407
    /// let mut vm = Engine::new();
1408
    /// vm.run(r#"(+ 1 2 3)"#).unwrap();
1409
    /// ```
1410
    pub fn new() -> Self {
188✔
1411
        let mut engine = fresh_kernel_image(false);
376✔
1412

1413
        {
1414
            engine.virtual_machine.compiler.write().kernel = Some(Kernel::new());
188✔
1415
        }
1416

1417
        #[cfg(feature = "profiling")]
1418
        let now = std::time::Instant::now();
1419

1420
        if let Err(e) = engine.run(PRELUDE_WITHOUT_BASE) {
376✔
1421
            raise_error(&engine.virtual_machine.compiler.read().sources, e);
1422
            panic!("This shouldn't happen!");
1423
        }
1424

1425
        #[cfg(feature = "profiling")]
1426
        log::info!(target: "engine-creation", "Engine Creation: {:?}", now.elapsed());
1427

1428
        engine
188✔
1429
    }
1430

1431
    /// Adds a directory for the engine to resolve paths from.
1432
    ///
1433
    /// By default, the engine will search $STEEL_HOME/cogs for modules,
1434
    /// but any additional path added this way will increase the module
1435
    /// resolution search space.
1436
    pub fn add_search_directory(&mut self, dir: PathBuf) {
×
1437
        self.virtual_machine
×
1438
            .compiler
×
1439
            .write()
1440
            .add_search_directory(dir)
×
1441
    }
1442

1443
    pub fn with_interrupted(&mut self, interrupted: Arc<AtomicBool>) {
×
1444
        self.virtual_machine.with_interrupted(interrupted);
×
1445
    }
1446

1447
    pub fn get_thread_state_controller(&self) -> ThreadStateController {
×
1448
        self.virtual_machine.synchronizer.state.clone()
×
1449
    }
1450

1451
    /// Consumes the current `Engine` and emits a new `Engine` with the prelude added
1452
    /// to the environment. The prelude won't work unless the primitives are also enabled.
1453
    ///
1454
    /// # Examples
1455
    ///
1456
    /// ```
1457
    /// # extern crate steel;
1458
    /// # use steel::steel_vm::engine::Engine;
1459
    /// let mut vm = Engine::new_base().with_prelude().unwrap();
1460
    /// vm.run("(+ 1 2 3)").unwrap();
1461
    /// ```
1462
    pub fn with_prelude(mut self) -> Result<Self> {
×
1463
        let core_libraries = &[crate::stdlib::PRELUDE];
×
1464

1465
        for core in core_libraries {
×
1466
            self.compile_and_run_raw_program(*core)?;
×
1467
        }
1468

1469
        Ok(self)
×
1470
    }
1471

1472
    /// Registers the prelude to the environment of the given Engine.
1473
    /// The prelude won't work unless the primitives are also enabled.
1474
    ///
1475
    /// # Examples
1476
    ///
1477
    /// ```
1478
    /// # extern crate steel;
1479
    /// # use steel::steel_vm::engine::Engine;
1480
    /// let mut vm = Engine::new_base();
1481
    /// vm.register_prelude().unwrap();
1482
    /// vm.run("(+ 1 2 3)").unwrap();
1483
    /// ```
1484
    pub fn register_prelude(&mut self) -> Result<&mut Self> {
×
1485
        let core_libraries = &[crate::stdlib::PRELUDE];
×
1486

1487
        for core in core_libraries {
×
1488
            self.compile_and_run_raw_program(*core)?;
×
1489
        }
1490

1491
        Ok(self)
×
1492
    }
1493

1494
    // Registers the given module into the virtual machine
1495
    pub fn register_module(&mut self, module: BuiltInModule) -> &mut Self {
323✔
1496
        // Add the module to the map
1497
        self.modules.insert(module.name(), module.clone());
1,938✔
1498
        // Register the actual module itself as a value to make the virtual machine capable of reading from it
1499
        self.register_value(
646✔
1500
            module.unreadable_name().as_str(),
323✔
1501
            module.into_steelval().unwrap(),
969✔
1502
        );
1503

1504
        self
323✔
1505
    }
1506

1507
    #[cfg(feature = "dylibs")]
1508
    pub fn register_external_module(
×
1509
        &mut self,
1510
        module: abi_stable::std_types::RBox<FFIModule>,
1511
    ) -> Result<&mut Self> {
1512
        let external_module = FFIWrappedModule::new(module, None)?.build();
×
1513

1514
        self.modules
×
1515
            .insert(external_module.name(), external_module.clone());
×
1516

1517
        self.register_value(
×
1518
            external_module.unreadable_name().as_str(),
×
1519
            external_module.into_steelval().unwrap(),
×
1520
        );
1521

1522
        Ok(self)
×
1523
    }
1524

1525
    pub fn emit_raw_program_no_path<E: AsRef<str> + Into<Cow<'static, str>>>(
1✔
1526
        &mut self,
1527
        expr: E,
1528
    ) -> Result<RawProgramWithSymbols> {
1529
        self.with_sources_guard(|| {
3✔
1530
            self.virtual_machine
2✔
1531
                .compiler
2✔
1532
                .write()
2✔
1533
                .compile_executable(expr, None)
2✔
1534
        })
1535
    }
1536

1537
    pub fn emit_raw_program<E: AsRef<str> + Into<Cow<'static, str>>>(
×
1538
        &mut self,
1539
        expr: E,
1540
        path: PathBuf,
1541
    ) -> Result<RawProgramWithSymbols> {
1542
        self.with_sources_guard(|| {
×
1543
            self.virtual_machine
×
1544
                .compiler
×
1545
                .write()
×
1546
                .compile_executable(expr, Some(path))
×
1547
        })
1548
    }
1549

1550
    #[doc(hidden)]
1551
    pub fn debug_build_strings(&mut self, program: RawProgramWithSymbols) -> Result<Vec<String>> {
×
1552
        program.debug_generate_instructions(&mut self.virtual_machine.compiler.write().symbol_map)
×
1553
    }
1554

1555
    pub fn debug_print_build(
×
1556
        &mut self,
1557
        name: String,
1558
        program: RawProgramWithSymbols,
1559
    ) -> Result<()> {
1560
        program.debug_build(name, &mut self.virtual_machine.compiler.write().symbol_map)
×
1561
    }
1562

1563
    pub fn globals(&self) -> MappedRwLockReadGuard<'_, Vec<InternedString>> {
×
1564
        RwLockReadGuard::map(self.virtual_machine.compiler.read(), |x| {
×
1565
            x.symbol_map.values()
×
1566
        })
1567
    }
1568

1569
    // TODO: Remove duplicates!
1570
    pub fn readable_globals(&self, after_offset: usize) -> Vec<InternedString> {
×
1571
        let mut seen = HashSet::new();
×
1572

1573
        self.virtual_machine
×
1574
            .compiler
×
1575
            .read()
×
1576
            .symbol_map
×
1577
            .values()
1578
            .iter()
1579
            .skip(after_offset)
×
1580
            .filter(|x| {
×
1581
                let resolved = x.resolve();
×
1582
                !resolved.starts_with("#")
×
1583
                    && !resolved.starts_with("%")
×
1584
                    && !resolved.starts_with("mangler#%")
×
1585
                    && !resolved.starts_with(MANGLER_PREFIX)
×
1586
                    && !resolved.starts_with("__module")
×
1587
                    && !resolved.ends_with("__doc__")
×
1588
            })
1589
            .filter_map(|x| {
×
1590
                if seen.contains(x) {
×
1591
                    None
×
1592
                } else {
1593
                    seen.insert(x);
×
1594
                    Some(x)
×
1595
                }
1596
            })
1597
            .copied()
1598
            .collect()
1599
    }
1600

1601
    // Generate dynamically linked files, containing all of the necessary information
1602
    // This means - compiling all macros as well.
1603
    fn load_raw_program(&mut self, mut program: RawProgramWithSymbols) {
×
1604
        fn eval_atom(t: &SyntaxObject) -> Result<SteelVal> {
×
1605
            match &t.ty {
×
1606
                TokenType::BooleanLiteral(b) => Ok((*b).into()),
×
1607
                TokenType::Number(n) => (&**n).into_steelval(),
×
1608
                TokenType::StringLiteral(s) => Ok(SteelVal::StringV(s.clone().into())),
×
1609
                TokenType::CharacterLiteral(c) => Ok(SteelVal::CharV(*c)),
×
1610
                // TODO: Keywords shouldn't be misused as an expression - only in function calls are keywords allowed
1611
                TokenType::Keyword(k) => Ok(SteelVal::SymbolV((*k).into())),
×
1612
                what => {
×
1613
                    // println!("getting here in the eval_atom - code_gen");
1614
                    stop!(UnexpectedToken => what; t.span)
×
1615
                }
1616
            }
1617
        }
1618

1619
        for expr in &mut program.instructions {
×
1620
            // Reform the program to conform to the current state of _this_ engine.
1621
            for instruction in expr.iter_mut() {
×
1622
                todo!()
1623

1624
                // match instruction {
1625
                //     Instruction {
1626
                //         op_code: OpCode::PUSHCONST,
1627
                //         contents: Some(Expr::Atom(constant_value)),
1628
                //         ..
1629
                //     } => {
1630
                //         let value =
1631
                //             eval_atom(&constant_value).expect("This must be a constant value");
1632

1633
                //         instruction.payload_size = self.compiler.constant_map.add_or_get(value);
1634
                //     }
1635

1636
                //     Instruction {
1637
                //         op_code: OpCode::PUSHCONST,
1638
                //         contents: Some(Expr::List(expression)),
1639
                //         ..
1640
                //     } => {
1641
                //         let value = SteelVal::try_from(expression.clone())
1642
                //             .expect("This conversion must work");
1643

1644
                //         instruction.payload_size = self.compiler.constant_map.add_or_get(value);
1645
                //     }
1646

1647
                //     _ => {
1648
                //         todo!()
1649
                //     }
1650
                // }
1651
            }
1652
        }
1653
    }
1654

1655
    pub fn expand_to_file<E: AsRef<str> + Into<Cow<'static, str>>>(
×
1656
        &mut self,
1657
        exprs: E,
1658
        path: PathBuf,
1659
    ) {
1660
        self.with_sources_guard(|| {
×
1661
            self.virtual_machine
×
1662
                .compiler
×
1663
                .write()
×
1664
                .fully_expand_to_file(exprs, Some(path))
×
1665
                .unwrap()
×
1666
        });
1667
    }
1668

1669
    pub fn load_from_expanded_file(&mut self, path: &str) {
×
1670
        let program = self.with_sources_guard(|| {
×
1671
            self.virtual_machine
×
1672
                .compiler
×
1673
                .write()
×
1674
                .load_from_file(path)
×
1675
                .unwrap()
×
1676
        });
1677

1678
        self.run_raw_program(program).unwrap();
×
1679
    }
1680

1681
    // TODO -> clean up this API a lot
1682
    pub fn compile_and_run_raw_program_with_path<E: AsRef<str> + Into<Cow<'static, str>>>(
3✔
1683
        &mut self,
1684
        exprs: E,
1685
        path: PathBuf,
1686
    ) -> Result<Vec<SteelVal>> {
1687
        let program = self.with_sources_guard(|| {
12✔
1688
            self.virtual_machine
6✔
1689
                .compiler
6✔
1690
                .write()
6✔
1691
                .compile_executable(exprs, Some(path))
6✔
1692
        })?;
1693

1694
        self.run_raw_program(program)
×
1695
    }
1696

1697
    pub(crate) fn run_raw_program_from_exprs(
191✔
1698
        &mut self,
1699
        exprs: Vec<ExprKind>,
1700
    ) -> Result<Vec<SteelVal>> {
1701
        let program = self
573✔
1702
            .virtual_machine
382✔
1703
            .compiler
382✔
1704
            .write()
1705
            .compile_executable_from_expressions(exprs)?;
191✔
1706
        self.run_raw_program(program)
1707
    }
1708

1709
    pub fn compile_and_run_raw_program<E: AsRef<str> + Into<Cow<'static, str>>>(
11,288✔
1710
        &mut self,
1711
        exprs: E,
1712
    ) -> Result<Vec<SteelVal>> {
1713
        let program = self.with_sources_guard(|| {
45,124✔
1714
            self.virtual_machine
22,576✔
1715
                .compiler
22,576✔
1716
                .write()
22,576✔
1717
                .compile_executable(exprs, None)
22,576✔
1718
        })?;
1719

1720
        self.run_raw_program(program)
×
1721
    }
1722

1723
    pub fn raw_program_to_executable(
11,454✔
1724
        &mut self,
1725
        program: RawProgramWithSymbols,
1726
    ) -> Result<Executable> {
1727
        let symbol_map_offset = self.virtual_machine.compiler.read().symbol_map.len();
34,362✔
1728

1729
        let result = program.build(
34,362✔
1730
            "TestProgram".to_string(),
22,908✔
1731
            &mut self.virtual_machine.compiler.write().symbol_map,
11,454✔
1732
        );
1733

1734
        // Revisit if we need to do this at all?
1735
        if result.is_err() {
22,930✔
1736
            let mut guard = self.virtual_machine.compiler.write();
66✔
1737

1738
            // Reset the compiled modules as well.
1739
            guard.symbol_map.roll_back(symbol_map_offset);
88✔
1740
            guard.module_manager.rollback_metadata();
22✔
1741
        }
1742

1743
        result
11,454✔
1744
    }
1745

1746
    // TODO: Add doc for this
1747
    #[doc(hidden)]
1748
    pub fn environment_offset(&self) -> GlobalCheckpoint {
×
1749
        GlobalCheckpoint {
1750
            symbol_map_offset: self.virtual_machine.compiler.read().symbol_map.len(),
×
1751
            globals_offset: self.virtual_machine.global_env.len(),
×
1752
        }
1753
    }
1754

1755
    // TODO: Add doc for this
1756
    #[doc(hidden)]
1757
    pub fn rollback_to_checkpoint(&mut self, checkpoint: GlobalCheckpoint) -> Result<()> {
×
1758
        self.virtual_machine
×
1759
            .compiler
×
1760
            .write()
×
1761
            .symbol_map
×
1762
            .roll_back(checkpoint.symbol_map_offset);
×
1763

1764
        // // TODO: Does this even need to happen?
1765
        // #[cfg(feature = "sync")]
1766
        // {
1767
        //     self.virtual_machine
1768
        //         .global_env
1769
        //         .bindings_vec
1770
        //         .write()
1771
        //         // .unwrap()
1772
        //         .truncate(checkpoint.globals_offset);
1773
        // }
1774

1775
        #[cfg(not(feature = "sync"))]
1776
        {
1777
            self.virtual_machine
1778
                .global_env
1779
                .bindings_vec
1780
                .truncate(checkpoint.globals_offset);
1781
        }
1782

1783
        Ok(())
×
1784
    }
1785

1786
    pub fn run_raw_program(&mut self, program: RawProgramWithSymbols) -> Result<Vec<SteelVal>> {
11,454✔
1787
        // TODO: Add hook here to check what values are now unreachable!
1788
        let executable = self.raw_program_to_executable(program)?;
45,816✔
1789

1790
        self.gc_shadowed_roots();
1791

1792
        self.virtual_machine.run_executable(&executable)
1793
    }
1794

1795
    fn gc_shadowed_roots(&mut self) {
11,432✔
1796
        // Unfortunately, we have to invoke a whole GC algorithm here
1797
        // for shadowed rooted values
1798
        if self
11,432✔
1799
            .virtual_machine
11,432✔
1800
            .compiler
11,432✔
1801
            .write()
11,432✔
1802
            .symbol_map
11,432✔
1803
            .free_list
11,432✔
1804
            .should_collect()
1805
        {
1806
            #[cfg(feature = "sync")]
1807
            self.virtual_machine.with_locked_env(|ctx, env| {
645✔
1808
                #[cfg(feature = "sync")]
1809
                {
1810
                    GlobalSlotRecycler::free_shadowed_rooted_values(
430✔
1811
                        // &mut self.virtual_machine.global_env.bindings_vec.write(),
1812
                        env.0.as_mut_slice(),
430✔
1813
                        // .unwrap(),
1814
                        &mut ctx.compiler.write().symbol_map,
430✔
1815
                        &mut ctx.heap.lock().unwrap(),
215✔
1816
                    );
1817
                }
1818

1819
                #[cfg(not(feature = "sync"))]
1820
                {
1821
                    GlobalSlotRecycler::free_shadowed_rooted_values(
1822
                        &mut ctx.global_env.bindings_vec,
1823
                        &mut ctx.compiler.write().symbol_map,
1824
                        &mut ctx.heap.lock().unwrap(),
1825
                    );
1826
                }
1827

1828
                // Drop the pure functions which have been lifted.
1829
                ctx.function_interner
215✔
1830
                    .pure_function_interner
215✔
1831
                    .retain(|key, value| crate::gc::Gc::strong_count(value) > 1);
59,513✔
1832

1833
                // TODO:
1834
                // It is also necessary to keep track of the values to drop
1835
                // for closures - this can be done by tracking the closure id
1836
                // throughout the generated bytecode. Same way we keep track of
1837
                // the referenced globals; we track the referenced closures.
1838
                // FIXME: Add that code here
1839

1840
                ctx.compiler
215✔
1841
                    .write()
215✔
1842
                    .symbol_map
215✔
1843
                    .free_list
215✔
1844
                    .increment_generation();
215✔
1845
            });
1846

1847
            #[cfg(not(feature = "sync"))]
1848
            self.virtual_machine.with_locked_env(|ctx| {
1849
                #[cfg(feature = "sync")]
1850
                {
1851
                    GlobalSlotRecycler::free_shadowed_rooted_values(
1852
                        &mut ctx.virtual_machine.global_env.bindings_vec.write(),
1853
                        // &mut env.0.as_mut_slice(),
1854
                        // .unwrap(),
1855
                        &mut ctx.compiler.write().symbol_map,
1856
                        &mut ctx.heap.lock().unwrap(),
1857
                    );
1858
                }
1859

1860
                #[cfg(not(feature = "sync"))]
1861
                {
1862
                    GlobalSlotRecycler::free_shadowed_rooted_values(
1863
                        &mut ctx.global_env.bindings_vec,
1864
                        &mut ctx.compiler.write().symbol_map,
1865
                        &mut ctx.heap.lock().unwrap(),
1866
                    );
1867
                }
1868

1869
                // Drop the pure functions which have been lifted.
1870
                ctx.function_interner
1871
                    .pure_function_interner
1872
                    .retain(|key, value| crate::gc::Gc::strong_count(value) > 1);
1873

1874
                // TODO:
1875
                // It is also necessary to keep track of the values to drop
1876
                // for closures - this can be done by tracking the closure id
1877
                // throughout the generated bytecode. Same way we keep track of
1878
                // the referenced globals; we track the referenced closures.
1879
                // FIXME: Add that code here
1880

1881
                ctx.compiler
1882
                    .write()
1883
                    .symbol_map
1884
                    .free_list
1885
                    .increment_generation();
1886
            });
1887
        }
1888
    }
1889

1890
    pub fn run_executable(&mut self, executable: &Executable) -> Result<Vec<SteelVal>> {
×
1891
        self.virtual_machine.run_executable(executable)
×
1892
    }
1893

1894
    /// Directly emit the expanded ast
1895
    pub fn emit_expanded_ast(
×
1896
        &mut self,
1897
        expr: &str,
1898
        path: Option<PathBuf>,
1899
    ) -> Result<Vec<ExprKind>> {
1900
        self.with_sources_guard(|| {
×
1901
            self.virtual_machine
×
1902
                .compiler
×
1903
                .write()
×
1904
                .emit_expanded_ast(expr, path)
×
1905
        })
1906
    }
1907

1908
    fn with_sources_guard<T>(&self, func: impl FnOnce() -> T) -> T {
11,293✔
1909
        let res = func();
22,586✔
1910

1911
        self.virtual_machine
22,586✔
1912
            .compiler
22,586✔
1913
            .write()
1914
            .gc_sources(self.virtual_machine.function_interner.spans.values());
22,586✔
1915

1916
        res
11,293✔
1917
    }
1918

1919
    pub fn emit_expanded_ast_without_optimizations(
1✔
1920
        &mut self,
1921
        expr: &str,
1922
        path: Option<PathBuf>,
1923
    ) -> Result<Vec<ExprKind>> {
1924
        self.with_sources_guard(|| {
3✔
1925
            self.virtual_machine
2✔
1926
                .compiler
2✔
1927
                .write()
2✔
1928
                .emit_expanded_ast_without_optimizations(expr, path)
2✔
1929
        })
1930
    }
1931

1932
    /// Emit the unexpanded AST
1933
    pub fn emit_ast_to_string(expr: &str) -> Result<String> {
×
1934
        let parsed = Self::emit_ast(expr)?;
×
1935
        Ok(parsed.into_iter().map(|x| x.to_pretty(60)).join("\n\n"))
×
1936
    }
1937

1938
    pub fn emit_ast(expr: &str) -> Result<Vec<ExprKind>> {
×
1939
        let parsed: std::result::Result<Vec<ExprKind>, ParseError> =
×
1940
            Parser::new(expr, SourceId::none()).collect();
×
1941
        Ok(parsed?)
×
1942
    }
1943

1944
    /// Emit the fully expanded AST as a pretty printed string
1945
    pub fn emit_fully_expanded_ast_to_string(
×
1946
        &mut self,
1947
        expr: &str,
1948
        path: Option<PathBuf>,
1949
    ) -> Result<String> {
1950
        self.with_sources_guard(|| {
×
1951
            Ok(self
×
1952
                .virtual_machine
×
1953
                .compiler
×
1954
                .write()
×
1955
                .emit_expanded_ast(expr, path)?
×
1956
                .into_iter()
×
1957
                .map(|x| x.to_pretty(60))
×
1958
                .join("\n\n"))
×
1959
        })
1960
    }
1961

1962
    /// Emits the fully expanded AST directly.
1963
    pub fn emit_fully_expanded_ast(
×
1964
        &mut self,
1965
        expr: &str,
1966
        path: Option<PathBuf>,
1967
    ) -> Result<Vec<ExprKind>> {
1968
        self.with_sources_guard(|| {
×
1969
            self.virtual_machine
×
1970
                .compiler
×
1971
                .write()
×
1972
                .emit_expanded_ast(expr, path)
×
1973
        })
1974
    }
1975

1976
    /// Registers an external value of any type as long as it implements [`FromSteelVal`](crate::rvals::FromSteelVal) and
1977
    /// [`IntoSteelVal`](crate::rvals::IntoSteelVal). This method does the coercion to embed the type into the `Engine`'s
1978
    /// environment with the name `name`. This function can fail only if the conversion from `T` to [`SteelVal`](crate::rvals::SteelVal) fails.
1979
    ///
1980
    /// # Examples
1981
    ///
1982
    /// ```
1983
    /// # extern crate steel;
1984
    /// # use steel::steel_vm::engine::Engine;
1985
    /// let mut vm = Engine::new();
1986
    /// let external_value = "hello-world".to_string();
1987
    /// vm.register_external_value("hello-world", external_value).unwrap();
1988
    /// vm.run("hello-world").unwrap(); // Will return the string
1989
    /// ```
1990
    pub fn register_external_value<T: FromSteelVal + IntoSteelVal>(
1✔
1991
        &mut self,
1992
        name: &str,
1993
        value: T,
1994
    ) -> Result<&mut Self> {
1995
        let converted = value.into_steelval()?;
3✔
1996
        Ok(self.register_value(name, converted))
×
1997
    }
1998

1999
    /// Registers a [`SteelVal`](crate::rvals::SteelVal) under the name `name` in the `Engine`'s internal environment.
2000
    ///
2001
    /// # Examples
2002
    /// ```
2003
    /// # extern crate steel;
2004
    /// # use steel::steel_vm::engine::Engine;
2005
    /// use steel::rvals::SteelVal;
2006
    ///
2007
    /// let mut vm = Engine::new();
2008
    /// let external_value = SteelVal::StringV("hello-world".to_string().into());
2009
    /// vm.register_value("hello-world", external_value);
2010
    /// vm.run("hello-world").unwrap(); // Will return the string
2011
    /// ```
2012
    pub fn register_value(&mut self, name: &str, value: SteelVal) -> &mut Self {
868✔
2013
        self.register_value_inner(name, value)
2,604✔
2014
    }
2015

2016
    pub fn update_value(&mut self, name: &str, value: SteelVal) -> Option<&mut Self> {
4✔
2017
        let idx = self.virtual_machine.compiler.read().get_idx(name)?;
12✔
2018
        self.virtual_machine.global_env.repl_set_idx(idx, value);
2019
        Some(self)
2020
    }
2021

2022
    /// Registers multiple values at once
2023
    pub fn register_values(
×
2024
        &mut self,
2025
        values: impl Iterator<Item = (String, SteelVal)>,
2026
    ) -> &mut Self {
2027
        for (name, value) in values {
×
2028
            self.register_value(name.as_str(), value);
×
2029
        }
2030
        self
×
2031
    }
2032

2033
    /// Registers a predicate for a given type. When embedding external values, it is convenient
2034
    /// to be able to have a predicate to test if the given value is the specified type.
2035
    /// In order to be registered, a type must implement [`FromSteelVal`](crate::rvals::FromSteelVal)
2036
    /// and [`IntoSteelVal`](crate::rvals::IntoSteelVal)
2037
    ///
2038
    /// # Examples
2039
    ///
2040
    /// ```
2041
    /// # extern crate steel;
2042
    /// # use steel::steel_vm::engine::Engine;
2043
    /// use steel::steel_vm::register_fn::RegisterFn;
2044
    /// fn foo() -> usize {
2045
    ///    10
2046
    /// }
2047
    ///
2048
    /// let mut vm = Engine::new();
2049
    /// vm.register_fn("foo", foo);
2050
    ///
2051
    /// vm.run(r#"(foo)"#).unwrap(); // Returns vec![10]
2052
    /// ```
2053
    pub fn register_type<T: FromSteelVal + IntoSteelVal>(
1✔
2054
        &mut self,
2055
        predicate_name: &'static str,
2056
    ) -> &mut Self {
2057
        let f = move |args: &[SteelVal]| -> Result<SteelVal> {
1✔
2058
            if args.len() != 1 {
×
2059
                stop!(ArityMismatch => format!("{} expected 1 argument, got {}", predicate_name, args.len()));
×
2060
            }
2061

2062
            assert!(args.len() == 1);
×
2063

2064
            Ok(SteelVal::BoolV(T::from_steelval(&args[0]).is_ok()))
×
2065
        };
2066

2067
        self.register_value(
3✔
2068
            predicate_name,
2✔
2069
            SteelVal::BoxedFunction(Gc::new(BoxedDynFunction::new(
3✔
2070
                Arc::new(f),
2✔
2071
                Some(predicate_name),
1✔
2072
                Some(1),
1✔
2073
            ))),
2074
        )
2075
    }
2076

2077
    /// Extracts a value with the given identifier `name` from the internal environment.
2078
    /// If a script calculated some series of bound values, then it can be extracted this way.
2079
    /// This will return the [`SteelVal`](crate::rvals::SteelVal), not the underlying data.
2080
    /// To unwrap the value, use the [`extract`](crate::steel_vm::engine::Engine::extract) method and pass the type parameter.
2081
    ///
2082
    /// The function will return an error if the `name` is not currently bound in the `Engine`'s internal environment.
2083
    ///
2084
    /// # Examples
2085
    ///
2086
    /// ```
2087
    /// # extern crate steel;
2088
    /// # use steel::steel_vm::engine::Engine;
2089
    /// use steel::rvals::SteelVal;
2090
    /// let mut vm = Engine::new();
2091
    /// vm.run("(define a 10)").unwrap();
2092
    /// assert_eq!(vm.extract_value("a").unwrap(), SteelVal::IntV(10));
2093
    /// ```
2094
    pub fn extract_value(&self, name: &str) -> Result<SteelVal> {
2,873✔
2095
        let idx = self.virtual_machine.compiler.read().get_idx(name).ok_or_else(throw!(
13,000✔
2096
            Generic => format!("free identifier: {name} - identifier given cannot be found in the global environment")
2097
        ))?;
2098

2099
        self.virtual_machine.extract_value(idx)
2100
            .ok_or_else(throw!(
2101
                Generic => format!("free identifier: {name} - identifier given cannot be found in the global environment")
2102
            ))
2103
    }
2104

2105
    /// Extracts a value with the given identifier `name` from the internal environment, and attempts to coerce it to the
2106
    /// given type. This will return an error if the `name` is not currently bound in the `Engine`'s internal environment, or
2107
    /// if the type passed in does not match the value (and thus the coercion using [`FromSteelVal`](crate::rvals::FromSteelVal) fails)
2108
    ///
2109
    /// # Examples
2110
    ///
2111
    /// ```
2112
    /// # extern crate steel;
2113
    /// # use steel::steel_vm::engine::Engine;
2114
    /// let mut vm = Engine::new();
2115
    /// vm.run("(define a 10)").unwrap();
2116
    /// assert_eq!(vm.extract::<usize>("a").unwrap(), 10);
2117
    /// ```
2118
    pub fn extract<T: FromSteelVal>(&self, name: &str) -> Result<T> {
8✔
2119
        T::from_steelval(&self.extract_value(name)?)
32✔
2120
    }
2121

2122
    /// Raise the error within the stack trace
2123
    pub fn raise_error(&self, error: SteelErr) {
×
2124
        raise_error(&self.virtual_machine.compiler.read().sources, error)
×
2125
    }
2126

2127
    /// Emit an error string reporing, the back trace.
2128
    pub fn raise_error_to_string(&self, error: SteelErr) -> Option<String> {
×
2129
        raise_error_to_string(&self.virtual_machine.compiler.read().sources, error)
×
2130
    }
2131

2132
    pub fn add_module(&mut self, path: String) -> Result<()> {
×
2133
        self.virtual_machine
×
2134
            .compiler
×
2135
            .write()
2136
            .compile_module(path.into(), self.modules.clone())
×
2137
    }
2138

2139
    pub fn modules(&self) -> MappedRwLockReadGuard<'_, crate::HashMap<PathBuf, CompiledModule>> {
×
2140
        RwLockReadGuard::map(self.virtual_machine.compiler.read(), |x| x.modules())
×
2141
    }
2142

2143
    pub fn global_exists(&self, ident: &str) -> bool {
×
2144
        let spur = if let Some(spur) = InternedString::try_get(ident) {
×
2145
            spur
2146
        } else {
2147
            return false;
×
2148
        };
2149

2150
        self.virtual_machine
2151
            .compiler
2152
            .read()
2153
            .symbol_map
2154
            .get(&spur)
2155
            .is_ok()
2156
    }
2157

2158
    pub fn symbol_map(&self) -> MappedRwLockReadGuard<'_, SymbolMap> {
×
2159
        RwLockReadGuard::map(self.virtual_machine.compiler.read(), |x| &x.symbol_map)
×
2160
    }
2161

2162
    pub fn in_scope_macros(
8✔
2163
        &self,
2164
    ) -> MappedRwLockReadGuard<'_, FxHashMap<InternedString, SteelMacro>> {
2165
        RwLockReadGuard::map(self.virtual_machine.compiler.read(), |x| &x.macro_env)
16✔
2166
    }
2167

2168
    pub fn in_scope_macros_mut(
×
2169
        &mut self,
2170
    ) -> MappedRwLockWriteGuard<'_, FxHashMap<InternedString, SteelMacro>> {
2171
        RwLockWriteGuard::map(self.virtual_machine.compiler.write(), |x| &mut x.macro_env)
×
2172
    }
2173

2174
    // TODO: Re-implement the module path expansion
2175
    pub fn get_module(&self, path: PathBuf) -> Result<SteelVal> {
×
2176
        let module_path = path_to_module_name(path);
×
2177
        self.extract_value(&module_path)
×
2178
    }
2179

2180
    pub fn get_source_id(&self, path: &PathBuf) -> Option<SourceId> {
×
2181
        self.virtual_machine
×
2182
            .compiler
×
2183
            .read()
×
2184
            .sources
×
2185
            .get_source_id(path)
×
2186
    }
2187

2188
    pub fn get_path_for_source_id(&self, source_id: &SourceId) -> Option<PathBuf> {
×
2189
        self.virtual_machine
×
2190
            .compiler
×
2191
            .read()
×
2192
            .sources
×
2193
            .get_path(source_id)
×
2194
    }
2195

2196
    pub fn get_source(&self, source_id: &SourceId) -> Option<Arc<Cow<'static, str>>> {
×
2197
        self.virtual_machine
×
2198
            .compiler
×
2199
            .read()
×
2200
            .sources
×
2201
            .sources
×
2202
            .get(*source_id)
×
2203
            .cloned()
2204
    }
2205

2206
    #[doc(hidden)]
2207
    pub fn get_doc_for_identifier(&self, ident: &str) -> Option<String> {
×
2208
        let value = self.extract_value(ident).ok()?;
×
2209

2210
        match self.virtual_machine.compiler.read().get_doc(value)? {
×
2211
            crate::compiler::compiler::StringOrSteelString::String(s) => Some(s),
×
2212
            crate::compiler::compiler::StringOrSteelString::SteelString(steel_string) => {
×
2213
                Some(steel_string.as_str().to_string())
×
2214
            }
2215
        }
2216
    }
2217
}
2218

2219
// #[cfg(test)]
2220
// mod on_progress_tests {
2221
//     use super::*;
2222
//     use std::cell::Cell;
2223
//     use std::rc::Rc;
2224

2225
//     // TODO: At the moment the on progress business is turned off
2226

2227
//     // #[test]
2228
//     // fn count_every_thousand() {
2229
//     //     let mut vm = Engine::new();
2230

2231
//     //     let external_count = Rc::new(Cell::new(0));
2232
//     //     let embedded_count = Rc::clone(&external_count);
2233

2234
//     //     vm.on_progress(move |count| {
2235
//     //         // parameter is 'usize' - number of instructions performed up to this point
2236
//     //         if count % 1000 == 0 {
2237
//     //             // print out a progress log every 1000 operations
2238
//     //             println!("Number of instructions up to this point: {}", count);
2239
//     //             embedded_count.set(embedded_count.get() + 1);
2240

2241
//     //             // Returning false here would quit the evaluation of the function
2242
//     //             return true;
2243
//     //         }
2244
//     //         true
2245
//     //     });
2246

2247
//     //     // This should end with "Number of instructions up to this point: 4000"
2248
//     //     vm.run(
2249
//     //         r#"
2250
//     //         (define (loop x)
2251
//     //             (if (equal? x 1000)
2252
//     //                 x
2253
//     //                 (loop (+ x 1))))
2254
//     //         (displayln (loop 0))
2255
//     //     "#,
2256
//     //     )
2257
//     //     .unwrap();
2258

2259
//     //     assert_eq!(external_count.get(), 4);
2260
//     // }
2261
// }
2262

2263
fn raise_error(sources: &Sources, error: SteelErr) {
×
2264
    if let Some(span) = error.span() {
×
2265
        let source_id = span.source_id();
2266

2267
        if let Some(source_id) = source_id {
×
2268
            let sources = &sources.sources;
2269

2270
            let file_name = sources.get_path(&source_id);
2271

2272
            if let Some(file_content) = sources.get(source_id) {
×
2273
                // Build stack trace if we have it:
2274
                if let Some(trace) = error.stack_trace() {
×
2275
                    // TODO: Flatten recursive calls into the same stack trace
2276
                    // and present the count
2277
                    for dehydrated_context in trace.trace().iter().take(20) {
×
2278
                        // Report a call stack with whatever we actually have,
2279
                        if let Some(span) = dehydrated_context.span() {
×
2280
                            let id = span.source_id();
2281
                            if let Some(id) = id {
×
2282
                                if let Some(source) = sources.get(id) {
×
2283
                                    let trace_line_file_name = sources.get_path(&id);
2284

2285
                                    let resolved_file_name = trace_line_file_name
2286
                                        .as_ref()
2287
                                        .and_then(|x| x.to_str())
×
2288
                                        .unwrap_or_default();
2289

2290
                                    back_trace(resolved_file_name, source, *span);
2291
                                }
2292
                            }
2293
                        }
2294
                    }
2295
                }
2296

2297
                let resolved_file_name = file_name.unwrap_or_default();
2298

2299
                error.emit_result(resolved_file_name.to_str().unwrap(), file_content);
2300
                return;
2301
            }
2302
        }
2303
    }
2304

2305
    println!("Unable to locate source and span information for this error: {error}");
×
2306
}
2307

2308
// If we are to construct an error object, emit that
2309
pub(crate) fn raise_error_to_string(sources: &Sources, error: SteelErr) -> Option<String> {
×
2310
    if let Some(span) = error.span() {
×
2311
        let source_id = span.source_id();
×
2312
        if let Some(source_id) = source_id {
×
2313
            let sources = &sources.sources;
×
2314

2315
            let file_name = sources.get_path(&source_id);
×
2316

2317
            if let Some(file_content) = sources.get(source_id) {
×
2318
                let mut back_traces = Vec::with_capacity(20);
×
2319

2320
                // Build stack trace if we have it:
2321
                if let Some(trace) = error.stack_trace() {
×
2322
                    // TODO: Flatten recursive calls into the same stack trace
2323
                    // and present the count
2324
                    for dehydrated_context in trace.trace().iter().take(20) {
×
2325
                        // Report a call stack with whatever we actually have,
2326
                        if let Some(span) = dehydrated_context.span() {
×
2327
                            // Missing the span, its not particularly worth reporting?
2328
                            if span.start == 0 && span.end == 0 {
×
2329
                                continue;
×
2330
                            }
2331

2332
                            let id = span.source_id();
×
2333

2334
                            if let Some(id) = id {
×
2335
                                if let Some(source) = sources.get(id) {
×
2336
                                    let trace_line_file_name = sources.get_path(&id);
×
2337

2338
                                    let resolved_file_name = trace_line_file_name
×
2339
                                        .as_ref()
2340
                                        .and_then(|x| x.to_str())
×
2341
                                        .unwrap_or_default();
2342

2343
                                    let bt =
×
2344
                                        back_trace_to_string(resolved_file_name, source, *span);
×
2345
                                    back_traces.push(bt);
×
2346
                                }
2347
                            }
2348
                        }
2349
                    }
2350
                }
2351

2352
                let resolved_file_name = file_name.unwrap_or_default();
×
2353

2354
                let final_error =
×
2355
                    error.emit_result_to_string(resolved_file_name.to_str().unwrap(), file_content);
×
2356

2357
                back_traces.push(final_error);
×
2358

2359
                return Some(back_traces.join("\n"));
×
2360
            }
2361
        }
2362
    }
2363

2364
    // println!("Unable to locate source and span information for this error: {error}");
2365

2366
    None
×
2367
}
2368

2369
pub struct EngineBuilder {
2370
    engine: Engine,
2371
}
2372

2373
impl EngineBuilder {
2374
    pub fn raw() -> Self {
×
2375
        Self {
2376
            engine: Engine::new_raw(),
×
2377
        }
2378
    }
2379
}
2380

2381
#[cfg(test)]
2382
mod engine_api_tests {
2383
    use crate::custom_reference;
2384

2385
    use super::*;
2386

2387
    struct ReferenceStruct {
2388
        value: usize,
2389
    }
2390

2391
    impl ReferenceStruct {
2392
        pub fn get_value(&mut self) -> usize {
2393
            self.value
2394
        }
2395

2396
        pub fn get_value_immutable(&self) -> usize {
2397
            self.value
2398
        }
2399
    }
2400

2401
    impl CustomReference for ReferenceStruct {}
2402
    custom_reference!(ReferenceStruct);
2403

2404
    #[test]
2405
    fn test_references_in_engine() {
2406
        let mut engine = Engine::new();
2407
        let mut external_object = ReferenceStruct { value: 10 };
2408

2409
        engine.register_value("*external*", SteelVal::Void);
2410
        engine.register_fn("external-get-value", ReferenceStruct::get_value);
2411

2412
        {
2413
            let res = engine
2414
                .run_with_reference::<ReferenceStruct, ReferenceStruct>(
2415
                    &mut external_object,
2416
                    "*external*",
2417
                    "(external-get-value *external*)",
2418
                )
2419
                .unwrap();
2420

2421
            assert_eq!(res, SteelVal::IntV(10));
2422
        }
2423
    }
2424

2425
    #[test]
2426
    fn test_references_in_engine_get_removed_after_lifetime() {
2427
        let mut engine = Engine::new();
2428
        let mut external_object = ReferenceStruct { value: 10 };
2429

2430
        engine.register_value("*external*", SteelVal::Void);
2431
        engine.register_fn("external-get-value", ReferenceStruct::get_value);
2432

2433
        let res = engine
2434
            .run_with_reference::<ReferenceStruct, ReferenceStruct>(
2435
                &mut external_object,
2436
                "*external*",
2437
                "(external-get-value *external*)",
2438
            )
2439
            .unwrap();
2440

2441
        assert_eq!(res, SteelVal::IntV(10));
2442

2443
        // Afterwards, the value should be gone
2444
        assert_eq!(engine.extract_value("*external*").unwrap(), SteelVal::Void);
2445
    }
2446

2447
    #[test]
2448
    fn test_immutable_references_in_engine_get_removed_after_lifetime() {
2449
        let mut engine = Engine::new();
2450
        let external_object = ReferenceStruct { value: 10 };
2451

2452
        engine.register_fn("external-get-value", ReferenceStruct::get_value);
2453

2454
        engine.register_fn(
2455
            "external-get-value-imm",
2456
            ReferenceStruct::get_value_immutable,
2457
        );
2458

2459
        let res = engine
2460
            .run_thunk_with_ro_reference::<ReferenceStruct, ReferenceStruct>(
2461
                &external_object,
2462
                |mut engine, value| {
2463
                    engine.register_value("*external*", value);
2464
                    engine
2465
                        .compile_and_run_raw_program("(external-get-value-imm *external*)")
2466
                        .map(|x| x.into_iter().next().unwrap())
2467
                },
2468
            )
2469
            .unwrap();
2470

2471
        assert_eq!(res, SteelVal::IntV(10));
2472

2473
        // This absolutely has to fail, otherwise we're in trouble.
2474
        assert!(engine
2475
            .compile_and_run_raw_program("(external-get-value-imm *external*)")
2476
            .is_err());
2477
    }
2478
}
2479

2480
#[cfg(test)]
2481
mod engine_sandbox_tests {
2482
    use super::*;
2483

2484
    #[test]
2485
    fn sandbox() {
2486
        let mut engine = Engine::new_sandboxed();
2487

2488
        assert!(engine
2489
            .compile_and_run_raw_program(r#"(create-directory! "foo-bar")"#)
2490
            .is_err());
2491
    }
2492
}
2493

2494
#[cfg(test)]
2495
mod derive_macro_tests {
2496
    use super::*;
2497

2498
    #[derive(steel_derive::_Steel, PartialEq, Debug)]
2499
    #[steel(getters)]
2500
    struct FooBar {
2501
        baz: usize,
2502
    }
2503

2504
    #[test]
2505
    fn test_struct_registered() {
2506
        let mut module = BuiltInModule::new("foo");
2507
        FooBar::register_type(&mut module);
2508
    }
2509

2510
    #[derive(steel_derive::_Steel, PartialEq, Debug)]
2511
    #[steel(equality, getters, constructors)]
2512
    enum TestEnumVariants {
2513
        Foo,
2514
        Bar,
2515
        Baz(usize, usize),
2516
        Bazinga {
2517
            foo: usize,
2518
            bananas: usize,
2519
        },
2520
        #[steel(ignore)]
2521
        Ignored(SteelString),
2522
    }
2523

2524
    fn test() {
2525
        let mut module = BuiltInModule::new("foo");
2526
        TestEnumVariants::register_enum_variants(&mut module);
2527
    }
2528

2529
    enum Foo {
2530
        Bananas(usize, usize, usize, usize),
2531
        Bananas2(usize, usize, usize, usize),
2532
    }
2533

2534
    #[test]
2535
    fn test_primitives_are_registered() {
2536
        let mut engine = Engine::new();
2537
        let mut module = BuiltInModule::new("foo");
2538

2539
        TestEnumVariants::register_enum_variants(&mut module);
2540
        engine.register_module(module);
2541

2542
        engine
2543
            .run(
2544
                r#"
2545
(require-builtin foo)
2546
(assert! (TestEnumVariants-Foo? (TestEnumVariants-Foo)))
2547
(assert! (TestEnumVariants-Bar? (TestEnumVariants-Bar)))
2548
(assert! (TestEnumVariants-Baz? (TestEnumVariants-Baz 10 20)))
2549
(assert! (TestEnumVariants-Bazinga? (TestEnumVariants-Bazinga 100 200)))
2550

2551
(assert! (equal? (TestEnumVariants-Bazinga-foo (TestEnumVariants-Bazinga 100 200)) 100))
2552
(assert! (equal? (TestEnumVariants-Bazinga-bananas (TestEnumVariants-Bazinga 100 200)) 200))
2553
(assert! (equal? (TestEnumVariants-Baz-0 (TestEnumVariants-Baz 100 200)) 100))
2554
(assert! (equal? (TestEnumVariants-Baz-1 (TestEnumVariants-Baz 100 200)) 200))
2555

2556
(assert! (not (equal? 100 (TestEnumVariants-Foo))))
2557

2558
            "#,
2559
            )
2560
            .unwrap();
2561

2562
        assert!(engine
2563
            .run(r#"(TestEnumVariants-Ignored "Hello world")"#)
2564
            .is_err())
2565
    }
2566
}
2567

2568
#[test]
2569
fn test_steel_quote_macro() {
2570
    let foobarbaz = ExprKind::atom("foo");
2571
    let foobarbaz_list = ExprKind::List(List::new(vec![ExprKind::atom("foo")]));
2572

2573
    let expanded = steel_derive::internal_steel_quote! {
2574
        (define bananas #foobarbaz)
2575
        (define x (begin @foobarbaz_list ...))
2576
    }
2577
    .unwrap();
2578

2579
    println!("{}", expanded);
2580
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc