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

mattwparas / steel / 18461079395

13 Oct 2025 09:20AM UTC coverage: 42.731% (-0.9%) from 43.668%
18461079395

Pull #536

github

web-flow
Merge 6f55a8b56 into e378cba22
Pull Request #536: Initial proposal for no_std support

63 of 755 new or added lines in 38 files covered. (8.34%)

73 existing lines in 15 files now uncovered.

12324 of 28841 relevant lines covered (42.73%)

3215759.81 hits per line

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

62.64
/crates/steel-core/src/values/functions.rs
1
#![allow(unused)]
2

3
use std::{
4
    cell::{Cell, RefCell},
5
    convert::TryFrom,
6
    hash::Hasher,
7
    sync::Arc,
8
};
9

10
use crate::collections::{HashMap, HashSet};
11

12
use fxhash::FxHashSet;
13

14
use crate::{
15
    core::{instructions::DenseInstruction, opcode::OpCode},
16
    gc::{
17
        shared::{MutContainer, ShareableMut, StandardShared},
18
        Gc, Shared, SharedMut,
19
    },
20
    parser::{parser::SyntaxObjectId, span::Span},
21
    rvals::{
22
        from_serializable_value, into_serializable_value, AsRefSteelVal, Custom, FunctionSignature,
23
        HeapSerializer, IntoSteelVal, MutFunctionSignature, SerializableSteelVal, SteelString,
24
    },
25
    steel_vm::{
26
        register_fn::SendSyncStatic,
27
        vm::{BlockMetadata, BlockPattern, BuiltInSignature},
28
    },
29
    // values::contracts::ContractedFunction,
30
    SteelErr,
31
    SteelVal,
32
};
33

34
use super::{
35
    closed::{Heap, HeapRef},
36
    structs::UserDefinedStruct,
37
};
38

39
// pub(crate) enum Function {
40
//     BoxedFunction(BoxedFunctionSignature),
41
//     Closure(Gc<ByteCodeLambda>),
42
//     FuncV(FunctionSignature),
43
//     ContractedFunction(Gc<ContractedFunction>),
44
//     MutFuncV(MutFunctionSignature),
45
//     Builtin(BuiltInSignature),
46
// }
47

48
// Keep track of this metadata table for getting the docs associated
49
// with a given function?
50
pub struct LambdaMetadataTable {
51
    fn_ptr_table: HashMap<usize, SteelString>,
52
}
53

54
impl Custom for LambdaMetadataTable {}
55

56
impl LambdaMetadataTable {
57
    pub fn new() -> Self {
4✔
58
        Self {
59
            fn_ptr_table: HashMap::new(),
4✔
60
        }
61
    }
62

63
    pub fn add(&mut self, function: SteelVal, doc: SteelString) {
2,445✔
64
        match function {
2,445✔
65
            SteelVal::Closure(b) => {
2,445✔
66
                self.fn_ptr_table.insert(b.id as _, doc);
67
            }
68
            SteelVal::BoxedFunction(b) => {
×
69
                self.fn_ptr_table.insert(Gc::as_ptr(&b) as usize, doc);
×
70
            }
71
            _ => {}
×
72
        }
73
    }
74

75
    pub fn get(&self, function: SteelVal) -> Option<SteelString> {
×
76
        match function {
×
77
            SteelVal::Closure(b) => self.fn_ptr_table.get(&(b.id as _)).cloned(),
×
78
            SteelVal::BoxedFunction(b) => {
×
79
                self.fn_ptr_table.get(&(Gc::as_ptr(&b) as usize)).cloned()
×
80
            }
81
            _ => None,
×
82
        }
83
    }
84

85
    // TODO: This will need to get called in other places
86
    pub fn collect_garbage(&mut self, keep_set: impl Iterator<Item = usize>) {
×
NEW
87
        let set = keep_set.collect::<HashSet<_>>();
×
88

89
        self.fn_ptr_table.retain(|k, _| set.contains(k));
×
90
    }
91
}
92

93
#[cfg(feature = "inline-captures")]
94
const INLINE_CAPTURE_SIZE: usize = 3;
95

96
#[cfg(not(feature = "inline-captures"))]
97
pub type CaptureVec = Vec<SteelVal>;
98

99
#[cfg(feature = "inline-captures")]
100
pub type CaptureVec = smallvec::SmallVec<[SteelVal; INLINE_CAPTURE_SIZE]>;
101

102
#[derive(Clone, Debug)]
103
pub struct ByteCodeLambda {
104
    pub(crate) id: u32,
105
    /// body of the function with identifiers yet to be bound
106
    #[cfg(feature = "dynamic")]
107
    pub(crate) body_exp: RefCell<Shared<[DenseInstruction]>>,
108

109
    #[cfg(not(feature = "dynamic"))]
110
    pub(crate) body_exp: StandardShared<[DenseInstruction]>,
111

112
    pub(crate) arity: u16,
113

114
    #[cfg(feature = "dynamic")]
115
    call_count: Cell<usize>,
116

117
    pub(crate) is_multi_arity: bool,
118

119
    // Store... some amount inline?
120
    // pub(crate) captures: Vec<SteelVal>,
121
    pub(crate) captures: CaptureVec,
122

123
    // pub(crate) captures: Box<[SteelVal]>
124
    #[cfg(feature = "dynamic")]
125
    pub(crate) blocks: RefCell<Vec<(BlockPattern, BlockMetadata)>>,
126

127
    // This is a little suspicious, but it should give us the necessary information to attach a struct of metadata
128
    #[cfg(feature = "sync")]
129
    contract: SharedMut<Option<Gc<UserDefinedStruct>>>,
130

131
    #[cfg(not(feature = "sync"))]
132
    contract: MutContainer<Option<Gc<UserDefinedStruct>>>,
133
}
134

135
impl PartialEq for ByteCodeLambda {
136
    fn eq(&self, other: &Self) -> bool {
68✔
137
        // self.body_exp == other.body_exp &&
138
        self.arity == other.arity && self.id == other.id
112✔
139
    }
140
}
141

142
impl Eq for ByteCodeLambda {}
143

144
impl std::hash::Hash for ByteCodeLambda {
145
    fn hash<H: Hasher>(&self, state: &mut H) {
1,773✔
146
        self.id.hash(state);
5,319✔
147
        // self.body_exp.as_ptr().hash(state);
148
        self.arity.hash(state);
5,319✔
149

150
        // self.sub_expression_env.as_ptr().hash(state);
151
    }
152
}
153

154
// Can this be moved across threads? What does it cost to execute a closure in another thread?
155
// Engine instances be deep cloned?
156
pub struct SerializedLambda {
157
    pub id: u32,
158
    pub body_exp: Vec<DenseInstruction>,
159
    pub arity: usize,
160
    pub is_multi_arity: bool,
161
    // TODO: Go ahead and create a ThreadSafeSteelVal where we will just deep clone everything, move
162
    // it across the thread, and reconstruct on the other side.
163
    pub captures: Vec<SerializableSteelVal>,
164
}
165

166
#[derive(Clone)]
167
pub struct SerializedLambdaPrototype {
168
    pub id: u32,
169
    pub body_exp: Vec<DenseInstruction>,
170
    pub arity: usize,
171
    pub is_multi_arity: bool,
172
}
173

174
#[derive(Clone, PartialEq, Eq)]
175
pub struct RootedInstructions {
176
    #[cfg(feature = "rooted-instructions")]
177
    inner: *const [DenseInstruction],
178
    #[cfg(not(feature = "rooted-instructions"))]
179
    inner: StandardShared<[DenseInstruction]>,
180
}
181

182
#[cfg(feature = "rooted-instructions")]
183
impl Copy for RootedInstructions {}
184

185
// TODO: Come back to this
186
unsafe impl Send for RootedInstructions {}
187
unsafe impl Sync for RootedInstructions {}
188

189
impl RootedInstructions {
190
    pub fn new(instructions: StandardShared<[DenseInstruction]>) -> Self {
123,520✔
191
        Self {
192
            #[cfg(feature = "rooted-instructions")]
193
            inner: StandardShared::as_ptr(&instructions),
123,520✔
194
            #[cfg(not(feature = "rooted-instructions"))]
195
            inner: instructions,
196
        }
197
    }
198
}
199

200
impl std::fmt::Debug for RootedInstructions {
201
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
202
        write!(f, "{:?}", self.inner)
×
203
    }
204
}
205

206
impl std::ops::Deref for RootedInstructions {
207
    type Target = [DenseInstruction];
208

209
    fn deref(&self) -> &Self::Target {
2,147,483,647✔
210
        #[cfg(feature = "rooted-instructions")]
211
        unsafe {
212
            &(*self.inner)
2,147,483,647✔
213
        }
214

215
        #[cfg(not(feature = "rooted-instructions"))]
216
        &self.inner
217
    }
218
}
219

220
impl ByteCodeLambda {
221
    pub fn new(
128,228✔
222
        id: u32,
223
        body_exp: StandardShared<[DenseInstruction]>,
224
        arity: usize,
225
        is_multi_arity: bool,
226
        captures: CaptureVec,
227
    ) -> ByteCodeLambda {
228
        ByteCodeLambda {
229
            id,
230

231
            #[cfg(feature = "dynamic")]
232
            body_exp: RefCell::new(body_exp),
128,228✔
233
            #[cfg(not(feature = "dynamic"))]
234
            body_exp,
235

236
            arity: arity as u16,
256,456✔
237

238
            #[cfg(feature = "dynamic")]
239
            call_count: Cell::new(0),
128,228✔
240

241
            is_multi_arity,
242
            captures,
243

244
            #[cfg(feature = "sync")]
245
            contract: SharedMut::new(MutContainer::new(None)),
256,456✔
246

247
            #[cfg(not(feature = "sync"))]
248
            contract: MutContainer::new(None),
128,228✔
249

250
            #[cfg(feature = "dynamic")]
251
            blocks: RefCell::new(Vec::new()),
128,228✔
252
        }
253
    }
254

255
    pub(crate) fn from_serialized(heap: &mut HeapSerializer, value: SerializedLambda) -> Self {
×
256
        ByteCodeLambda::new(
257
            value.id,
×
258
            value.body_exp.into(),
×
259
            value.arity,
×
260
            value.is_multi_arity,
×
261
            value
×
262
                .captures
×
263
                .into_iter()
×
264
                .map(|x| from_serializable_value(heap, x))
×
265
                .collect(),
×
266
        )
267
    }
268

269
    pub fn rooted(instructions: StandardShared<[DenseInstruction]>) -> ByteCodeLambda {
62,818✔
270
        Self::new(
271
            SyntaxObjectId::fresh().into(),
125,636✔
272
            instructions,
62,818✔
273
            0,
274
            false,
275
            CaptureVec::default(),
62,818✔
276
        )
277
    }
278

279
    pub fn main(instructions: Vec<DenseInstruction>) -> ByteCodeLambda {
8✔
280
        Self::new(
281
            SyntaxObjectId::fresh().into(),
16✔
282
            instructions.into(),
16✔
283
            0,
284
            false,
285
            CaptureVec::default(),
8✔
286
        )
287
    }
288

289
    pub fn set_captures(&mut self, captures: CaptureVec) {
4,671,265✔
290
        self.captures = captures;
9,342,530✔
291
    }
292

293
    // TODO: The lifecycle of `RootedInstructions` should not be
294
    // beyond the scope of execution. This invariant should in
295
    // general hold - with the exception of continuations, which
296
    // should probably hold on to any functions that are contains
297
    // strongly - so there should be some kind of slot on the continuation
298
    // to hold on to a strong reference to each instruction set.
299
    pub(crate) fn body_exp(&self) -> RootedInstructions {
95,457,805✔
300
        // #[cfg(feature = "dynamic")]
301
        // return Shared::clone(&self.body_exp.borrow());
302

303
        // #[cfg(not(feature = "dynamic"))]
304
        // Shared::clone(&self.body_exp)
305

306
        #[cfg(not(feature = "rooted-instructions"))]
307
        return RootedInstructions {
308
            inner: StandardShared::clone(&self.body_exp),
309
        };
310

311
        #[cfg(feature = "rooted-instructions")]
312
        return RootedInstructions {
95,457,805✔
313
            inner: StandardShared::as_ptr(&self.body_exp),
95,457,805✔
314
        };
315
    }
316

317
    pub fn body_mut_exp(&mut self) -> StandardShared<[DenseInstruction]> {
×
318
        #[cfg(feature = "dynamic")]
319
        return StandardShared::clone(self.body_exp.get_mut());
320

321
        #[cfg(not(feature = "dynamic"))]
322
        StandardShared::clone(&self.body_exp)
×
323
    }
324

325
    // pub fn spans(&self) -> Rc<[Span]> {
326
    //     Rc::clone(&self.spans)
327
    // }
328

329
    // Get the starting index in the instruction set, and the new ID to associate with this
330
    // super instruction set.
331
    // Deep copy the old instruction set, update the new spot to have a dynamic super instruction
332
    // associated with it.
333
    #[cfg(feature = "dynamic")]
334
    pub fn update_to_super_instruction(
335
        &self,
336
        start: usize,
337
        super_instruction_id: usize,
338
    ) -> (DenseInstruction, Shared<[DenseInstruction]>) {
339
        let mut guard = self.body_exp.borrow_mut();
340
        let mut old: Box<[_]> = guard.iter().copied().collect();
341

342
        // set up the head instruction to get returned, we'll need it in the block first
343
        let head_instruction = old[start];
344

345
        // Point to the new super instruction
346
        old[start].op_code = OpCode::DynSuperInstruction;
347
        old[start].payload_size = super_instruction_id as _;
348
        *guard = old.into();
349
        (head_instruction, Shared::clone(&guard))
350
    }
351

352
    #[inline(always)]
353
    pub fn arity(&self) -> usize {
112,444,124✔
354
        self.arity as usize
112,444,124✔
355
    }
356

357
    #[inline(always)]
358
    pub fn is_multi_arity(&self) -> bool {
13,538✔
359
        self.is_multi_arity
13,538✔
360
    }
361

362
    // pub fn heap_allocated(&self) -> &RefCell<Vec<HeapRef<SteelVal>>> {
363
    //     &self.heap_allocated
364
    // }
365

366
    pub fn captures(&self) -> &[SteelVal] {
264,359,830✔
367
        &self.captures
264,359,830✔
368
    }
369

370
    #[cfg(feature = "dynamic")]
371
    #[inline(always)]
372
    pub fn increment_call_count(&self) {
×
373
        // self.call_count += 1;
374
        self.call_count.set(self.call_count.get() + 1);
×
375
    }
376

377
    #[cfg(feature = "dynamic")]
378
    pub fn call_count(&self) -> usize {
379
        self.call_count.get()
380
    }
381

382
    // pub fn set_cannot_be_compiled(&self) {
383
    //     self.cant_be_compiled.set(true)
384
    // }
385

386
    // pub fn has_attempted_to_be_compiled(&self) -> bool {
387
    //     self.cant_be_compiled.get()
388
    // }
389

390
    pub fn attach_contract_information(&self, steel_struct: Gc<UserDefinedStruct>) {
651✔
391
        #[cfg(feature = "sync")]
392
        {
393
            let mut guard = self.contract.write();
1,953✔
394

395
            *guard = Some(steel_struct);
651✔
396
        }
397

398
        #[cfg(not(feature = "sync"))]
399
        {
400
            let mut guard = self.contract.borrow_mut();
401

402
            *guard = Some(steel_struct);
403
        }
404
    }
405

406
    pub fn get_contract_information(&self) -> Option<SteelVal> {
68,391✔
407
        #[cfg(feature = "sync")]
408
        {
409
            self.contract
68,391✔
410
                .read()
411
                .as_ref()
412
                .map(|x| SteelVal::CustomStruct(x.clone()))
69,531✔
413
        }
414

415
        #[cfg(not(feature = "sync"))]
416
        {
417
            self.contract
418
                .borrow()
419
                .as_ref()
420
                .map(|x| SteelVal::CustomStruct(x.clone()))
421
        }
422
    }
423

424
    // pub fn mark_hot(&self) {
425
    //     self.is_hot.set(true)
426
    // }
427

428
    // pub(crate) fn mark_block_tail(&self, pattern: BlockPattern) {
429
    //     self.blocks.borrow_mut();
430
    // }
431

432
    // pub(crate) fn check_tail(&self, pattern: &BlockPattern) -> bool {
433
    //     self.blocks.borrow().contains(pattern)
434
    // }
435

436
    // pub(crate) fn block_tail(&self, block_pattern
437
}
438

439
pub fn attach_contract_struct(args: &[SteelVal]) -> crate::rvals::Result<SteelVal> {
651✔
440
    if let SteelVal::Closure(closure) = &args[0] {
1,302✔
441
        if let SteelVal::CustomStruct(s) = &args[1] {
651✔
442
            closure.attach_contract_information(s.clone());
443

444
            Ok(SteelVal::Void)
445
        } else {
446
            stop!(TypeMismatch => "attach-contract-struct! expects a struct in the second position")
×
447
        }
448
    } else {
449
        stop!(TypeMismatch => "attach-contract-struct! expects a function in the first position")
×
450
    }
451
}
452

453
pub fn get_contract(args: &[SteelVal]) -> crate::rvals::Result<SteelVal> {
674✔
454
    if let SteelVal::Closure(closure) = &args[0] {
1,150✔
455
        closure.get_contract_information().into_steelval()
456
    } else {
457
        Ok(SteelVal::BoolV(false))
198✔
458

459
        // stop!(TypeMismatch => "get-contract-struct! expects a function in the first position, found: {}", &args[0])
460
    }
461
}
462

463
#[derive(Clone)]
464
#[repr(C)]
465
pub enum StaticOrRcStr {
466
    Static(&'static str),
467
    Owned(Arc<String>),
468
}
469

470
/// This allows cloning the underlying closure, so we can send it across threads.
471
/// It does _not_ solve serializing closures fully, but it does mean we can move function
472
/// pointers across threads, which should be very helpful with spawning native threads.
473
// TODO: @Matt - Replace usage of BoxedDynFunction (and subsequent call sites) with this instead
474
// trait DynamicFunction: Send + Sync {
475
//     #[inline]
476
//     fn call(&self, args: &[SteelVal]) -> crate::rvals::Result<SteelVal>;
477
//     fn clone_box(&self) -> Box<dyn DynamicFunction>;
478
// }
479

480
// // Allow only the capturing of send + sync variables?
481
// impl<F: Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Clone + Send + Sync + 'static>
482
//     DynamicFunction for F
483
// {
484
//     fn call(&self, args: &[SteelVal]) -> crate::rvals::Result<SteelVal> {
485
//         (self)(args)
486
//     }
487

488
//     fn clone_box(&self) -> Box<dyn DynamicFunction> {
489
//         Box::new(self.clone())
490
//     }
491
// }
492

493
// impl Clone for Box<dyn DynamicFunction> {
494
//     fn clone(&self) -> Self {
495
//         self.clone_box()
496
//     }
497
// }
498

499
// pub enum MaybeSendSyncFunction {}
500

501
#[derive(Clone)]
502
#[repr(C)]
503
pub struct BoxedDynFunction {
504
    pub function:
505
        Arc<dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static>,
506
    pub name: Option<Arc<String>>,
507
    pub arity: Option<u32>,
508
}
509

510
impl BoxedDynFunction {
511
    // pub fn spawn_on_thread(self) {
512
    //     std::thread::spawn(move || self.function);
513
    // }
514

515
    pub(crate) fn new(
989✔
516
        function: Arc<
517
            dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static,
518
        >,
519
        name: Option<&str>,
520
        arity: Option<u32>,
521
    ) -> Self {
522
        BoxedDynFunction {
523
            function,
524
            name: name.map(|x| Arc::new(x.to_string())),
4,945✔
525
            arity,
526
        }
527
    }
528

529
    pub(crate) fn new_owned(
12,057✔
530
        function: Arc<
531
            dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static,
532
        >,
533
        name: Option<Arc<String>>,
534
        arity: Option<u32>,
535
    ) -> Self {
536
        BoxedDynFunction {
537
            function,
538
            name,
539
            arity,
540
        }
541
    }
542

543
    #[inline(always)]
544
    pub fn func(
310,760✔
545
        &self,
546
    ) -> &(dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static) {
547
        self.function.as_ref()
310,760✔
548
    }
549

550
    #[inline(always)]
551
    pub fn get_arity(&self) -> Option<u32> {
×
552
        self.arity
×
553
    }
554

555
    #[inline(always)]
556
    pub fn name(&self) -> Option<&str> {
×
557
        self.name.as_ref().map(|x| x.as_str())
×
558
    }
559
}
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