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