Skip to main content

boa_ast/
scope.rs

1//! This module implements the binding scope for various AST nodes.
2//!
3//! Scopes are used to track the bindings of identifiers in the AST.
4
5use bitflags::bitflags;
6use boa_string::JsString;
7use rustc_hash::FxHashMap;
8use std::{
9    cell::{Cell, RefCell},
10    fmt::Debug,
11    rc::Rc,
12};
13
14bitflags! {
15    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
16    struct BindingFlags: u8 {
17        const MUTABLE  = 1 << 0;
18        const LEX      = 1 << 1;
19        const STRICT   = 1 << 2;
20        const ESCAPES  = 1 << 3;
21        const ACCESSED = 1 << 4;
22    }
23}
24
25impl BindingFlags {
26    fn is_mutable(self) -> bool {
27        self.contains(BindingFlags::MUTABLE)
28    }
29    fn is_lex(self) -> bool {
30        self.contains(BindingFlags::LEX)
31    }
32    fn is_strict(self) -> bool {
33        self.contains(BindingFlags::STRICT)
34    }
35    fn escapes(self) -> bool {
36        self.contains(BindingFlags::ESCAPES)
37    }
38    fn is_accessed(self) -> bool {
39        self.contains(BindingFlags::ACCESSED)
40    }
41}
42
43#[derive(Clone, Debug, PartialEq)]
44struct Binding {
45    name: JsString,
46    index: u32,
47    flags: BindingFlags,
48}
49
50impl Binding {
51    fn is_mutable(&self) -> bool {
52        self.flags.is_mutable()
53    }
54    fn is_lex(&self) -> bool {
55        self.flags.is_lex()
56    }
57    fn is_strict(&self) -> bool {
58        self.flags.is_strict()
59    }
60    fn escapes(&self) -> bool {
61        self.flags.escapes()
62    }
63    fn is_accessed(&self) -> bool {
64        self.flags.is_accessed()
65    }
66}
67
68/// A scope maps bound identifiers to their binding positions.
69///
70/// It can be either a global scope or a function scope or a declarative scope.
71#[derive(Clone, PartialEq)]
72pub struct Scope {
73    inner: Rc<Inner>,
74}
75
76impl Debug for Scope {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        f.debug_struct("Scope")
79            .field("outer", &self.inner.outer)
80            .field("index", &self.inner.index)
81            .field("bindings", &self.inner.bindings)
82            .field("function", &self.inner.function)
83            .finish()
84    }
85}
86
87impl Default for Scope {
88    fn default() -> Self {
89        Self::new_global()
90    }
91}
92
93#[cfg(feature = "arbitrary")]
94impl<'a> arbitrary::Arbitrary<'a> for Scope {
95    fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
96        Ok(Self::new_global())
97    }
98}
99
100#[derive(Debug, PartialEq)]
101pub(crate) struct Inner {
102    unique_id: u32,
103    outer: Option<Scope>,
104    index: Cell<u32>,
105    bindings: RefCell<Vec<Binding>>,
106    // Position of each binding by name. Bindings are only ever pushed and names
107    // are unique per scope, so this mirrors `bindings` and turns every lookup
108    // from a scan into a hash probe; large bundles reference thousands of
109    // names in one scope.
110    names: RefCell<FxHashMap<JsString, usize>>,
111    function: bool,
112    // Has the `this` been accessed/escaped outside the function environment boundary.
113    this_escaped: Cell<bool>,
114
115    context: Rc<ScopeContext>,
116}
117
118impl Scope {
119    /// Creates a new global scope.
120    #[must_use]
121    pub fn new_global() -> Self {
122        Self {
123            inner: Rc::new(Inner {
124                unique_id: 0,
125                outer: None,
126                index: Cell::default(),
127                bindings: RefCell::default(),
128                names: RefCell::default(),
129                function: true,
130                this_escaped: Cell::new(false),
131                context: Rc::default(),
132            }),
133        }
134    }
135
136    /// Creates a new scope.
137    #[must_use]
138    pub fn new(parent: Self, function: bool) -> Self {
139        Self {
140            inner: Rc::new(Inner {
141                unique_id: parent.inner.context.next_unique_id(),
142                index: Cell::new(parent.inner.index.get() + 1),
143                bindings: RefCell::default(),
144                names: RefCell::default(),
145                function,
146                this_escaped: Cell::new(false),
147                context: parent.inner.context.clone(),
148                outer: Some(parent),
149            }),
150        }
151    }
152
153    /// Checks if the scope has only local bindings.
154    #[must_use]
155    pub fn all_bindings_local(&self) -> bool {
156        // if self.inner.function && self.inn
157        self.inner
158            .bindings
159            .borrow()
160            .iter()
161            .all(|binding| !binding.escapes())
162    }
163
164    /// Marks all bindings in this scope as escaping.
165    pub fn escape_all_bindings(&self) {
166        for binding in self.inner.bindings.borrow_mut().iter_mut() {
167            binding.flags.insert(BindingFlags::ESCAPES);
168        }
169    }
170
171    /// Has this binding escaped.
172    #[must_use]
173    pub fn escaped_this(&self) -> bool {
174        self.inner.this_escaped.get()
175    }
176
177    /// The position of `name` in this scope's bindings, or `usize::MAX`, which
178    /// no slice index reaches, when the scope has no such binding.
179    fn position(&self, name: &JsString) -> usize {
180        self.inner
181            .names
182            .borrow()
183            .get(name)
184            .copied()
185            .unwrap_or(usize::MAX)
186    }
187
188    /// Check if the scope has a lexical binding with the given name.
189    #[must_use]
190    pub fn has_lex_binding(&self, name: &JsString) -> bool {
191        self.inner
192            .bindings
193            .borrow()
194            .get(self.position(name))
195            .is_some_and(Binding::is_lex)
196    }
197
198    /// Check if the scope has a binding with the given name.
199    #[must_use]
200    pub fn has_binding(&self, name: &JsString) -> bool {
201        self.position(name) < usize::MAX
202    }
203
204    /// Get the binding locator for a binding with the given name.
205    /// Fall back to the global scope if the binding is not found.
206    #[must_use]
207    pub fn get_identifier_reference(&self, name: JsString) -> IdentifierReference {
208        if let Some(binding) = self.inner.bindings.borrow().get(self.position(&name)) {
209            IdentifierReference::new(
210                BindingLocator::declarative(
211                    name,
212                    self.inner.index.get(),
213                    binding.index,
214                    self.inner.unique_id,
215                ),
216                binding.is_lex(),
217                binding.escapes(),
218            )
219        } else if let Some(outer) = &self.inner.outer {
220            outer.get_identifier_reference(name)
221        } else {
222            IdentifierReference::new(BindingLocator::global(name), false, true)
223        }
224    }
225
226    /// Returns the number of bindings in this scope.
227    #[must_use]
228    #[allow(clippy::cast_possible_truncation)]
229    pub fn num_bindings(&self) -> u32 {
230        self.inner.bindings.borrow().len() as u32
231    }
232
233    /// Returns the number of bindings in this scope that are not local.
234    #[must_use]
235    #[allow(clippy::cast_possible_truncation)]
236    pub fn num_bindings_non_local(&self) -> u32 {
237        self.inner
238            .bindings
239            .borrow()
240            .iter()
241            .filter(|binding| binding.escapes())
242            .count() as u32
243    }
244
245    /// Adjust the binding indices to exclude local bindings.
246    pub(crate) fn reorder_binding_indices(&self) {
247        let mut bindings = self.inner.bindings.borrow_mut();
248        let mut index = 0;
249        for binding in bindings.iter_mut() {
250            if !binding.escapes() {
251                binding.index = 0;
252                continue;
253            }
254            binding.index = index;
255            index += 1;
256        }
257    }
258
259    /// Returns the index of this scope.
260    #[must_use]
261    pub fn scope_index(&self) -> u32 {
262        self.inner.index.get()
263    }
264
265    /// Set the index of this scope.
266    pub(crate) fn set_index(&self, index: u32) {
267        self.inner.index.set(index);
268    }
269
270    /// Check if the scope is a function scope.
271    #[must_use]
272    pub fn is_function(&self) -> bool {
273        self.inner.function
274    }
275
276    /// Check if the scope is a global scope.
277    #[must_use]
278    pub fn is_global(&self) -> bool {
279        self.inner.outer.is_none()
280    }
281
282    /// Check if a binding with the given name is mutable.
283    ///
284    /// Returns `Some(true)` for mutable bindings (`let`, `var`),
285    /// `Some(false)` for immutable bindings (`const`),
286    /// or `None` if the binding is not found in this or any outer scope.
287    #[must_use]
288    pub fn is_binding_mutable(&self, name: &JsString) -> Option<bool> {
289        if let Some(binding) = self.inner.bindings.borrow().get(self.position(name)) {
290            Some(binding.is_mutable())
291        } else if let Some(outer) = &self.inner.outer {
292            outer.is_binding_mutable(name)
293        } else {
294            None
295        }
296    }
297
298    /// Get the locator for a binding name.
299    #[must_use]
300    pub fn get_binding(&self, name: &JsString) -> Option<BindingLocator> {
301        self.inner
302            .bindings
303            .borrow()
304            .get(self.position(name))
305            .map(|binding| {
306                BindingLocator::declarative(
307                    name.clone(),
308                    self.inner.index.get(),
309                    binding.index,
310                    self.inner.unique_id,
311                )
312            })
313    }
314
315    /// Get the locator for a binding name.
316    #[must_use]
317    pub fn get_binding_reference(&self, name: &JsString) -> Option<IdentifierReference> {
318        self.inner
319            .bindings
320            .borrow()
321            .get(self.position(name))
322            .map(|binding| {
323                IdentifierReference::new(
324                    BindingLocator::declarative(
325                        name.clone(),
326                        self.inner.index.get(),
327                        binding.index,
328                        self.inner.unique_id,
329                    ),
330                    binding.is_lex(),
331                    binding.escapes(),
332                )
333            })
334    }
335
336    /// Simulate a binding access.
337    ///
338    /// - If the binding access crosses a function border, the binding is marked as escaping.
339    /// - If the binding access is in an eval or with scope, the binding is marked as escaping.
340    pub fn access_binding(&self, name: &JsString, eval_or_with: bool) {
341        let mut crossed_function_border = false;
342        let mut current = self;
343        loop {
344            if let Some(binding) = current
345                .inner
346                .bindings
347                .borrow_mut()
348                .get_mut(current.position(name))
349            {
350                binding.flags.insert(BindingFlags::ACCESSED);
351                if crossed_function_border || eval_or_with {
352                    binding.flags.insert(BindingFlags::ESCAPES);
353                }
354                return;
355            }
356            if let Some(outer) = &current.inner.outer {
357                if current.inner.function {
358                    crossed_function_border = true;
359                }
360                current = outer;
361            } else {
362                return;
363            }
364        }
365    }
366
367    /// Escape enclosing function environment's `this`.
368    pub fn escape_this_in_enclosing_function_scope(&self) {
369        let mut current = self;
370        let mut crossed_function_border = false;
371
372        loop {
373            if crossed_function_border && current.is_function() {
374                current.inner.this_escaped.set(true);
375                return;
376            }
377            if let Some(outer) = &current.inner.outer {
378                if current.is_function() {
379                    crossed_function_border = true;
380                }
381                current = outer;
382            } else {
383                return;
384            }
385        }
386    }
387
388    /// Creates a mutable binding.
389    #[must_use]
390    #[allow(clippy::cast_possible_truncation)]
391    pub fn create_mutable_binding(&self, name: JsString, function_scope: bool) -> BindingLocator {
392        let mut bindings = self.inner.bindings.borrow_mut();
393        let binding_index = bindings.len() as u32;
394        if let Some(binding) = bindings.get(self.position(&name)) {
395            return BindingLocator::declarative(
396                name,
397                self.inner.index.get(),
398                binding.index,
399                self.inner.unique_id,
400            );
401        }
402        let mut flags = BindingFlags::MUTABLE;
403        flags.set(BindingFlags::LEX, !function_scope);
404        flags.set(BindingFlags::ESCAPES, self.is_global());
405        bindings.push(Binding {
406            name: name.clone(),
407            index: binding_index,
408            flags,
409        });
410        self.inner
411            .names
412            .borrow_mut()
413            .insert(name.clone(), bindings.len() - 1);
414        BindingLocator::declarative(
415            name,
416            self.inner.index.get(),
417            binding_index,
418            self.inner.unique_id,
419        )
420    }
421
422    /// Crate an immutable binding.
423    #[allow(clippy::cast_possible_truncation)]
424    pub(crate) fn create_immutable_binding(&self, name: JsString, strict: bool) {
425        let mut bindings = self.inner.bindings.borrow_mut();
426        if self.position(&name) < usize::MAX {
427            return;
428        }
429        let binding_index = bindings.len() as u32;
430        let mut flags = BindingFlags::LEX;
431        flags.set(BindingFlags::STRICT, strict);
432        flags.set(BindingFlags::ESCAPES, self.is_global());
433        bindings.push(Binding {
434            name: name.clone(),
435            index: binding_index,
436            flags,
437        });
438        self.inner
439            .names
440            .borrow_mut()
441            .insert(name, bindings.len() - 1);
442    }
443
444    /// Return the binding locator for a mutable binding.
445    ///
446    /// # Errors
447    /// Returns an error if the binding is not mutable or does not exist.
448    pub fn set_mutable_binding(
449        &self,
450        name: JsString,
451    ) -> Result<IdentifierReference, BindingLocatorError> {
452        Ok(
453            match self.inner.bindings.borrow().get(self.position(&name)) {
454                Some(binding) if binding.is_mutable() => IdentifierReference::new(
455                    BindingLocator::declarative(
456                        name,
457                        self.inner.index.get(),
458                        binding.index,
459                        self.inner.unique_id,
460                    ),
461                    binding.is_lex(),
462                    binding.escapes(),
463                ),
464                Some(binding) if binding.is_strict() => {
465                    return Err(BindingLocatorError::MutateImmutable);
466                }
467                Some(_) => return Err(BindingLocatorError::Silent),
468                None => self.inner.outer.as_ref().map_or_else(
469                    || {
470                        Ok(IdentifierReference::new(
471                            BindingLocator::global(name.clone()),
472                            false,
473                            true,
474                        ))
475                    },
476                    |outer| outer.set_mutable_binding(name.clone()),
477                )?,
478            },
479        )
480    }
481
482    #[cfg(feature = "annex-b")]
483    /// Return the binding locator for a set operation on an existing var binding.
484    ///
485    /// # Errors
486    /// Returns an error if the binding is not mutable or does not exist.
487    pub fn set_mutable_binding_var(
488        &self,
489        name: JsString,
490    ) -> Result<IdentifierReference, BindingLocatorError> {
491        if !self.is_function() {
492            return self.inner.outer.as_ref().map_or_else(
493                || {
494                    Ok(IdentifierReference::new(
495                        BindingLocator::global(name.clone()),
496                        false,
497                        true,
498                    ))
499                },
500                |outer| outer.set_mutable_binding_var(name.clone()),
501            );
502        }
503
504        Ok(
505            match self.inner.bindings.borrow().get(self.position(&name)) {
506                Some(binding) if binding.is_mutable() => IdentifierReference::new(
507                    BindingLocator::declarative(
508                        name,
509                        self.inner.index.get(),
510                        binding.index,
511                        self.inner.unique_id,
512                    ),
513                    binding.is_lex(),
514                    binding.escapes(),
515                ),
516                Some(binding) if binding.is_strict() => {
517                    return Err(BindingLocatorError::MutateImmutable);
518                }
519                Some(_) => return Err(BindingLocatorError::Silent),
520                None => self.inner.outer.as_ref().map_or_else(
521                    || {
522                        Ok(IdentifierReference::new(
523                            BindingLocator::global(name.clone()),
524                            false,
525                            true,
526                        ))
527                    },
528                    |outer| outer.set_mutable_binding_var(name.clone()),
529                )?,
530            },
531        )
532    }
533
534    /// Gets the outer scope of this scope.
535    #[must_use]
536    pub fn outer(&self) -> Option<&Self> {
537        self.inner.outer.as_ref()
538    }
539
540    /// Returns the unique ID of this scope.
541    #[must_use]
542    pub fn unique_id(&self) -> u32 {
543        self.inner.unique_id
544    }
545}
546
547/// Additional state that all Scopes of a single AST share for bookkeeping.
548#[derive(Debug, PartialEq, Default)]
549struct ScopeContext {
550    /// A counter for unique IDs for Scopes generated as part of this scope tree.
551    ///
552    /// The value is the highest unique ID assigned so far (initialized with 0
553    /// which is also the unique ID of the global scope).
554    ///
555    /// Only used in the root scope.
556    unique_id_ctr: Cell<u32>,
557}
558
559impl ScopeContext {
560    /// Returns the next unique ID for a scope in this scope tree.
561    fn next_unique_id(&self) -> u32 {
562        let id = self.unique_id_ctr.get() + 1;
563        self.unique_id_ctr.set(id);
564        id
565    }
566}
567
568/// A reference to an identifier in a scope.
569#[derive(Clone, Debug, PartialEq, Eq, Hash)]
570pub struct IdentifierReference {
571    locator: BindingLocator,
572    lexical: bool,
573    escapes: bool,
574}
575
576impl IdentifierReference {
577    /// Create a new identifier reference.
578    pub(crate) fn new(locator: BindingLocator, lexical: bool, escapes: bool) -> Self {
579        Self {
580            locator,
581            lexical,
582            escapes,
583        }
584    }
585
586    /// Get the binding locator for this identifier reference.
587    #[must_use]
588    pub fn locator(&self) -> BindingLocator {
589        self.locator.clone()
590    }
591
592    /// Returns if the binding can be function local.
593    #[must_use]
594    pub fn local(&self) -> bool {
595        self.locator.scope > 0 && !self.escapes
596    }
597
598    /// Returns if the binding is on the global object.
599    #[must_use]
600    pub fn is_global_object(&self) -> bool {
601        self.locator.scope == 0
602    }
603
604    /// Check if this identifier reference is lexical.
605    #[must_use]
606    pub fn is_lexical(&self) -> bool {
607        self.lexical
608    }
609}
610
611/// A binding locator contains all information about a binding that is needed to resolve it at runtime.
612#[derive(Clone, Debug, Eq, Hash, PartialEq)]
613pub struct BindingLocator {
614    /// Name of the binding.
615    name: JsString,
616
617    /// Scope of the binding.
618    /// - 0: Global object
619    /// - 1: Global declarative scope
620    /// - n: Stack scope at index n - 2
621    scope: u32,
622
623    /// Index of the binding in the scope.
624    binding_index: u32,
625
626    unique_scope_id: u32,
627}
628
629impl BindingLocator {
630    /// Creates a new declarative binding locator that has knows indices.
631    pub(crate) const fn declarative(
632        name: JsString,
633        scope_index: u32,
634        binding_index: u32,
635        unique_scope_id: u32,
636    ) -> Self {
637        Self {
638            name,
639            scope: scope_index + 1,
640            binding_index,
641            unique_scope_id,
642        }
643    }
644
645    /// Creates a binding locator that indicates that the binding is on the global object.
646    pub(super) const fn global(name: JsString) -> Self {
647        Self {
648            name,
649            scope: 0,
650            binding_index: 0,
651            unique_scope_id: 0,
652        }
653    }
654
655    /// Returns the name of the binding.
656    #[must_use]
657    pub const fn name(&self) -> &JsString {
658        &self.name
659    }
660
661    /// Returns if the binding is located on the global object.
662    #[must_use]
663    pub const fn is_global(&self) -> bool {
664        self.scope == 0
665    }
666
667    /// Returns the scope of the binding.
668    #[must_use]
669    pub fn scope(&self) -> BindingLocatorScope {
670        match self.scope {
671            0 => BindingLocatorScope::GlobalObject,
672            1 => BindingLocatorScope::GlobalDeclarative,
673            n => BindingLocatorScope::Stack(n - 2),
674        }
675    }
676
677    /// Sets the scope of the binding.
678    pub fn set_scope(&mut self, scope: BindingLocatorScope) {
679        self.scope = match scope {
680            BindingLocatorScope::GlobalObject => 0,
681            BindingLocatorScope::GlobalDeclarative => 1,
682            BindingLocatorScope::Stack(index) => index + 2,
683        };
684    }
685
686    /// Returns the binding index of the binding.
687    #[must_use]
688    pub const fn binding_index(&self) -> u32 {
689        self.binding_index
690    }
691
692    /// Sets the binding index of the binding.
693    pub fn set_binding_index(&mut self, index: u32) {
694        self.binding_index = index;
695    }
696
697    /// Returns the unique scope ID of the binding.
698    #[must_use]
699    pub fn unique_scope_id(&self) -> u32 {
700        self.unique_scope_id
701    }
702}
703
704/// Action that is returned when a fallible binding operation.
705#[derive(Copy, Clone, Debug)]
706pub enum BindingLocatorError {
707    /// Trying to mutate immutable binding,
708    MutateImmutable,
709
710    /// Indicates that any action is silently ignored.
711    Silent,
712}
713
714/// The scope in which a binding is located.
715#[derive(Clone, Copy, Debug, PartialEq, Eq)]
716pub enum BindingLocatorScope {
717    /// The binding is located on the global object.
718    GlobalObject,
719
720    /// The binding is located in the global declarative scope.
721    GlobalDeclarative,
722
723    /// The binding is located in the scope stack at the given index.
724    Stack(u32),
725}
726
727/// A collection of function scopes.
728#[derive(Clone, Debug, Default, PartialEq)]
729pub struct FunctionScopes {
730    pub(crate) function_scope: Scope,
731    pub(crate) parameters_eval_scope: Option<Scope>,
732    pub(crate) parameters_scope: Option<Scope>,
733    pub(crate) lexical_scope: Option<Scope>,
734    pub(crate) mapped_arguments_object: bool,
735    pub(crate) requires_function_scope: bool,
736}
737
738impl FunctionScopes {
739    /// Returns the function scope for this function.
740    #[must_use]
741    pub fn function_scope(&self) -> &Scope {
742        &self.function_scope
743    }
744
745    /// Returns if the arguments object is accessed in this function.
746    #[must_use]
747    pub fn arguments_object_accessed(&self) -> bool {
748        if self
749            .function_scope
750            .inner
751            .bindings
752            .borrow()
753            .first()
754            .as_ref()
755            .is_some_and(|b| b.name == "arguments" && b.is_accessed())
756        {
757            return true;
758        }
759
760        if let Some(scope) = &self.parameters_eval_scope
761            && scope
762                .inner
763                .bindings
764                .borrow()
765                .first()
766                .as_ref()
767                .is_some_and(|b| b.name == "arguments" && b.is_accessed())
768        {
769            return true;
770        }
771
772        false
773    }
774
775    /// Check if the creation of the function scope is required.
776    #[must_use]
777    pub fn requires_function_scope(&self) -> bool {
778        self.requires_function_scope
779    }
780
781    /// Returns the parameters eval scope for this function.
782    #[must_use]
783    pub fn parameters_eval_scope(&self) -> Option<&Scope> {
784        self.parameters_eval_scope.as_ref()
785    }
786
787    /// Returns the parameters scope for this function.
788    #[must_use]
789    pub fn parameters_scope(&self) -> Option<&Scope> {
790        self.parameters_scope.as_ref()
791    }
792
793    /// Returns the lexical scope for this function.
794    #[must_use]
795    pub fn lexical_scope(&self) -> Option<&Scope> {
796        self.lexical_scope.as_ref()
797    }
798
799    /// Returns the effective parameter scope for this function.
800    #[must_use]
801    pub fn parameter_scope(&self) -> Scope {
802        if let Some(parameters_eval_scope) = &self.parameters_eval_scope {
803            return parameters_eval_scope.clone();
804        }
805        self.function_scope.clone()
806    }
807
808    /// Returns the effective body scope for this function.
809    pub(crate) fn body_scope(&self) -> Scope {
810        if let Some(lexical_scope) = &self.lexical_scope {
811            return lexical_scope.clone();
812        }
813        if let Some(parameters_scope) = &self.parameters_scope {
814            return parameters_scope.clone();
815        }
816        if let Some(parameters_eval_scope) = &self.parameters_eval_scope {
817            return parameters_eval_scope.clone();
818        }
819        self.function_scope.clone()
820    }
821
822    /// Marks all bindings in all scopes as escaping.
823    pub(crate) fn escape_all_bindings(&self) {
824        self.function_scope.escape_all_bindings();
825        if let Some(parameters_eval_scope) = &self.parameters_eval_scope {
826            parameters_eval_scope.escape_all_bindings();
827        }
828        if let Some(parameters_scope) = &self.parameters_scope {
829            parameters_scope.escape_all_bindings();
830        }
831        if let Some(lexical_scope) = &self.lexical_scope {
832            lexical_scope.escape_all_bindings();
833        }
834    }
835
836    pub(crate) fn reorder_binding_indices(&self) {
837        self.function_scope.reorder_binding_indices();
838        if let Some(parameters_eval_scope) = &self.parameters_eval_scope {
839            parameters_eval_scope.reorder_binding_indices();
840        }
841        if let Some(parameters_scope) = &self.parameters_scope {
842            parameters_scope.reorder_binding_indices();
843        }
844        if let Some(lexical_scope) = &self.lexical_scope {
845            lexical_scope.reorder_binding_indices();
846        }
847    }
848}
849
850#[cfg(feature = "arbitrary")]
851impl<'a> arbitrary::Arbitrary<'a> for FunctionScopes {
852    fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
853        Ok(Self {
854            function_scope: Scope::new_global(),
855            parameters_eval_scope: None,
856            parameters_scope: None,
857            lexical_scope: None,
858            mapped_arguments_object: false,
859            requires_function_scope: false,
860        })
861    }
862}