grass_compiler/evaluate/
env.rs

1use codemap::{Span, Spanned};
2
3use crate::{
4    ast::{AstForwardRule, Configuration, ConfiguredValue, Mixin},
5    builtin::modules::{ForwardedModule, Module, ModuleScope, Modules, ShadowedModule},
6    common::Identifier,
7    error::SassResult,
8    selector::ExtensionStore,
9    value::{SassFunction, Value},
10};
11use std::{
12    cell::RefCell,
13    collections::{BTreeMap, HashSet},
14    sync::Arc,
15};
16
17type Mutable<T> = Arc<RefCell<T>>;
18
19use super::{scope::Scopes, visitor::CallableContentBlock};
20
21#[derive(Debug, Clone)]
22pub(crate) struct Environment {
23    pub scopes: Scopes,
24    pub modules: Mutable<Modules>,
25    pub global_modules: Vec<Mutable<Module>>,
26    pub content: Option<Arc<CallableContentBlock>>,
27    pub forwarded_modules: Mutable<Vec<Mutable<Module>>>,
28    pub imported_modules: Mutable<Vec<Mutable<Module>>>,
29    #[allow(clippy::type_complexity)]
30    pub nested_forwarded_modules: Option<Mutable<Vec<Mutable<Vec<Mutable<Module>>>>>>,
31}
32
33impl Environment {
34    pub fn new() -> Self {
35        Self {
36            scopes: Scopes::new(),
37            modules: Arc::new(RefCell::new(Modules::new())),
38            global_modules: Vec::new(),
39            content: None,
40            forwarded_modules: Arc::new(RefCell::new(Vec::new())),
41            imported_modules: Arc::new(RefCell::new(Vec::new())),
42            nested_forwarded_modules: None,
43        }
44    }
45
46    pub fn new_closure(&self) -> Self {
47        Self {
48            scopes: self.scopes.new_closure(),
49            modules: Arc::clone(&self.modules),
50            global_modules: self.global_modules.iter().map(Arc::clone).collect(),
51            content: self.content.as_ref().map(Arc::clone),
52            forwarded_modules: Arc::clone(&self.forwarded_modules),
53            imported_modules: Arc::clone(&self.imported_modules),
54            nested_forwarded_modules: self.nested_forwarded_modules.as_ref().map(Arc::clone),
55        }
56    }
57
58    pub fn for_import(&self) -> Self {
59        Self {
60            scopes: self.scopes.new_closure(),
61            modules: Arc::new(RefCell::new(Modules::new())),
62            global_modules: Vec::new(),
63            content: self.content.as_ref().map(Arc::clone),
64            forwarded_modules: Arc::clone(&self.forwarded_modules),
65            imported_modules: Arc::clone(&self.imported_modules),
66            nested_forwarded_modules: self.nested_forwarded_modules.as_ref().map(Arc::clone),
67        }
68    }
69
70    pub fn to_dummy_module(&self, span: Span) -> Module {
71        Module::Environment {
72            scope: ModuleScope::new(),
73            upstream: Vec::new(),
74            extension_store: ExtensionStore::new(span),
75            env: self.clone(),
76        }
77    }
78
79    /// Makes the members forwarded by [module] available in the current
80    /// environment.
81    ///
82    /// This is called when [module] is `@import`ed.
83    pub fn import_forwards(&mut self, _env: Module) {
84        if let Module::Environment { env, .. } = _env {
85            let mut forwarded = env.forwarded_modules;
86
87            if (*forwarded).borrow().is_empty() {
88                return;
89            }
90
91            // Omit modules from [forwarded] that are already globally available and
92            // forwarded in this module.
93            let forwarded_modules = Arc::clone(&self.forwarded_modules);
94            if !(*forwarded_modules).borrow().is_empty() {
95                // todo: intermediate name
96                let mut x = Vec::new();
97                for entry in (*forwarded).borrow().iter() {
98                    if !forwarded_modules
99                        .borrow()
100                        .iter()
101                        .any(|module| Arc::ptr_eq(module, entry))
102                        || !self
103                            .global_modules
104                            .iter()
105                            .any(|module| Arc::ptr_eq(module, entry))
106                    {
107                        x.push(Arc::clone(entry));
108                    }
109                }
110
111                forwarded = Arc::new(RefCell::new(x));
112            }
113
114            let forwarded_var_names = forwarded
115                .borrow()
116                .iter()
117                .flat_map(|module| (*module).borrow().scope().variables.keys())
118                .collect::<HashSet<Identifier>>();
119            let forwarded_fn_names = forwarded
120                .borrow()
121                .iter()
122                .flat_map(|module| (*module).borrow().scope().functions.keys())
123                .collect::<HashSet<Identifier>>();
124            let forwarded_mixin_names = forwarded
125                .borrow()
126                .iter()
127                .flat_map(|module| (*module).borrow().scope().mixins.keys())
128                .collect::<HashSet<Identifier>>();
129
130            if self.at_root() {
131                let mut to_remove = Vec::new();
132
133                // Hide members from modules that have already been imported or
134                // forwarded that would otherwise conflict with the @imported members.
135                for (idx, module) in (*self.imported_modules).borrow().iter().enumerate() {
136                    let shadowed = ShadowedModule::if_necessary(
137                        Arc::clone(module),
138                        Some(&forwarded_var_names),
139                        Some(&forwarded_fn_names),
140                        Some(&forwarded_mixin_names),
141                    );
142
143                    if shadowed.is_some() {
144                        to_remove.push(idx);
145                    }
146                }
147
148                let mut imported_modules = (*self.imported_modules).borrow_mut();
149
150                for &idx in to_remove.iter().rev() {
151                    imported_modules.remove(idx);
152                }
153
154                to_remove.clear();
155
156                for (idx, module) in (*self.forwarded_modules).borrow().iter().enumerate() {
157                    let shadowed = ShadowedModule::if_necessary(
158                        Arc::clone(module),
159                        Some(&forwarded_var_names),
160                        Some(&forwarded_fn_names),
161                        Some(&forwarded_mixin_names),
162                    );
163
164                    if shadowed.is_some() {
165                        to_remove.push(idx);
166                    }
167                }
168
169                let mut forwarded_modules = (*self.forwarded_modules).borrow_mut();
170
171                for &idx in to_remove.iter().rev() {
172                    forwarded_modules.remove(idx);
173                }
174
175                imported_modules.extend(forwarded.borrow().iter().map(Arc::clone));
176                forwarded_modules.extend(forwarded.borrow().iter().map(Arc::clone));
177            } else {
178                self.nested_forwarded_modules
179                    .get_or_insert_with(|| {
180                        Arc::new(RefCell::new(
181                            (0..self.scopes.len())
182                                .map(|_| Arc::new(RefCell::new(Vec::new())))
183                                .collect(),
184                        ))
185                    })
186                    .borrow_mut()
187                    .last_mut()
188                    .unwrap()
189                    .borrow_mut()
190                    .extend(forwarded.borrow().iter().map(Arc::clone));
191            }
192
193            // Remove existing member definitions that are now shadowed by the
194            // forwarded modules.
195            for variable in forwarded_var_names {
196                (*self.scopes.variables)
197                    .borrow_mut()
198                    .last_mut()
199                    .unwrap()
200                    .borrow_mut()
201                    .remove(&variable);
202            }
203            self.scopes.last_variable_index = None;
204
205            for func in forwarded_fn_names {
206                (*self.scopes.functions)
207                    .borrow_mut()
208                    .last_mut()
209                    .unwrap()
210                    .borrow_mut()
211                    .remove(&func);
212            }
213            for mixin in forwarded_mixin_names {
214                (*self.scopes.mixins)
215                    .borrow_mut()
216                    .last_mut()
217                    .unwrap()
218                    .borrow_mut()
219                    .remove(&mixin);
220            }
221        }
222    }
223
224    pub fn to_implicit_configuration(&self) -> Configuration {
225        let mut configuration = BTreeMap::new();
226
227        let variables = (*self.scopes.variables).borrow();
228
229        for variables in variables.iter() {
230            let entries = (**variables).borrow();
231            for (key, value) in entries.iter() {
232                // Implicit configurations are never invalid, making [configurationSpan]
233                // unnecessary, so we pass null here to avoid having to compute it.
234                configuration.insert(*key, ConfiguredValue::implicit(value.clone()));
235            }
236        }
237
238        Configuration::implicit(configuration)
239    }
240
241    pub fn forward_module(&mut self, module: Arc<RefCell<Module>>, rule: AstForwardRule) {
242        let view = ForwardedModule::if_necessary(module, rule);
243        (*self.forwarded_modules).borrow_mut().push(view);
244
245        // todo: assertnoconflicts
246    }
247
248    pub fn insert_mixin(&mut self, name: Identifier, mixin: Mixin) {
249        self.scopes.insert_mixin(name, mixin);
250    }
251
252    pub fn mixin_exists(&self, name: Identifier) -> bool {
253        self.scopes.mixin_exists(name)
254    }
255
256    pub fn get_mixin(
257        &self,
258        name: Spanned<Identifier>,
259        namespace: Option<Spanned<Identifier>>,
260    ) -> SassResult<Mixin> {
261        if let Some(namespace) = namespace {
262            let modules = (*self.modules).borrow();
263            let module = modules.get(namespace.node, namespace.span)?;
264            return (*module).borrow().get_mixin(name);
265        }
266
267        match self.scopes.get_mixin(name) {
268            Ok(v) => Ok(v),
269            Err(e) => {
270                if let Some(v) = self.get_mixin_from_global_modules(name.node) {
271                    return Ok(v);
272                }
273
274                Err(e)
275            }
276        }
277    }
278
279    pub fn insert_fn(&mut self, func: SassFunction) {
280        self.scopes.insert_fn(func);
281    }
282
283    pub fn fn_exists(&self, name: Identifier) -> bool {
284        self.scopes.fn_exists(name)
285    }
286
287    pub fn get_fn(
288        &self,
289        name: Identifier,
290        namespace: Option<Spanned<Identifier>>,
291    ) -> SassResult<Option<SassFunction>> {
292        if let Some(namespace) = namespace {
293            let modules = (*self.modules).borrow();
294            let module = modules.get(namespace.node, namespace.span)?;
295            return Ok((*module).borrow().get_fn(name));
296        }
297
298        Ok(self
299            .scopes
300            .get_fn(name)
301            .or_else(|| self.get_function_from_global_modules(name)))
302    }
303
304    pub fn var_exists(
305        &self,
306        name: Identifier,
307        namespace: Option<Spanned<Identifier>>,
308    ) -> SassResult<bool> {
309        if let Some(namespace) = namespace {
310            let modules = (*self.modules).borrow();
311            let module = modules.get(namespace.node, namespace.span)?;
312            return Ok((*module).borrow().var_exists(name));
313        }
314
315        Ok(self.scopes.var_exists(name))
316    }
317
318    pub fn get_var(
319        &mut self,
320        name: Spanned<Identifier>,
321        namespace: Option<Spanned<Identifier>>,
322    ) -> SassResult<Value> {
323        if let Some(namespace) = namespace {
324            let modules = (*self.modules).borrow();
325            let module = modules.get(namespace.node, namespace.span)?;
326            return (*module).borrow().get_var(name);
327        }
328
329        match self.scopes.get_var(name) {
330            Ok(v) => Ok(v),
331            Err(e) => {
332                if let Some(v) = self.get_variable_from_global_modules(name.node) {
333                    Ok(v)
334                } else {
335                    Err(e)
336                }
337            }
338        }
339    }
340
341    pub fn insert_var(
342        &mut self,
343        name: Spanned<Identifier>,
344        namespace: Option<Spanned<Identifier>>,
345        value: Value,
346        is_global: bool,
347        in_semi_global_scope: bool,
348    ) -> SassResult<()> {
349        if let Some(namespace) = namespace {
350            let mut modules = (*self.modules).borrow_mut();
351            let module = modules.get_mut(namespace.node, namespace.span)?;
352            (*module).borrow_mut().update_var(name, value)?;
353            return Ok(());
354        }
355
356        if is_global || self.at_root() {
357            // If this module doesn't already contain a variable named [name], try
358            // setting it in a global module.
359            if !self.scopes.global_var_exists(name.node) {
360                let module_with_name = self.from_one_module(name.node, "variable", |module| {
361                    if module.borrow().var_exists(*name) {
362                        Some(Arc::clone(module))
363                    } else {
364                        None
365                    }
366                });
367
368                if let Some(module_with_name) = module_with_name {
369                    module_with_name.borrow_mut().update_var(name, value)?;
370                    return Ok(());
371                }
372            }
373
374            self.scopes.insert_var(0, name.node, value);
375            return Ok(());
376        }
377
378        let mut index = self
379            .scopes
380            .find_var(name.node)
381            .unwrap_or(self.scopes.len() - 1);
382
383        if !in_semi_global_scope && index == 0 {
384            index = self.scopes.len() - 1;
385        }
386
387        self.scopes.last_variable_index = Some((name.node, index));
388
389        self.scopes.insert_var(index, name.node, value);
390
391        Ok(())
392    }
393
394    pub fn at_root(&self) -> bool {
395        self.scopes.len() == 1
396    }
397
398    pub fn scopes_mut(&mut self) -> &mut Scopes {
399        &mut self.scopes
400    }
401
402    pub fn global_vars(&self) -> Arc<RefCell<BTreeMap<Identifier, Value>>> {
403        self.scopes.global_variables()
404    }
405
406    pub fn global_mixins(&self) -> Arc<RefCell<BTreeMap<Identifier, Mixin>>> {
407        self.scopes.global_mixins()
408    }
409
410    pub fn global_functions(&self) -> Arc<RefCell<BTreeMap<Identifier, SassFunction>>> {
411        self.scopes.global_functions()
412    }
413
414    fn get_variable_from_global_modules(&self, name: Identifier) -> Option<Value> {
415        self.from_one_module(name, "variable", |module| {
416            (**module).borrow().get_var_no_err(name)
417        })
418    }
419
420    fn get_function_from_global_modules(&self, name: Identifier) -> Option<SassFunction> {
421        self.from_one_module(name, "function", |module| (**module).borrow().get_fn(name))
422    }
423
424    fn get_mixin_from_global_modules(&self, name: Identifier) -> Option<Mixin> {
425        self.from_one_module(name, "mixin", |module| {
426            (**module).borrow().get_mixin_no_err(name)
427        })
428    }
429
430    pub fn add_module(
431        &mut self,
432        namespace: Option<Identifier>,
433        module: Arc<RefCell<Module>>,
434        span: Span,
435    ) -> SassResult<()> {
436        match namespace {
437            Some(namespace) => {
438                (*self.modules)
439                    .borrow_mut()
440                    .insert(namespace, module, span)?;
441            }
442            None => {
443                for name in (*self.scopes.global_variables()).borrow().keys() {
444                    if (*module).borrow().var_exists(*name) {
445                        return Err((
446                            format!("This module and the new module both define a variable named \"${name}\".", name = name)
447                        , span).into());
448                    }
449                }
450
451                self.global_modules.push(module);
452            }
453        }
454
455        Ok(())
456    }
457
458    pub fn to_module(self, extension_store: ExtensionStore) -> Arc<RefCell<Module>> {
459        debug_assert!(self.at_root());
460
461        Arc::new(RefCell::new(Module::new_env(self, extension_store)))
462    }
463
464    fn from_one_module<T>(
465        &self,
466        _name: Identifier,
467        _ty: &str,
468        callback: impl Fn(&Arc<RefCell<Module>>) -> Option<T>,
469    ) -> Option<T> {
470        if let Some(nested_forwarded_modules) = &self.nested_forwarded_modules {
471            for modules in nested_forwarded_modules.borrow().iter().rev() {
472                for module in modules.borrow().iter().rev() {
473                    if let Some(value) = callback(module) {
474                        return Some(value);
475                    }
476                }
477            }
478        }
479
480        for module in self.imported_modules.borrow().iter() {
481            if let Some(value) = callback(module) {
482                return Some(value);
483            }
484        }
485
486        let mut value: Option<T> = None;
487        //     Object? identity;
488
489        for module in self.global_modules.iter() {
490            let value_in_module = match callback(module) {
491                Some(v) => v,
492                None => continue,
493            };
494
495            value = Some(value_in_module);
496
497            //       Object? identityFromModule = valueInModule is AsyncCallable
498            //           ? valueInModule
499            //           : module.variableIdentity(name);
500            //       if (identityFromModule == identity) continue;
501
502            //       if (value != null) {
503            //         var spans = _globalModules.entries.map(
504            //             (entry) => callback(entry.key).andThen((_) => entry.value.span));
505
506            //         throw MultiSpanSassScriptException(
507            //             'This $type is available from multiple global modules.',
508            //             '$type use', {
509            //           for (var span in spans)
510            //             if (span != null) span: 'includes $type'
511            //         });
512            //       }
513
514            //       value = valueInModule;
515            //       identity = identityFromModule;
516        }
517
518        value
519    }
520}