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

mattwparas / steel / 11947392359

21 Nov 2024 05:44AM UTC coverage: 46.072% (-0.2%) from 46.234%
11947392359

Pull #291

github

web-flow
Merge 8d943de47 into 9a2bb2ef5
Pull Request #291: Add rustls library and fix free identifier error message

16 of 86 new or added lines in 6 files covered. (18.6%)

37 existing lines in 10 files now uncovered.

12334 of 26771 relevant lines covered (46.07%)

415077.16 hits per line

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

78.26
/crates/steel-core/src/compiler/map.rs
1
use crate::throw;
2
use crate::{parser::interner::InternedString, rvals::Result};
3
use serde::{Deserialize, Serialize};
4
use std::collections::HashMap;
5

6
/// At the REPL or within an application, a script might be repeatedly
7
/// loaded. In this situation, the behavior that Steel picks is that
8
/// values that are shadowed are still there. Right now - we just leave
9
/// those values around, and they are unable to be reclaimed.
10
///
11
/// Instead what we should do is if a script is executed, we should
12
/// check if any values are shadowed directly. If they are shadowed,
13
/// we should add them to a candidate list of indices that we could
14
/// potentially mark as unreachable, freeing them for future use.
15
///
16
/// In order to mark things as reachable, we will need to run a GC
17
/// pass explicitly to check for the values that might be unreachable.
18
///
19
/// Since these are known to potentially be unreachable, this is our
20
/// candidate set to drop. Reachability in this case, is just checking
21
/// if the global variable is referenced by any instructions. That offset
22
/// into the global namespace is the only way that globals get referenced.
23
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
24
pub struct FreeList {
25
    // Slots which are potentially unreachable.
26
    pub(crate) shadowed_slots: Vec<usize>,
27

28
    // Slots which represent lambda lifted functions.
29
    pub(crate) lambda_lifted: Vec<usize>,
30

31
    // Once the above slots have been deemed definitely unreachable,
32
    // we put them here. This is now the candidate set of variables
33
    // to fill in from. We don't have to slot at the back, we can
34
    // slot in over the things that have filled up otherwise.
35
    pub(crate) free_list: Vec<usize>,
36

37
    pub(crate) threshold: usize,
38

39
    pub(crate) multiplier: usize,
40

41
    pub(crate) epoch: usize,
42
}
43

44
impl FreeList {
45
    pub fn add_shadowed(&mut self, val: usize) {
8,317✔
46
        self.shadowed_slots.push(val);
8,317✔
47
    }
48

49
    pub fn pop_next_free(&mut self) -> Option<usize> {
53,954✔
50
        self.free_list.pop()
53,954✔
51
    }
52

53
    pub fn shadowed_count(&self) -> usize {
319✔
54
        self.shadowed_slots.len()
319✔
55
    }
56

57
    pub fn should_collect(&self) -> bool {
319✔
58
        self.shadowed_count() > self.threshold
319✔
59
    }
60

61
    pub fn increment_generation(&mut self) {
19✔
62
        if self.epoch == 4 {
19✔
63
            self.threshold = 100;
×
64
            self.epoch = 1;
×
65
        } else {
66
            self.threshold *= self.multiplier;
19✔
67
            self.epoch += 1;
19✔
68
        }
69
    }
70
}
71

72
#[derive(Debug, Clone, Serialize, Deserialize)]
73
pub struct SymbolMap {
74
    values: Vec<InternedString>,
75
    map: HashMap<InternedString, usize>,
76
    // TODO:
77
    // This definitely does not need to be this way, and also it
78
    // can be locked during the entire duration of the compilation
79
    // process
80
    pub(crate) free_list: FreeList,
81
}
82

83
impl Default for SymbolMap {
84
    fn default() -> Self {
×
85
        Self::new()
×
86
    }
87
}
88

89
impl SymbolMap {
90
    pub fn new() -> Self {
5✔
91
        SymbolMap {
92
            values: Vec::new(),
5✔
93
            map: HashMap::new(),
5✔
94
            free_list: FreeList {
5✔
95
                threshold: 100,
96
                multiplier: 2,
97
                epoch: 1,
98
                ..Default::default()
99
            },
100
        }
101
    }
102

103
    pub fn values(&self) -> &Vec<InternedString> {
×
104
        &self.values
×
105
    }
106

UNCOV
107
    pub fn len(&self) -> usize {
×
UNCOV
108
        self.values.len()
×
109
    }
110

111
    pub fn roll_back(&mut self, index: usize) {
22✔
112
        for value in self.values.drain(index..) {
42✔
113
            self.map.remove(&value);
10✔
114
        }
115
    }
116

117
    pub fn contains(&mut self, ident: InternedString) -> bool {
×
118
        self.map.contains_key(&ident)
×
119
    }
120

121
    pub fn add(&mut self, ident: &InternedString) -> usize {
53,954✔
122
        // Check the free list for the next value. If the free list has an open slot
123
        // then we should take that. Otherwise, just insert it at the end.
124
        let idx = self
53,954✔
125
            .free_list
53,954✔
126
            .pop_next_free()
127
            .unwrap_or_else(|| self.values.len());
161,840✔
128

129
        {
130
            let resolved = ident.resolve();
53,954✔
131

132
            if resolved.starts_with("##__lifted_pure_function")
53,954✔
133
                || resolved.starts_with("##lambda-lifting")
51,442✔
134
            {
135
                self.free_list.lambda_lifted.push(idx);
2,512✔
136
            }
137
        }
138

139
        let prev = self.map.insert(*ident, idx);
53,954✔
140

141
        // There was something there previously.
142
        // And now that we're overriding it, go ahead and put
143
        // it in the shadowed_slots.
144
        if let Some(prev) = prev {
8,317✔
145
            // println!("Potentially shadowed: {} -> {}", ident, prev);
146

147
            self.free_list.add_shadowed(prev);
148
        }
149

150
        // If this is going in at the end, then we just slot it in there.
151
        // Otherwise, we need to overwrite the existing global slot.
152
        if idx == self.values.len() {
53,932✔
153
            // Add the values so we can do a backwards resolution
154
            self.values.push(*ident);
53,932✔
155
        } else {
156
            self.values[idx] = *ident;
22✔
157
        }
158

159
        idx
160
    }
161

162
    // fallible
163
    pub fn get(&self, ident: &InternedString) -> Result<usize> {
341,658✔
164
        self.map
341,658✔
165
            .get(ident)
341,658✔
166
            .copied()
167
            .ok_or_else(throw!(FreeIdentifier => "Cannot reference an identifier before its definition: {}", ident.resolve()))
341,658✔
168
    }
169
}
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