Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/jsparagus-scope/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 125 kB image not shown  

Quelle  builder.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

//! Data collection.
//!
//! `ScopeBuilder` is the entry point of this module.
//!
//! Each AST node that will hold bindings (global, block, function, etc.) has a
//! corresponding scope builder type defined in this module:
//!   * `GlobalScopeBuilder`
//!   * `BlockScopeBuilder`
//!   * `FunctionExpressionScopeBuilder`
//!   * `FunctionParametersScopeBuilder`
//!   * `FunctionBodyScopeBuilder`
//!
//! They follow the pattern:
//!   * They are created and pushed to the `ScopeBuilderStack` when the
//!     algorithm enters a scope.
//!   * They collect the information necessary to build a `ScopeData` object
//!     (which will eventually become a `js::ScopeCreationData` on the
//!      C++ side).
//!   * They stay on the scope builder stack until the algorithm leaves that
//!     scope.
//!   * Then they are converted by `into_scope_data()`.
//!
//! Fields in the builder types mostly correspond to local variables in spec
//! algorithms.  For example, `GlobalScopeBuilder` has fields named
//! `functions_to_initialize`, `declared_function_names`, and
//! `declared_var_names` which correspond to the
//! [GlobalDeclarationInstantiation][1] algorithm's local variables
//! *functionsToInitialize*, *declaredFunctionNames*, and *declaredVarNames*.
//!
//! This module performs some steps of those algorithms -- the parts that can
//! be done at compile time. The results are passed along to the emitter and
//! ultimately the JS runtime.
//!
//! # Syntax-only mode
//!
//! When doing syntax-only parsing, we collect minimal information, that does
//! not include `ScopeData`, but `ScriptStencil` for functions.
//!
//! [1]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation

use crate::data::FunctionDeclarationPropertyMap;
use crate::free_name_tracker::FreeNameTracker;
use ast::associated_data::AssociatedData;
use ast::source_atom_set::{CommonSourceAtomSetIndices, SourceAtomSetIndex};
use ast::source_location_accessor::SourceLocationAccessor;
use ast::type_id::NodeTypeIdAccessor;
use indexmap::set::IndexSet;
use std::collections::hash_map::Keys;
use std::collections::{HashMap, HashSet};
use stencil::function::{FunctionFlags, FunctionSyntaxKind};
use stencil::scope::{
    BindingName, FunctionScopeData, GlobalScopeData, LexicalScopeData, ScopeData, ScopeDataList,
    ScopeDataMap, ScopeIndex, VarScopeData,
};
use stencil::script::{ScriptStencil, ScriptStencilIndex, ScriptStencilList, SourceExtent};

/// The kind of items inside the result of VarScopedDeclarations.
///
/// This enum isn't actually used, but just for simplifying comment in
/// ScopeKind.
#[derive(Debug, Clone, PartialEq)]
enum VarScopedDeclarationsItemKind {
    /// Static Semantics: VarScopedDeclarations
    /// https://tc39.es/ecma262/#sec-variable-statement-static-semantics-varscopeddeclarations
    ///
    /// VariableDeclarationList : VariableDeclaration
    ///
    /// 1. Return a new List containing VariableDeclaration.
    ///
    /// VariableDeclarationList : VariableDeclarationList, VariableDeclaration
    ///
    /// 1. Let declarations be VarScopedDeclarations of VariableDeclarationList.
    /// 2. Append VariableDeclaration to declarations.
    /// 3. Return declarations.
    #[allow(dead_code)]
    VariableDeclaration,

    /// Static Semantics: VarScopedDeclarations
    /// https://tc39.es/ecma262/#sec-for-in-and-for-of-statements-static-semantics-varscopeddeclarations
    ///
    /// IterationStatement :
    ///   for ( var ForBinding in Expression ) Statement
    ///   for ( var ForBinding of AssignmentExpression ) Statement
    ///   for await ( var ForBinding of AssignmentExpression ) Statement
    ///
    /// 1. Let declarations be a List containing ForBinding.
    /// 2. Append to declarations the elements of the VarScopedDeclarations of
    ///    Statement.
    /// 3. Return declarations.
    #[allow(dead_code)]
    ForBinding,

    /// Static Semantics: VarScopedDeclarations
    /// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-varscopeddeclarations
    ///
    /// FunctionStatementList : StatementList
    ///
    /// 1. Return the TopLevelVarScopedDeclarations of StatementList.

    /// Static Semantics: VarScopedDeclarations
    /// https://tc39.es/ecma262/#sec-scripts-static-semantics-varscopeddeclarations
    ///
    /// ScriptBody : StatementList
    ///
    /// 1. Return TopLevelVarScopedDeclarations of StatementList.

    /// Static Semantics: TopLevelVarScopedDeclarations
    /// https://tc39.es/ecma262/#sec-block-static-semantics-toplevelvarscopeddeclarations
    ///
    /// StatementListItem : Declaration
    ///
    /// 1. If Declaration is Declaration : HoistableDeclaration, then
    ///   a. Let declaration be DeclarationPart of HoistableDeclaration.
    ///   b. Return « declaration ».
    /// 2. Return a new empty List.

    /// Static Semantics: DeclarationPart
    /// https://tc39.es/ecma262/#sec-static-semantics-declarationpart
    ///
    /// HoistableDeclaration : FunctionDeclaration
    ///
    /// 1. Return FunctionDeclaration.
    #[allow(dead_code)]
    FunctionDeclaration,

    /// HoistableDeclaration : GeneratorDeclaration
    ///
    /// 1. Return GeneratorDeclaration.
    #[allow(dead_code)]
    GeneratorDeclaration,

    /// HoistableDeclaration : AsyncFunctionDeclaration
    ///
    /// 1. Return AsyncFunctionDeclaration.
    #[allow(dead_code)]
    AsyncFunctionDeclaration,

    /// HoistableDeclaration : AsyncGeneratorDeclaration
    ///
    /// 1. Return AsyncGeneratorDeclaration.
    #[allow(dead_code)]
    AsyncGeneratorDeclaration,

    /// Static Semantics: TopLevelVarScopedDeclarations
    /// https://tc39.es/ecma262/#sec-labelled-statements-static-semantics-toplevelvarscopeddeclarations
    ///
    /// LabelledItem : FunctionDeclaration
    ///
    /// 1. Return a new List containing FunctionDeclaration.
    /* FunctionDeclaration */

    /// Annex B Initializers in ForIn Statement Heads
    /// https://tc39.es/ecma262/#sec-initializers-in-forin-statement-heads
    ///
    /// IterationStatement :
    ///   for ( var BindingIdentifier Initializer in Expression ) Statement
    ///
    /// 1. Let declarations be a List containing BindingIdentifier.
    /// 2. Append to declarations the elements of the VarScopedDeclarations of
    ///    Statement.
    /// 3. Return declarations.
    #[allow(dead_code)]
    BindingIdentifier,
}

/// The kind of items inside the result of LexicallyScopedDeclarations.
///
/// This enum isn't actually used, but just for simplifying comment in
/// ScopeKind.
#[derive(Debug, Clone, PartialEq)]
enum LexicallyScopedDeclarations {
    /// Static Semantics: LexicallyScopedDeclarations
    /// https://tc39.es/ecma262/#sec-block-static-semantics-lexicallyscopeddeclarations
    ///
    /// StatementListItem : Declaration
    ///
    /// 1. Return a new List containing DeclarationPart of Declaration.

    /// Static Semantics: DeclarationPart
    /// https://tc39.es/ecma262/#sec-static-semantics-declarationpart
    ///
    /// HoistableDeclaration : FunctionDeclaration
    ///
    /// 1. Return FunctionDeclaration.
    #[allow(dead_code)]
    FunctionDeclaration,

    /// HoistableDeclaration : GeneratorDeclaration
    ///
    /// 1. Return GeneratorDeclaration.
    #[allow(dead_code)]
    GeneratorDeclaration,

    /// HoistableDeclaration : AsyncFunctionDeclaration
    ///
    /// 1. Return AsyncFunctionDeclaration.
    #[allow(dead_code)]
    AsyncFunctionDeclaration,

    /// HoistableDeclaration : AsyncGeneratorDeclaration
    ///
    /// 1. Return AsyncGeneratorDeclaration.
    #[allow(dead_code)]
    AsyncGeneratorDeclaration,

    /// Declaration : ClassDeclaration
    ///
    /// 1. Return ClassDeclaration.
    #[allow(dead_code)]
    ClassDeclaration,

    /// Declaration : LexicalDeclaration
    ///
    /// 1. Return LexicalDeclaration.
    #[allow(dead_code)]
    LexicalDeclarationWithLet,
    #[allow(dead_code)]
    LexicalDeclarationWithConst,

    /// Static Semantics: LexicallyScopedDeclarations
    /// https://tc39.es/ecma262/#sec-labelled-statements-static-semantics-lexicallyscopeddeclarations
    ///
    /// LabelledItem : FunctionDeclaration
    ///
    /// 1. Return a new List containing FunctionDeclaration.
    /* FunctionDeclaration */

    /// Static Semantics: LexicallyScopedDeclarations
    /// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-lexicallyscopeddeclarations
    ///
    /// FunctionStatementList : StatementList
    ///
    /// 1. Return the TopLevelLexicallyScopedDeclarations of StatementList.

    /// Static Semantics: LexicallyScopedDeclarations
    /// https://tc39.es/ecma262/#sec-scripts-static-semantics-lexicallyscopeddeclarations
    ///
    /// ScriptBody : StatementList
    ///
    /// 1. Return TopLevelLexicallyScopedDeclarations of StatementList.

    /// Static Semantics: TopLevelLexicallyScopedDeclarations
    /// https://tc39.es/ecma262/#sec-block-static-semantics-toplevellexicallyscopeddeclarations
    ///
    /// StatementListItem : Declaration
    ///
    /// 1. If Declaration is Declaration : HoistableDeclaration, then
    ///   a. Return « ».
    /// 2. Return a new List containing Declaration.

    /// Static Semantics: LexicallyScopedDeclarations
    /// https://tc39.es/ecma262/#sec-exports-static-semantics-lexicallyscopeddeclarations
    ///
    /// ExportDeclaration : export Declaration
    ///
    /// 1. Return a new List containing DeclarationPart of Declaration.

    /// ExportDeclaration : export default HoistableDeclaration
    ///
    /// 1. Return a new List containing DeclarationPart of HoistableDeclaration.

    /// ExportDeclaration : export default ClassDeclaration
    ///
    /// 1. Return a new List containing ClassDeclaration.
    /* ClassDeclaration */

    /// ExportDeclaration : export default AssignmentExpression ;
    ///
    /// 1. Return a new List containing this ExportDeclaration.
    #[allow(dead_code)]
    ExportDeclarationWithAssignmentExpression,
}

/// Items on the ScopeBuilder.scope_stack.
/// Specifies the kind of BindingIdentifier.
///
/// This includes only BindingIdentifier that appears inside list or recursive
/// structure.
///
/// BindingIdentifier that appears only once for a structure
/// (e.g. Function.name) should be handled immediately, without using
/// ScopeBuilder.scope_stack.
#[derive(Debug, Clone, PartialEq)]
enum ScopeKind {
    /// VarScopedDeclarationsItemKind::VariableDeclaration
    /// VarScopedDeclarationsItemKind::ForBinding
    /// VarScopedDeclarationsItemKind::BindingIdentifier
    Var,

    /// LexicallyScopedDeclarations::LexicalDeclarationWithLet
    Let,

    /// LexicallyScopedDeclarations::LexicalDeclarationWithConst
    Const,

    /// Pushed when entering function, to catch function name.
    FunctionName,

    /// Pushed when entering function parameter, to disable FunctionName's
    /// effect.
    /// Equivalent to the case there's no kind on the stack.
    FunctionParametersAndBody,

    FormalParameter,

    #[allow(dead_code)]
    CatchParameter,

    /// LexicallyScopedDeclarations::ExportDeclarationWithAssignmentExpression
    #[allow(dead_code)]
    Export,

    /// VarScopedDeclarationsItemKind::FunctionDeclaration
    /// VarScopedDeclarationsItemKind::GeneratorDeclaration
    /// VarScopedDeclarationsItemKind::AsyncFunctionDeclaration
    /// VarScopedDeclarationsItemKind::AsyncGeneratorDeclaration
    #[allow(dead_code)]
    ScriptBodyStatementList,
    #[allow(dead_code)]
    FunctionStatementList,

    /// LexicallyScopedDeclarations::FunctionDeclaration
    /// LexicallyScopedDeclarations::GeneratorDeclaration
    /// LexicallyScopedDeclarations::AsyncFunctionDeclaration
    /// LexicallyScopedDeclarations::AsyncGeneratorDeclaration
    /// LexicallyScopedDeclarations::ClassDeclaration
    #[allow(dead_code)]
    BlockStatementList,
}

/// Index into BaseScopeData.bindings.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BindingIndex {
    index: usize,
}
impl BindingIndex {
    fn new(index: usize) -> Self {
        Self { index }
    }

    pub fn next(&self) -> Self {
        Self {
            index: self.index + 1,
        }
    }
}

impl From<BindingIndex> for usize {
    fn from(index: BindingIndex) -> usize {
        index.index
    }
}

#[derive(Debug)]
struct PossiblyAnnexBFunction {
    #[allow(dead_code)]
    name: SourceAtomSetIndex,
    #[allow(dead_code)]
    owner_scope_index: ScopeIndex,
    #[allow(dead_code)]
    binding_index: BindingIndex,

    /// Index of the script in the list of `functions` in the
    /// `FunctionScriptStencilBuilder`.
    script_index: ScriptStencilIndex,
}

#[derive(Debug)]
struct PossiblyAnnexBFunctionList {
    functions: HashMap<SourceAtomSetIndex, Vec<PossiblyAnnexBFunction>>,
}

impl PossiblyAnnexBFunctionList {
    fn new() -> Self {
        Self {
            functions: HashMap::new(),
        }
    }

    fn push(
        &mut self,
        name: SourceAtomSetIndex,
        owner_scope_index: ScopeIndex,
        binding_index: BindingIndex,
        script_index: ScriptStencilIndex,
    ) {
        if let Some(functions) = self.functions.get_mut(&name) {
            functions.push(PossiblyAnnexBFunction {
                name,
                owner_scope_index,
                binding_index,
                script_index,
            });
            return;
        }

        let mut functions = Vec::with_capacity(1);
        functions.push(PossiblyAnnexBFunction {
            name,
            owner_scope_index,
            binding_index,
            script_index,
        });
        self.functions.insert(name, functions);
    }

    fn remove_if_exists(&mut self, name: SourceAtomSetIndex) {
        self.functions.remove(&name);
    }

    fn mark_annex_b(&self, function_declaration_properties: &mut FunctionDeclarationPropertyMap) {
        for functions in &mut self.functions.values() {
            for fun in functions {
                function_declaration_properties.mark_annex_b(fun.script_index);
            }
        }
    }

    fn names(&self) -> Keys<SourceAtomSetIndex, Vec<PossiblyAnnexBFunction>> {
        self.functions.keys()
    }

    fn clear(&mut self) {
        self.functions.clear();
    }
}

/// Common fields across all *ScopeBuilder.
#[derive(Debug)]
struct BaseScopeBuilder {
    name_tracker: FreeNameTracker,

    /// Bindings in this scope can be accessed dynamically by:
    ///   * direct `eval`
    ///   * `with` statement
    ///   * `delete name` statement
    bindings_accessed_dynamically: bool,
}

impl BaseScopeBuilder {
    fn new() -> Self {
        Self {
            name_tracker: FreeNameTracker::new(),
            bindings_accessed_dynamically: false,
        }
    }

    fn propagate_common(&mut self, inner: &BaseScopeBuilder) {
        // When construct such as `eval`, `with` and `delete` access
        // name dynamically in inner scopes, we have to propagate this
        // flag to the outer scope such that we prevent optimizations.
        self.bindings_accessed_dynamically |= inner.bindings_accessed_dynamically;
    }

    fn propagate_from_inner_non_script(&mut self, inner: &BaseScopeBuilder) {
        self.propagate_common(inner);
        self.name_tracker
            .propagate_from_inner_non_script(&inner.name_tracker);
    }

    fn propagate_from_inner_script(&mut self, inner: &BaseScopeBuilder) {
        self.propagate_common(inner);
        self.name_tracker
            .propagate_from_inner_script(&inner.name_tracker);
    }

    fn declare_var(&mut self, name: SourceAtomSetIndex) {
        self.name_tracker.note_def(name);
    }

    fn declare_let(&mut self, name: SourceAtomSetIndex) {
        self.name_tracker.note_def(name);
    }

    fn declare_const(&mut self, name: SourceAtomSetIndex) {
        self.name_tracker.note_def(name);
    }

    fn declare_function(&mut self, name: SourceAtomSetIndex) {
        self.name_tracker.note_def(name);
    }

    fn set_function_name(&mut self, name: SourceAtomSetIndex) {
        self.name_tracker.note_def(name);
    }

    fn declare_param(&mut self, name: SourceAtomSetIndex) {
        self.name_tracker.note_def(name);
    }
}

/// Variables declared/used in GlobalDeclarationInstantiation.
#[derive(Debug)]
struct GlobalScopeBuilder {
    base: BaseScopeBuilder,

    /// Runtime Semantics: GlobalDeclarationInstantiation ( script, env )
    /// https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
    ///
    /// Step 8. Let functionsToInitialize be a new empty List.
    functions_to_initialize: Vec<ScriptStencilIndex>,

    /// Step 9. Let declaredFunctionNames be a new empty List.
    declared_function_names: IndexSet<SourceAtomSetIndex>,

    /// Step 11. Let declaredVarNames be a new empty List.
    /// NOTE: This is slightly different than the spec that this can contain
    ///       names in declaredFunctionNames.
    ///       The duplication should be filtered before the use.
    declared_var_names: IndexSet<SourceAtomSetIndex>,

    /// Step 15. Let lexDeclarations be the LexicallyScopedDeclarations of
    ///          script.
    let_names: Vec<SourceAtomSetIndex>,
    const_names: Vec<SourceAtomSetIndex>,

    scope_index: ScopeIndex,
}

impl GlobalScopeBuilder {
    fn new(scope_index: ScopeIndex) -> Self {
        Self {
            base: BaseScopeBuilder::new(),
            functions_to_initialize: Vec::new(),
            declared_function_names: IndexSet::new(),
            declared_var_names: IndexSet::new(),
            let_names: Vec::new(),
            const_names: Vec::new(),
            scope_index,
        }
    }

    fn declare_var(&mut self, name: SourceAtomSetIndex) {
        // Runtime Semantics: GlobalDeclarationInstantiation ( script, env )
        // https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
        //
        // Step 7. Let varDeclarations be the VarScopedDeclarations of script.
        //
        // Step 12. For each d in varDeclarations, do
        // Step 12.a. If d is a VariableDeclaration, a ForBinding, or a
        //            BindingIdentifier, then
        // Step 12.a.i. For each String vn in the BoundNames of d, do
        // (implicit)

        // Step 12.a.i.i If vn is not an element of declaredFunctionNames, then
        // (done in remove_function_names_from_var_names)

        // Step 12.a.i.1.a. Let vnDefinable be ? envRec.CanDeclareGlobalVar(vn).
        // Step 12.a.i.1.b. If vnDefinable is false, throw a TypeError
        //                  exception.
        // (done in runtime)

        // Step 12.a.i.1.c. If vn is not an element of declaredVarNames, then
        // Step 12.a.i.1.a.i. Append vn to declaredVarNames.
        self.declared_var_names.insert(name);
        self.base.declare_var(name);
    }

    fn declare_let(&mut self, name: SourceAtomSetIndex) {
        // Runtime Semantics: GlobalDeclarationInstantiation ( script, env )
        // https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
        //
        // Step 15. Let lexDeclarations be the LexicallyScopedDeclarations of
        //          script.
        self.let_names.push(name);
        self.base.declare_let(name);
    }

    fn declare_const(&mut self, name: SourceAtomSetIndex) {
        // Runtime Semantics: GlobalDeclarationInstantiation ( script, env )
        // https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
        //
        // Step 15. Let lexDeclarations be the LexicallyScopedDeclarations of
        //          script.
        self.const_names.push(name);
        self.base.declare_const(name);
    }

    fn declare_function(&mut self, name: SourceAtomSetIndex, fun_index: ScriptStencilIndex) {
        // Runtime Semantics: GlobalDeclarationInstantiation ( script, env )
        // https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
        //
        // Step 10. For each d in varDeclarations, in reverse list order, do
        // Step 10.a. If d is neither a VariableDeclaration nor a ForBinding
        //            nor a BindingIdentifier, then
        // (implicit)

        // Step 10.a.i. Assert: d is either a FunctionDeclaration,
        //              a GeneratorDeclaration, an AsyncFunctionDeclaration,
        //              or an AsyncGeneratorDeclaration.

        // Step 10.a.ii. NOTE: If there are multiple function declarations for
        //               the same name, the last declaration is used.

        // Step 10.a.iii. Let fn be the sole element of the BoundNames of d.

        // Step 10.a.iv. If fn is not an element of declaredFunctionNames, then
        //
        // NOTE: Instead of iterating in reverse list oder, we iterate in
        // normal order and overwrite existing item.

        // Steps 10.a.iv.1. Let fnDefinable be
        //                  ? envRec.CanDeclareGlobalFunction(fn).
        // Steps 10.a.iv.2. If fnDefinable is false, throw a TypeError
        //                  exception.
        // (done in runtime)

        // Step 10.a.iv.3. Append fn to declaredFunctionNames.
        self.declared_function_names.insert(name.clone());

        // Step 10.a.iv.4. Insert d as the first element of
        //                 functionsToInitialize.
        self.functions_to_initialize.push(fun_index);

        self.base.declare_function(name);
    }

    fn remove_function_names_from_var_names(&mut self) {
        // Runtime Semantics: GlobalDeclarationInstantiation ( script, env )
        // https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
        //
        // Step 12.a.i.i If vn is not an element of declaredFunctionNames, then
        //
        // To avoid doing 2-pass, we note all var names, and filter function
        // names out after visiting all of them.
        for n in &self.declared_function_names {
            self.declared_var_names.remove(n);
        }
    }

    fn perform_annex_b(
        &mut self,
        function_declaration_properties: &mut FunctionDeclarationPropertyMap,
        possibly_annex_b_functions: &mut PossiblyAnnexBFunctionList,
    ) {
        // Annex B
        // Changes to GlobalDeclarationInstantiation
        // https://tc39.es/ecma262/#sec-web-compat-globaldeclarationinstantiation
        //
        // Step 1. Let strict be IsStrict of script.
        //
        // FIXME: Once directives are supported, reflect it here.
        let strict = false;

        // Step 2. If strict is false, then
        if strict {
            return;
        }

        // Step 2.a. Let declaredFunctionOrVarNames be a new empty List.
        // Step 2.b. Append to declaredFunctionOrVarNames the elements of
        //           declaredFunctionNames.
        // Step 2.c. Append to declaredFunctionOrVarNames the elements of
        //           declaredVarNames.
        //
        // NOTE: Use `self.declared_var_names` to avoid duplication against
        //       `declaredVarNames`.
        //       And duplication against `declaredFunctionNames` will be
        //       removed in `remove_function_names_from_var_names`.

        // Step 2.d. For each FunctionDeclaration f that is directly contained
        //           in the StatementList of a Block, CaseClause, or
        //           DefaultClause Contained within script, do
        //
        // NOTE: `possibly_annex_b_functions` contains all of them.

        // Step 2.d.i. Let F be StringValue of the BindingIdentifier of f.
        // Step 2.d.ii. If replacing the FunctionDeclaration f with a
        //              VariableStatement that has F as a BindingIdentifier
        //              would not produce any Early Errors for script, then
        //
        // NOTE: Early Errors happen if any of top-level lexical has
        //       the same name.  Filter out those functions here.
        for n in &self.let_names {
            possibly_annex_b_functions.remove_if_exists(*n);
        }
        for n in &self.const_names {
            possibly_annex_b_functions.remove_if_exists(*n);
        }

        // Step 2.d.ii.1. If env.HasLexicalDeclaration(F) is false, then
        // Step 2.d.ii.1.a. Let fnDefinable be ? env.CanDeclareGlobalVar(F).
        // Step 2.d.ii.1.b. If fnDefinable is true, then
        //
        // FIXME: Are these steps performed by any implementation?
        //        https://github.com/tc39/ecma262/issues/2019

        // Step 2.d.ii.1.b.i. NOTE: A var binding for F is only instantiated
        //                    here if it is neither a VarDeclaredName nor
        //                    the name of another FunctionDeclaration.
        // Step 2.d.ii.1.b.ii. If declaredFunctionOrVarNames does not
        //                     contain F, then
        // Step 2.d.ii.1.b.ii.1. Perform
        //                       ?env.CreateGlobalVarBinding(F, false).
        // Step 2.d.ii.1.b.ii.2. Append F to declaredFunctionOrVarNames.
        for n in possibly_annex_b_functions.names() {
            self.declare_var(*n);
        }

        // Step 2.d.ii.1.b.iii. When the FunctionDeclaration f is evaluated,
        //                      perform the following steps in place of the
        //                      FunctionDeclaration Evaluation algorithm
        //                      provided in
        //                      https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-evaluation :
        // Step 2.d.ii.1.b.iii.1. Let genv be the running execution
        //                        context's VariableEnvironment.
        // Step 2.d.ii.1.b.iii.2. Let benv be the running execution
        //                        context's LexicalEnvironment.
        // Step 2.d.ii.1.b.iii.3. Let fobj be
        //                        ! benv.GetBindingValue(F, false).
        // Step 2.d.ii.1.b.iii.4. Perform
        //                        ? genv.SetMutableBinding(F, fobj, false).
        // Step 2.d.ii.1.b.iii.5. Return NormalCompletion(empty).
        possibly_annex_b_functions.mark_annex_b(function_declaration_properties);
    }

    fn into_scope_data(
        mut self,
        function_declaration_properties: &mut FunctionDeclarationPropertyMap,
        possibly_annex_b_functions: &mut PossiblyAnnexBFunctionList,
    ) -> ScopeData {
        // Runtime Semantics: GlobalDeclarationInstantiation ( script, env )
        // https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
        //
        // NOTE: Steps are reordered to match the order of binding in runtime.

        // Step 13. NOTE: Annex B adds additional steps at this point.
        //
        // NOTE: Reordered here to reflect the change to
        //       self.declared_var_names.
        self.perform_annex_b(function_declaration_properties, possibly_annex_b_functions);

        // Step 12.a.i.i If vn is not an element of declaredFunctionNames, then
        self.remove_function_names_from_var_names();

        let mut data = GlobalScopeData::new(
            self.declared_var_names.len() + self.declared_function_names.len(),
            self.let_names.len(),
            self.const_names.len(),
            self.functions_to_initialize,
        );

        // Step 18. For each String vn in declaredVarNames, in list order, do
        for n in &self.declared_var_names {
            // 18.a. Perform ? envRec.CreateGlobalVarBinding(vn, false).
            let is_closed_over = self.base.name_tracker.is_closed_over_def(n);
            data.base
                .bindings
                .push(BindingName::new(*n, is_closed_over))
        }

        // Step 17. For each Parse Node f in functionsToInitialize, do
        for n in &self.declared_function_names {
            // Step 17.a. Let fn be the sole element of the BoundNames of f.
            // Step 17.b. Let fo be InstantiateFunctionObject of f with
            //            argument env.
            // Step 17.c. Perform
            //            ? envRec.CreateGlobalFunctionBinding(fn, fo, false).
            let is_closed_over = self.base.name_tracker.is_closed_over_def(n);
            data.base
                .bindings
                .push(BindingName::new_top_level_function(*n, is_closed_over));
        }

        // Step 15. Let lexDeclarations be the LexicallyScopedDeclarations of
        //          script.
        // Step 16. For each element d in lexDeclarations, do
        // Step 16.b. For each element dn of the BoundNames of d, do
        for n in &self.let_names {
            // Step 16.b.ii. Else,
            // Step 16.b.ii.1. Perform ? envRec.CreateMutableBinding(dn, false).
            let is_closed_over = self.base.name_tracker.is_closed_over_def(n);
            data.base
                .bindings
                .push(BindingName::new(*n, is_closed_over))
        }
        for n in &self.const_names {
            // Step 16.b.i. If IsConstantDeclaration of d is true, then
            // Step 16.b.i.1. Perform ? envRec.CreateImmutableBinding(dn, true).
            let is_closed_over = self.base.name_tracker.is_closed_over_def(n);
            data.base
                .bindings
                .push(BindingName::new(*n, is_closed_over))
        }

        ScopeData::Global(data)
    }
}

#[derive(Debug)]
struct FunctionNameAndStencilIndex {
    name: SourceAtomSetIndex,
    stencil: ScriptStencilIndex,
}

/// Variables declared/used in BlockDeclarationInstantiation
#[derive(Debug)]
struct BlockScopeBuilder {
    base: BaseScopeBuilder,

    /// Runtime Semantics: BlockDeclarationInstantiation ( code, env )
    /// https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
    ///
    /// Step 3. Let declarations be the LexicallyScopedDeclarations of code.
    let_names: Vec<SourceAtomSetIndex>,
    const_names: Vec<SourceAtomSetIndex>,

    /// Runtime Semantics: BlockDeclarationInstantiation ( code, env )
    /// https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
    ///
    /// Step 4.b. If d is a FunctionDeclaration, a GeneratorDeclaration, an
    ///           AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration,
    ///           then
    functions: Vec<FunctionNameAndStencilIndex>,

    /// Scope associated to this builder.
    scope_index: ScopeIndex,
}

impl BlockScopeBuilder {
    fn new(scope_index: ScopeIndex) -> Self {
        Self {
            base: BaseScopeBuilder::new(),
            let_names: Vec::new(),
            const_names: Vec::new(),
            functions: Vec::new(),
            scope_index,
        }
    }

    fn declare_let(&mut self, name: SourceAtomSetIndex) {
        // Runtime Semantics: BlockDeclarationInstantiation ( code, env )
        // https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
        //
        // Step 3. Let declarations be the LexicallyScopedDeclarations of code.
        self.let_names.push(name);
        self.base.declare_let(name);
    }

    fn declare_const(&mut self, name: SourceAtomSetIndex) {
        // Runtime Semantics: BlockDeclarationInstantiation ( code, env )
        // https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
        //
        // Step 3. Let declarations be the LexicallyScopedDeclarations of code.
        self.const_names.push(name);
        self.base.declare_const(name);
    }

    fn declare_function(&mut self, name: SourceAtomSetIndex, fun_index: ScriptStencilIndex) {
        // Runtime Semantics: BlockDeclarationInstantiation ( code, env )
        // https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
        //
        // Step 3. Let declarations be the LexicallyScopedDeclarations of code.
        //
        // Step 4.b. If d is a FunctionDeclaration, a GeneratorDeclaration, an
        //           AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration,
        //           then
        self.functions.push(FunctionNameAndStencilIndex {
            name,
            stencil: fun_index,
        });

        self.base.declare_function(name);
    }

    fn into_scope_data(
        self,
        enclosing: ScopeIndex,
        possibly_annex_b_functions: &mut PossiblyAnnexBFunctionList,
    ) -> ScopeData {
        let mut data = LexicalScopeData::new_block(
            self.let_names.len() + self.functions.len(),
            self.const_names.len(),
            enclosing,
            self.functions.iter().map(|n| n.stencil).collect(),
        );

        // Runtime Semantics: BlockDeclarationInstantiation ( code, env )
        // https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
        //
        // Step 1. Let envRec be env's EnvironmentRecord.
        // Step 2. Assert: envRec is a declarative Environment Record.
        // (implicit)

        // Step 4. For each element d in declarations, do
        // Step 4.a. For each element dn of the BoundNames of d, do
        for n in &self.let_names {
            // Step 4.a.ii. Else,
            // Step 4.a.ii.1. Perform ! envRec.CreateMutableBinding(dn, false).
            let is_closed_over = self.base.name_tracker.is_closed_over_def(n);
            data.base
                .bindings
                .push(BindingName::new(*n, is_closed_over));
        }
        for n in &self.functions {
            // Step 4.b. If d is a FunctionDeclaration, a GeneratorDeclaration,
            //           an AsyncFunctionDeclaration,
            //           or an AsyncGeneratorDeclaration, then
            // Step 4.b.i. Let fn be the sole element of the BoundNames of d.
            // Step 4.b.ii. Let fo be InstantiateFunctionObject of d with
            //              argument env.
            // Step 4.b.iii. Perform envRec.InitializeBinding(fn, fo).
            let is_closed_over = self.base.name_tracker.is_closed_over_def(&n.name);
            let binding_index = BindingIndex::new(data.base.bindings.len());
            data.base
                .bindings
                .push(BindingName::new(n.name, is_closed_over));

            possibly_annex_b_functions.push(n.name, self.scope_index, binding_index, n.stencil);
        }
        for n in &self.const_names {
            // Step 4.a.i. If IsConstantDeclaration of d is true, then
            // Step 4.a.i.1. Perform ! envRec.CreateImmutableBinding(dn, true).
            let is_closed_over = self.base.name_tracker.is_closed_over_def(n);
            data.base
                .bindings
                .push(BindingName::new(*n, is_closed_over));
        }

        ScopeData::Lexical(data)
    }
}

/// Scope for a FunctionExpression.
///
/// The FunctionExpression `(function f() { return f; })` introduces a lexical
/// scope with a single binding `f`, set to the function itself. We create this
/// scope builder whether the FunctionExpression has a name or not, for
/// consistency.
#[derive(Debug)]
struct FunctionExpressionScopeBuilder {
    base: BaseScopeBuilder,

    function_expression_name: Option<SourceAtomSetIndex>,

    scope_index: ScopeIndex,
}

impl FunctionExpressionScopeBuilder {
    fn new(scope_index: ScopeIndex) -> Self {
        Self {
            base: BaseScopeBuilder::new(),
            function_expression_name: None,
            scope_index,
        }
    }

    fn set_function_name(&mut self, name: SourceAtomSetIndex) {
        self.function_expression_name = Some(name);
        self.base.set_function_name(name);
    }

    fn into_scope_data(self, enclosing: ScopeIndex) -> ScopeData {
        match &self.function_expression_name {
            Some(name) => {
                // Runtime Semantics: Evaluation
                // https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-evaluation
                //
                // FunctionExpression :
                //   function BindingIdentifier ( FormalParameters )
                //   { FunctionBody }
                //
                // Step 1. Let scope be the running execution context's
                //         LexicalEnvironment.
                // Step 2. Let funcEnv be NewDeclarativeEnvironment(scope).
                // Step 3. Let envRec be funcEnv's EnvironmentRecord.
                let mut data = LexicalScopeData::new_named_lambda(enclosing);

                // Step 4. Let name be StringValue of BindingIdentifier .
                // Step 5. Perform envRec.CreateImmutableBinding(name, false).
                let is_closed_over = self.base.name_tracker.is_closed_over_def(name);
                data.base
                    .bindings
                    .push(BindingName::new(*name, is_closed_over));

                ScopeData::Lexical(data)
            }
            None => ScopeData::Alias(enclosing),
        }
    }
}

/// The value of [[ThisMode]] internal slot of function object.
/// https://tc39.es/ecma262/#sec-ecmascript-function-objects
///
/// Defines how this references are interpreted within the formal parameters
/// and code body of the function.
#[derive(Debug, Clone, PartialEq)]
enum ThisMode {
    /// `this` refers to the `this` value of a lexically enclosing function.
    Lexical,

    /// `this` value is used exactly as provided by an invocation of the
    /// function.
    #[allow(dead_code)]
    Strict,

    /// `this` value of `undefined` is interpreted as a reference to the global
    /// object.
    Global,
}

/// The result of converting the builder of function parameters and body
/// into scope data.
struct FunctionScopeDataSet {
    /// ScopeData::Function.
    function: ScopeData,

    /// Either ScopeData::Var or ScopeData::Alias.
    extra_body_var: ScopeData,

    /// Either ScopeData::Lexical or ScopeData::Alias.
    lexical: ScopeData,
}

/// See FunctionParametersScopeBuilder.state.
#[derive(Debug)]
enum FunctionParametersState {
    /// Entered FormalParameters.
    Init,

    /// Entered Parameter.
    /// At this point, this parameter can be either non-destructuring or
    /// destructuring.
    /// If BindingIdentifier is found in this state, this parameter is
    /// non-destructuring.
    Parameter,

    /// Entered BindingPattern inside Parameter.
    /// This parameter is destructuring.
    DestructuringParameter,

    /// Entered rest parameter.
    /// At this point, the rest parameter can be either non-destructuring or
    /// destructuring.
    /// If BindingIdentifier is found in this state, the rest parameter is
    /// non-destructuring.
    RestParameter,

    /// Entered BindingPattern inside rest parameter.
    /// The rest parameter is destructuring.
    DestructuringRestParameter,
}

/// Function parameters in FormalParameters, and variables used in
/// FormalParameters
/// Shared part between full-parse and syntax-only parse.
#[derive(Debug)]
struct SharedFunctionParametersScopeBuilder {
    base: BaseScopeBuilder,

    /// FunctionDeclarationInstantiation ( func, argumentsList )
    /// https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
    ///
    /// Step 3. Let strict be func.[[Strict]].
    strict: bool,

    /// Step 5. Let parameterNames be the BoundNames of formals.
    parameter_names: HashSet<SourceAtomSetIndex>,

    /// Step 7. Let simpleParameterList be IsSimpleParameterList of formals.
    simple_parameter_list: bool,

    /// Step 8. Let hasParameterExpressions be ContainsExpression of formals.
    has_parameter_expressions: bool,

    /// Step 17. Else if "arguments" is an element of parameterNames, then
    parameter_has_arguments: bool,
}

impl SharedFunctionParametersScopeBuilder {
    fn new(is_arrow: bool) -> Self {
        let mut base = BaseScopeBuilder::new();

        if !is_arrow {
            // Arrow function closes over this/arguments from enclosing
            // function.
            base.name_tracker
                .note_def(CommonSourceAtomSetIndices::this());
            base.name_tracker
                .note_def(CommonSourceAtomSetIndices::arguments());
        }

        Self {
            base,

            // FIMXE: Receive the enclosing strictness,
            //        and update on directive in body.
            strict: false,

            parameter_names: HashSet::new(),
            simple_parameter_list: true,
            has_parameter_expressions: false,
            parameter_has_arguments: false,
        }
    }

    fn perform_annex_b(
        &self,
        function_declaration_properties: &mut FunctionDeclarationPropertyMap,
        possibly_annex_b_functions: &mut PossiblyAnnexBFunctionList,
        body_scope_builder: &mut SharedFunctionBodyScopeBuilder,
    ) {
        // Annex B
        // Changes to FunctionDeclarationInstantiation
        // https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation
        //
        // Step 1. If strict is false, then
        //
        // FIXME: Once directives are supported, reflect it here.
        let strict = false;
        if strict {
            return;
        }

        // Step 1.a. For each FunctionDeclaration f that is directly contained
        //           in the StatementList of a Block, CaseClause, or
        //           DefaultClause, do
        //
        // NOTE: `possibly_annex_b_functions` contains all of them.

        // Step 1.a.i. Let F be StringValue of the BindingIdentifier of f.
        // Step 1.a.ii. If replacing the FunctionDeclaration f with a
        //              VariableStatement that has F as a BindingIdentifier
        //              would not produce any Early Errors for func and F is
        //              not an element of parameterNames, then
        //
        // NOTE: Early Errors happen if any of top-level lexical has
        //       the same name.  Filter out those functions here.
        for n in &body_scope_builder.let_names {
            possibly_annex_b_functions.remove_if_exists(*n);
        }
        for n in &body_scope_builder.const_names {
            possibly_annex_b_functions.remove_if_exists(*n);
        }
        for n in &self.parameter_names {
            possibly_annex_b_functions.remove_if_exists(*n);
        }

        // Step 1.a.ii.1. NOTE: A var binding for F is only instantiated here
        //                if it is neither a VarDeclaredName, the name of a
        //                formal parameter, or another FunctionDeclaration.
        //
        // NOTE: The binding is merged into the list of other var names.

        // Step 1.a.ii.2. If initializedBindings does not contain F and F is
        //                not "arguments", then
        possibly_annex_b_functions.remove_if_exists(CommonSourceAtomSetIndices::arguments());

        // Step 1.a.ii.2.a. Perform ! varEnv.CreateMutableBinding(F, false).
        // Step 1.a.ii.2.b. Perform varEnv.InitializeBinding(F, undefined).
        // Step 1.a.ii.2.c. Append F to instantiatedVarNames.
        for n in possibly_annex_b_functions.names() {
            body_scope_builder.declare_var(*n);
        }

        // Step 1.a.ii.3. When the FunctionDeclaration f is evaluated, perform
        //                the following steps in place of the
        //                FunctionDeclaration Evaluation algorithm provided in
        //                https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-evaluation
        // Step 1.a.ii.3.a. Let fenv be the running execution context's
        //                  VariableEnvironment.
        // Step 1.a.ii.3.b. Let benv be the running execution context's
        //                  LexicalEnvironment.
        // Step 1.a.ii.3.c. Let fobj be ! benv.GetBindingValue(F, false).
        // Step 1.a.ii.3.d. Perform ! fenv.SetMutableBinding(F, fobj, false).
        // Step 1.a.ii.3.e. Return NormalCompletion(empty).
        possibly_annex_b_functions.mark_annex_b(function_declaration_properties);
    }

    fn before_binding_pattern(&mut self) {
        // Static Semantics: IsSimpleParameterList
        // https://tc39.es/ecma262/#sec-destructuring-binding-patterns-static-semantics-issimpleparameterlist
        //
        // BindingElement : BindingPattern
        //
        //   1. Return false.
        //
        // BindingElement : BindingPattern Initializer
        //
        //   1. Return false.
        self.simple_parameter_list = false;
    }

    fn before_rest_parameter(&mut self) {
        // Static Semantics: IsSimpleParameterList
        // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-issimpleparameterlist
        //
        // FormalParameters : FunctionRestParameter
        //
        //   1. Return false.
        //
        // FormalParameters : FormalParameterList , FunctionRestParameter
        //
        //   1. Return false.
        self.simple_parameter_list = false;
    }

    fn after_initializer(&mut self) {
        // Static Semantics: IsSimpleParameterList
        // https://tc39.es/ecma262/#sec-destructuring-binding-patterns-static-semantics-issimpleparameterlist
        //
        // BindingElement : BindingPattern Initializer
        //
        //   1. Return false.
        //
        // SingleNameBinding : BindingIdentifier Initializer
        //
        //   1. Return false.
        self.simple_parameter_list = false;

        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 8. Let hasParameterExpressions be ContainsExpression of formals.

        // Static Semantics: ContainsExpression
        // https://tc39.es/ecma262/#sec-destructuring-binding-patterns-static-semantics-containsexpression
        //
        // BindingElement : BindingPattern Initializer
        //
        //   1. Return true.
        //
        // SingleNameBinding : BindingIdentifier Initializer
        //
        //   1. Return true.
        self.has_parameter_expressions = true;
    }

    fn before_computed_property_name(&mut self) {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 8. Let hasParameterExpressions be ContainsExpression of formals.

        // Static Semantics: ContainsExpression
        // https://tc39.es/ecma262/#sec-destructuring-binding-patterns-static-semantics-containsexpression
        //
        // BindingProperty : PropertyName : BindingElement
        //
        //   1. Let has be IsComputedPropertyKey of PropertyName .
        //   2. If has is true, return true.
        //   3. Return ContainsExpression of BindingElement .
        self.has_parameter_expressions = true;
    }

    fn declare_param(&mut self, name: SourceAtomSetIndex) {
        // Step 17. Else if "arguments" is an element of parameterNames,
        //          then
        if name == CommonSourceAtomSetIndices::arguments() {
            self.parameter_has_arguments = true;
        }

        self.parameter_names.insert(name.clone());
        self.base.declare_param(name);
    }

    fn is_parameter_closed_over(&self) -> bool {
        for name in &self.parameter_names {
            if self.base.name_tracker.is_closed_over_def(name) {
                return true;
            }
        }

        false
    }
}

/// Function parameters in FormalParameters, and variables used in
/// FormalParameters
/// For full-parse.
#[derive(Debug)]
struct FunctionParametersScopeBuilder {
    shared: SharedFunctionParametersScopeBuilder,

    /// State of the analysis.
    /// This is used to determine what kind of binding the parameter is.
    state: FunctionParametersState,

    /// List of positional parameter or None if destructuring.
    /// This includes rest parameter.
    positional_parameter_names: Vec<Option<SourceAtomSetIndex>>,

    /// List of non-positional parameters (destructuring parameters).
    non_positional_parameter_names: Vec<SourceAtomSetIndex>,

    /// FunctionDeclarationInstantiation ( func, argumentsList )
    /// https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
    ///
    /// Step 16. If func.[[ThisMode]] is lexical, then
    this_mode: ThisMode,

    /// Step 6. If parameterNames has any duplicate entries, let hasDuplicates
    ///         be true. Otherwise, let hasDuplicates be false.
    has_duplicates: bool,

    scope_index: ScopeIndex,

    /// Index of the script in the list of `functions` in the
    /// `FunctionScriptStencilBuilder`.
    script_index: ScriptStencilIndex,

    has_direct_eval: bool,

    is_arrow: bool,
}

impl FunctionParametersScopeBuilder {
    fn new(scope_index: ScopeIndex, is_arrow: bool, script_index: ScriptStencilIndex) -> Self {
        Self {
            shared: SharedFunctionParametersScopeBuilder::new(is_arrow),

            state: FunctionParametersState::Init,

            positional_parameter_names: Vec::new(),
            non_positional_parameter_names: Vec::new(),

            // FIXME: Receive correct value.
            this_mode: ThisMode::Global,

            has_duplicates: false,
            scope_index,
            script_index,
            has_direct_eval: false,
            is_arrow,
        }
    }

    fn before_parameter(&mut self) {
        match self.state {
            FunctionParametersState::Init => {
                self.state = FunctionParametersState::Parameter;
            }
            FunctionParametersState::Parameter => {
                self.state = FunctionParametersState::Parameter;
            }
            FunctionParametersState::DestructuringParameter => {
                self.state = FunctionParametersState::Parameter;
            }
            FunctionParametersState::RestParameter
            | FunctionParametersState::DestructuringRestParameter => panic!("Invalid transition"),
        }
    }

    fn before_binding_pattern(&mut self) {
        self.shared.before_binding_pattern();

        match self.state {
            FunctionParametersState::Parameter => {
                self.positional_parameter_names.push(None);
                self.state = FunctionParametersState::DestructuringParameter;
            }
            FunctionParametersState::DestructuringParameter => {}
            FunctionParametersState::RestParameter => {
                self.positional_parameter_names.push(None);
                self.state = FunctionParametersState::DestructuringRestParameter;
            }
            FunctionParametersState::DestructuringRestParameter => {}
            FunctionParametersState::Init => panic!("Invalid transition"),
        }
    }

    fn before_rest_parameter(&mut self) {
        self.shared.before_rest_parameter();

        match self.state {
            FunctionParametersState::Init
            | FunctionParametersState::Parameter
            | FunctionParametersState::DestructuringParameter => {
                self.state = FunctionParametersState::RestParameter;
            }
            FunctionParametersState::RestParameter
            | FunctionParametersState::DestructuringRestParameter => panic!("Invalid transition"),
        }
    }

    fn after_initializer(&mut self) {
        self.shared.after_initializer();
    }

    fn before_computed_property_name(&mut self) {
        self.shared.before_computed_property_name();
    }

    fn declare_param(&mut self, name: SourceAtomSetIndex) {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 5. Let parameterNames be the BoundNames of formals.
        match self.state {
            FunctionParametersState::Init => panic!("Invalid state"),
            FunctionParametersState::Parameter => {
                self.positional_parameter_names.push(Some(name.clone()));
            }
            FunctionParametersState::DestructuringParameter => {
                self.non_positional_parameter_names.push(name.clone());
            }
            FunctionParametersState::RestParameter => {
                self.positional_parameter_names.push(Some(name.clone()));
            }
            FunctionParametersState::DestructuringRestParameter => {
                self.non_positional_parameter_names.push(name.clone());
            }
        }

        // Step 6. If parameterNames has any duplicate entries, let
        //         hasDuplicates be true. Otherwise, let hasDuplicates be
        //         false.
        if self.shared.parameter_names.contains(&name) {
            self.has_duplicates = true;
        }

        self.shared.declare_param(name);
    }

    fn into_scope_data_set(
        self,
        enclosing: ScopeIndex,
        body_scope_builder: FunctionBodyScopeBuilder,
    ) -> FunctionScopeDataSet {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 15. Let argumentsObjectNeeded be true.
        let mut arguments_object_needed = true;

        // Step 16. If func.[[ThisMode]] is lexical, then
        if self.this_mode == ThisMode::Lexical {
            // Step 16.a. NOTE: Arrow functions never have an arguments objects.
            // Step 16.b. Set argumentsObjectNeeded to false.
            arguments_object_needed = false;
        }
        // Step 17. Else if "arguments" is an element of parameterNames,
        //          then
        else if self.shared.parameter_has_arguments {
            // Step 17.a. Set argumentsObjectNeeded to false.
            arguments_object_needed = false;
        }
        // Step 18. Else if hasParameterExpressions is false, then
        else if !self.shared.parameter_has_arguments {
            // Step 18.a. If "arguments" is an element of functionNames or if
            //            "arguments" is
            //            an element of lexicalNames, then
            if body_scope_builder.shared.function_or_lexical_has_arguments {
                // Step 18.a.i. Set argumentsObjectNeeded to false.
                arguments_object_needed = false;
            }
        }

        // NOTE: In SpiderMonkey, single environment can have multiple
        //       binding kind.
        //       It's not necessary to create yet another environment here.
        //
        // Step 19. If strict is true or if hasParameterExpressions is false,
        //          then
        if self.shared.strict || !self.shared.has_parameter_expressions {
            // Step 19.a. NOTE: Only a single lexical environment is needed for
            //            the parameters and top-level vars.
            // Step 19.b. Let env be the LexicalEnvironment of calleeContext.
            // Step 19.c. Let envRec be env's EnvironmentRecord.
        }
        // Step 20. Else,
        else {
            // Step 20.a. NOTE: A separate Environment Record is needed to
            //            ensure that bindings created by direct eval calls in
            //            the formal parameter list are outside the environment
            //            where parameters are declared.
            // Step 20.b. Let calleeEnv be the LexicalEnvironment of
            //            calleeContext.
            // Step 20.c. Let env be NewDeclarativeEnvironment(calleeEnv).
            // Step 20.d. Let envRec be env's EnvironmentRecord.
            // Step 20.e. Assert: The VariableEnvironment of calleeContext is
            //            calleeEnv.
            // Step 20.f. Set the LexicalEnvironment of calleeContext to env.
        }

        let has_extra_body_var_scope = self.shared.has_parameter_expressions;

        // NOTE: Names in `body_scope_builder.var_names` is skipped if
        //       it's `arguments`, at step 27.c.i.
        //       The count here isn't the exact number of var bindings, but
        //       it's fine given FunctionScopeData::new doesn't require the
        //       exact number, but just maximum number.
        let function_max_var_names_count = if has_extra_body_var_scope {
            0
        } else {
            body_scope_builder.shared.var_names.len()
        };

        let mut function_scope_data = FunctionScopeData::new(
            self.shared.has_parameter_expressions,
            self.positional_parameter_names.len(),
            self.non_positional_parameter_names.len(),
            function_max_var_names_count,
            enclosing,
            self.script_index,
            self.is_arrow,
        );

        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 21. For each String paramName in parameterNames, do
        // Step 21.a. Let alreadyDeclared be envRec.HasBinding(paramName).
        // Step 21.b. NOTE: Early errors ensure that duplicate parameter names
        //            can only occur in non-strict functions that do not have
        //            parameter default values or rest parameters.
        // Step 21.c. If alreadyDeclared is false, then
        // Step 21.c.i. Perform ! envRec.CreateMutableBinding(paramName, false).
        // Step 21.c.ii. If hasDuplicates is true, then
        // Step 21.c.ii.1. Perform
        //                 ! envRec.InitializeBinding(paramName, undefined).
        //
        // NOTE: The existence of duplication isn't encoded in scope data.
        for maybe_name in &self.positional_parameter_names {
            match maybe_name {
                Some(n) => {
                    let is_closed_over = self.shared.base.name_tracker.is_closed_over_def(n)
                        || (!has_extra_body_var_scope
                            && body_scope_builder
                                .shared
                                .base
                                .name_tracker
                                .is_closed_over_def(n));
                    function_scope_data
                        .base
                        .bindings
                        .push(Some(BindingName::new(*n, is_closed_over)))
                }
                None => function_scope_data.base.bindings.push(None),
            }
        }
        for n in &self.non_positional_parameter_names {
            let is_closed_over = self.shared.base.name_tracker.is_closed_over_def(n)
                || (!has_extra_body_var_scope
                    && body_scope_builder
                        .shared
                        .base
                        .name_tracker
                        .is_closed_over_def(n));
            function_scope_data
                .base
                .bindings
                .push(Some(BindingName::new(*n, is_closed_over)))
        }

        // Step 22. If argumentsObjectNeeded is true, then
        // Steps 22.a-b. Create{Unm,M}appedArgumentsObject
        // (done in emitter)

        // Step 22.c. If strict is true, then
        // Step 22.c.i. Perform
        //              ! envRec.CreateImmutableBinding("arguments", false).
        // Step 22.d. Else,
        // Step 22.d.i. Perform
        //              ! envRec.CreateMutableBinding("arguments", false).
        // Step 22.e. Call envRec.InitializeBinding("arguments", ao).
        //
        // NOTE: In SpiderMonkey, whether immutable or not is not stored
        //       in scope data, but checked while parsing, including
        //       when parsing eval inside function.

        // Step 22.f. Let parameterBindings be a new List of parameterNames
        //            with "arguments" appended.
        //
        // NOTE: Done in each consumer of parameterNames.

        // Step 23. Else,
        // Step 23.a. Let parameterBindings be parameterNames.
        //
        // NOTE: Done in each consumer of parameterNames.

        // Steps 24-26. IteratorBindingInitialization
        // (done in emitter)

        // Step 27. If hasParameterExpressions is false, then
        let extra_body_var_scope_data = if !self.shared.has_parameter_expressions {
            debug_assert!(!has_extra_body_var_scope);

            // Step 27.a. NOTE: Only a single lexical environment is needed for
            //            the parameters and top-level vars.

            // Step 27.b. Let instantiatedVarNames be a copy of the List
            //            parameterBindings.

            // Step 27.c. For each n in varNames, do
            for n in &body_scope_builder.shared.var_names {
                // Step 27.c.i. If n is not an element of instantiatedVarNames,
                //              then
                // Step 27.c.i.1. Append n to instantiatedVarNames.
                //
                // NOTE: var_names is already unique.
                //       Check against parameters and `arguments` here.
                if self.shared.parameter_names.contains(n)
                    || (arguments_object_needed && *n == CommonSourceAtomSetIndices::arguments())
                {
                    continue;
                }

                // Step 27.c.i.2. Perform
                //                ! envRec.CreateMutableBinding(n, false).
                let is_closed_over = body_scope_builder
                    .shared
                    .base
                    .name_tracker
                    .is_closed_over_def(n);
                function_scope_data
                    .base
                    .bindings
                    .push(Some(BindingName::new(*n, is_closed_over)));

                // Step 27.c.i.3. Call envRec.InitializeBinding(n, undefined).
                // (done in runtime)
            }

            // Step 27.d. Let varEnv be env.
            // Step 27.e. Let varEnvRec be envRec.
            ScopeData::Alias(self.scope_index)
        }
        // Step 28. Else,
        else {
            debug_assert!(has_extra_body_var_scope);

            // In non-strict mode code, direct `eval` can extend function's
            // scope.
            let function_has_extensible_scope = !self.shared.strict && self.has_direct_eval;

            // Step 28.a. NOTE: A separate Environment Record is needed to
            //            ensure that closures created by expressions in the
            //            formal parameter list do not have visibility of
            //            declarations in the function body.

            // Step 28.b. Let varEnv be NewDeclarativeEnvironment(env).
            // Step 28.c. Set the VariableEnvironment of calleeContext to
            //            varEnv.
            let mut data = VarScopeData::new(
                body_scope_builder.shared.var_names.len(),
                function_has_extensible_scope,
                /* encloding= */ self.scope_index,
            );

            // Step 28.d. Let instantiatedVarNames be a new empty List.

            // Step 28.e. For each n in varNames, do
            for n in &body_scope_builder.shared.var_names {
                // Step 28.e.i. If n is not an element of instantiatedVarNames, then
                // Step 28.e.i.1. Append n to instantiatedVarNames.
                //
                // NOTE: var_names is already unique.

                // Step 28.e.i.2. Perform
                //                ! varEnv.CreateMutableBinding(n, false).
                let is_closed_over = body_scope_builder
                    .shared
                    .base
                    .name_tracker
                    .is_closed_over_def(n);
                data.base
                    .bindings
                    .push(BindingName::new(*n, is_closed_over));

                // Step 28.e.i.3. If n is not an element of parameterBindings or if
                //                n is an element of functionNames, let
                //                initialValue be undefined.
                // Step 28.e.i.4. Else,
                // Step 28.e.i.4.a. Let initialValue be
                //                  ! env.GetBindingValue(n, false).
                // Step 28.e.i.5. Call varEnv.InitializeBinding(n, initialValue).
                // (done in emitter)

                // Step 28.e.i.6. NOTE: A var with the same name as a formal
                //                parameter initially has the same value as the
                //                corresponding initialized parameter.
            }

            ScopeData::Var(data)
        };

        // Step 30. If strict is false, then
        // Step 30.a. Let lexEnv be NewDeclarativeEnvironment(varEnv).
        // Step 30.b. NOTE: Non-strict functions use a separate lexical
        //            Environment Record for top-level lexical declarations so
        //            that a direct eval can determine whether any var scoped
        //            declarations introduced by the eval code conflict with
        //            pre-existing top-level lexically scoped declarations.
        //            This is not needed for strict functions because a strict
        //            direct eval always places all declarations into a new
        //            Environment Record.
        // Step 31. Else, let lexEnv be varEnv.
        // Step 32. Let lexEnvRec be lexEnv's EnvironmentRecord.
        //
        // NOTE: SpiderMonkey creates lexical env whenever lexical binding
        //       exists.

        let lexical_scope_data = if body_scope_builder.shared.let_names.len() > 0
            || body_scope_builder.shared.const_names.len() > 0
        {
            let mut data = LexicalScopeData::new_function_lexical(
                body_scope_builder.shared.let_names.len(),
                body_scope_builder.shared.const_names.len(),
                /* encloding= */ body_scope_builder.var_scope_index,
            );

            // Step 33. Set the LexicalEnvironment of calleeContext to lexEnv.
            // Step 34. Let lexDeclarations be the LexicallyScopedDeclarations
            //          of code.
            // Step 35. For each element d in lexDeclarations, do
            // Step 35.a. NOTE: A lexically declared name cannot be the same as
            //            a function/generator declaration, formal parameter,
            //            or a var name. Lexically declared names are only
            //            instantiated here but not initialized.
            // Step 35.b. For each element dn of the BoundNames of d, do

            for n in &body_scope_builder.shared.let_names {
                // Step 35.b.ii. Else,
                // Step 35.b.ii.1. Perform
                //                 ! lexEnvRec.CreateMutableBinding(dn, false).
                let is_closed_over = body_scope_builder
                    .shared
                    .base
                    .name_tracker
                    .is_closed_over_def(n);
                data.base
                    .bindings
                    .push(BindingName::new(*n, is_closed_over))
            }
            for n in &body_scope_builder.shared.const_names {
                // Step 35.b.i. If IsConstantDeclaration of d is true, then
                // Step 35.b.i.1. Perform
                //                ! lexEnvRec.CreateImmutableBinding(dn, true).
                let is_closed_over = body_scope_builder
                    .shared
                    .base
                    .name_tracker
                    .is_closed_over_def(n);
                data.base
                    .bindings
                    .push(BindingName::new(*n, is_closed_over))
            }

            ScopeData::Lexical(data)
        } else {
            ScopeData::Alias(body_scope_builder.var_scope_index)
        };

        // Step 36. For each Parse Node f in functionsToInitialize, do
        // (done in emitter)

        FunctionScopeDataSet {
            function: ScopeData::Function(function_scope_data),
            extra_body_var: extra_body_var_scope_data,
            lexical: lexical_scope_data,
        }
    }
}

/// Variables declared/used in FunctionBody.
/// Shared part between full-parse and syntax-only parse.
#[derive(Debug)]
struct SharedFunctionBodyScopeBuilder {
    base: BaseScopeBuilder,

    /// FunctionDeclarationInstantiation ( func, argumentsList )
    /// https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
    ///
    /// Step 9. Let varNames be the VarDeclaredNames of code.
    var_names: IndexSet<SourceAtomSetIndex>,

    /// Step 11. Let lexicalNames be the LexicallyDeclaredNames of code.
    let_names: Vec<SourceAtomSetIndex>,
    const_names: Vec<SourceAtomSetIndex>,

    /// Step 18. Else if hasParameterExpressions is false, then
    /// Step 18.a. If "arguments" is an element of functionNames or
    ///            if "arguments" is an element of lexicalNames, then
    function_or_lexical_has_arguments: bool,
}

impl SharedFunctionBodyScopeBuilder {
    fn new() -> Self {
        Self {
            base: BaseScopeBuilder::new(),
            var_names: IndexSet::new(),
            let_names: Vec::new(),
            const_names: Vec::new(),
            function_or_lexical_has_arguments: false,
        }
    }

    fn declare_var(&mut self, name: SourceAtomSetIndex) {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 9. Let varNames be the VarDeclaredNames of code.
        self.var_names.insert(name);
        self.base.declare_var(name);
    }

    fn check_lexical_or_function_name(&mut self, name: SourceAtomSetIndex) {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 18.a. If "arguments" is an element of functionNames or if
        //            "arguments" is an element of lexicalNames, then
        if name == CommonSourceAtomSetIndices::arguments() {
            self.function_or_lexical_has_arguments = true;
        }
    }

    fn declare_let(&mut self, name: SourceAtomSetIndex) {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 11. Let lexicalNames be the LexicallyDeclaredNames of code.
        self.let_names.push(name.clone());
        self.check_lexical_or_function_name(name.clone());
        self.base.declare_let(name);
    }

    fn declare_const(&mut self, name: SourceAtomSetIndex) {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 11. Let lexicalNames be the LexicallyDeclaredNames of code.
        self.let_names.push(name.clone());
        self.check_lexical_or_function_name(name.clone());
        self.base.declare_const(name);
    }

    fn declare_function(&mut self, name: SourceAtomSetIndex) {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 9. Let varNames be the VarDeclaredNames of code.
        self.var_names.insert(name.clone());

        // Step 14. For each d in varDeclarations, in reverse list order, do
        // Step 14.a. If d is neither a VariableDeclaration nor a ForBinding
        //            nor a BindingIdentifier , then
        // (implicit)

        // Step 14.a.i. Assert: d is either a FunctionDeclaration, a
        //              GeneratorDeclaration, an AsyncFunctionDeclaration,
        //              or an AsyncGeneratorDeclaration.

        // Step 14.a.ii. Let fn be the sole element of the BoundNames of d.

        // Step 14.a.iii. If fn is not an element of functionNames, then
        //
        // NOTE: Instead of iterating in reverse list oder, we iterate in
        // normal order and overwrite existing item.

        // Step 14.a.iii.1. Insert fn as the first element of functionNames.
        // Step 14.a.iii.2. NOTE: If there are multiple function declarations
        //                  for the same name, the last declaration is used.
        self.check_lexical_or_function_name(name);

        self.base.declare_function(name)
    }

    fn is_var_closed_over(&self) -> bool {
        for name in &self.var_names {
            if self.base.name_tracker.is_closed_over_def(name) {
                return true;
            }
        }

        false
    }
}

/// Variables declared/used in FunctionBody.
/// For full-parse.
#[derive(Debug)]
struct FunctionBodyScopeBuilder {
    shared: SharedFunctionBodyScopeBuilder,

    /// FunctionDeclarationInstantiation ( func, argumentsList )
    /// https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
    ///
    /// Step 13. Let functionsToInitialize be a new empty List.
    functions_to_initialize: Vec<ScriptStencilIndex>,

    var_scope_index: ScopeIndex,
    lexical_scope_index: ScopeIndex,
}

impl FunctionBodyScopeBuilder {
    fn new(var_scope_index: ScopeIndex, lexical_scope_index: ScopeIndex) -> Self {
        Self {
            shared: SharedFunctionBodyScopeBuilder::new(),
            functions_to_initialize: Vec::new(),
            var_scope_index,
            lexical_scope_index,
        }
    }

    fn declare_var(&mut self, name: SourceAtomSetIndex) {
        self.shared.declare_var(name);
    }

    fn declare_let(&mut self, name: SourceAtomSetIndex) {
        self.shared.declare_let(name);
    }

    fn declare_const(&mut self, name: SourceAtomSetIndex) {
        self.shared.declare_const(name);
    }

    fn declare_function(&mut self, name: SourceAtomSetIndex, fun_index: ScriptStencilIndex) {
        self.shared.declare_function(name);

        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 14.a.iii.3. Insert d as the first element of
        //                  functionsToInitialize.
        self.functions_to_initialize.push(fun_index);
    }
}

#[derive(Debug)]
enum ScopeBuilder {
    Global(GlobalScopeBuilder),
    SyntaxOnlyGlobal(BaseScopeBuilder),

    Block(BlockScopeBuilder),
    SyntaxOnlyBlock(BaseScopeBuilder),

    FunctionExpression(FunctionExpressionScopeBuilder),
    SyntaxOnlyFunctionExpression(BaseScopeBuilder),

    FunctionParameters(FunctionParametersScopeBuilder),
    SyntaxOnlyFunctionParameters(SharedFunctionParametersScopeBuilder),

    FunctionBody(FunctionBodyScopeBuilder),
    SyntaxOnlyFunctionBody(SharedFunctionBodyScopeBuilder),
}

impl ScopeBuilder {
    fn get_scope_index(&self) -> Option<ScopeIndex> {
        match self {
            ScopeBuilder::Global(builder) => Some(builder.scope_index),
            ScopeBuilder::SyntaxOnlyGlobal(_) => None,
            ScopeBuilder::Block(builder) => Some(builder.scope_index),
            ScopeBuilder::SyntaxOnlyBlock(_) => None,
            ScopeBuilder::FunctionExpression(builder) => Some(builder.scope_index),
            ScopeBuilder::SyntaxOnlyFunctionExpression(_) => None,
            ScopeBuilder::FunctionParameters(builder) => Some(builder.scope_index),
            ScopeBuilder::SyntaxOnlyFunctionParameters(_) => None,
            ScopeBuilder::FunctionBody(builder) => Some(builder.lexical_scope_index),
            ScopeBuilder::SyntaxOnlyFunctionBody(_) => None,
        }
    }

    fn declare_var(&mut self, name: SourceAtomSetIndex) {
        match self {
            ScopeBuilder::Global(ref mut builder) => builder.declare_var(name),
            ScopeBuilder::SyntaxOnlyGlobal(ref mut builder) => builder.declare_var(name),
            ScopeBuilder::FunctionBody(ref mut builder) => builder.declare_var(name),
            ScopeBuilder::SyntaxOnlyFunctionBody(ref mut builder) => builder.declare_var(name),
            _ => panic!("unexpected var scope builder"),
        }
    }

    fn declare_let(&mut self, name: SourceAtomSetIndex) {
        match self {
            ScopeBuilder::Global(ref mut builder) => builder.declare_let(name),
            ScopeBuilder::SyntaxOnlyGlobal(ref mut builder) => builder.declare_let(name),
            ScopeBuilder::Block(ref mut builder) => builder.declare_let(name),
            ScopeBuilder::SyntaxOnlyBlock(ref mut builder) => builder.declare_let(name),
            ScopeBuilder::FunctionBody(ref mut builder) => builder.declare_let(name),
            ScopeBuilder::SyntaxOnlyFunctionBody(ref mut builder) => builder.declare_let(name),
            _ => panic!("unexpected lexical scope builder"),
        }
    }

    fn declare_const(&mut self, name: SourceAtomSetIndex) {
        match self {
            ScopeBuilder::Global(ref mut builder) => builder.declare_const(name),
            ScopeBuilder::SyntaxOnlyGlobal(ref mut builder) => builder.declare_const(name),
            ScopeBuilder::Block(ref mut builder) => builder.declare_const(name),
            ScopeBuilder::SyntaxOnlyBlock(ref mut builder) => builder.declare_const(name),
            ScopeBuilder::FunctionBody(ref mut builder) => builder.declare_const(name),
            ScopeBuilder::SyntaxOnlyFunctionBody(ref mut builder) => builder.declare_const(name),
            _ => panic!("unexpected lexical scope builder"),
        }
    }

    fn set_function_name(&mut self, name: SourceAtomSetIndex) {
        match self {
            ScopeBuilder::FunctionExpression(ref mut builder) => builder.set_function_name(name),
            ScopeBuilder::SyntaxOnlyFunctionExpression(ref mut builder) => {
                builder.set_function_name(name)
            }
            // FunctionDeclaration etc doesn't push any scope builder.
            // Just ignore.
            _ => {}
        }
    }

    fn declare_param(&mut self, name: SourceAtomSetIndex) {
        match self {
            ScopeBuilder::FunctionParameters(ref mut builder) => builder.declare_param(name),
            ScopeBuilder::SyntaxOnlyFunctionParameters(ref mut builder) => {
                builder.declare_param(name)
            }
            _ => panic!("unexpected function scope builder"),
        }
    }

    fn base_mut(&mut self) -> &mut BaseScopeBuilder {
        match self {
            ScopeBuilder::Global(builder) => &mut builder.base,
            ScopeBuilder::SyntaxOnlyGlobal(builder) => builder,
            ScopeBuilder::Block(builder) => &mut builder.base,
            ScopeBuilder::SyntaxOnlyBlock(builder) => builder,
            ScopeBuilder::FunctionExpression(builder) => &mut builder.base,
            ScopeBuilder::SyntaxOnlyFunctionExpression(builder) => builder,
            ScopeBuilder::FunctionParameters(builder) => &mut builder.shared.base,
            ScopeBuilder::SyntaxOnlyFunctionParameters(builder) => &mut builder.base,
            ScopeBuilder::FunctionBody(builder) => &mut builder.shared.base,
            ScopeBuilder::SyntaxOnlyFunctionBody(builder) => &mut builder.base,
        }
    }
}

/// Tracks what kind of binding the BindingIdentifier node corresponds to.
#[derive(Debug)]
struct ScopeKindStack {
    stack: Vec<ScopeKind>,
}

impl ScopeKindStack {
    fn new() -> Self {
        Self { stack: Vec::new() }
    }

    fn innermost<'a>(&'a self) -> &'a ScopeKind {
        self.stack
            .last()
            .expect("There should be at least one scope on the stack")
    }

    fn push(&mut self, kind: ScopeKind) {
        self.stack.push(kind)
    }

    fn pop(&mut self, kind: ScopeKind) {
        match self.stack.pop() {
            Some(k) if k == kind => {}
            _ => panic!("unmatching scope kind"),
        }
    }

    fn is_empty(&self) -> bool {
        self.stack.len() == 0
    }
}

/// The stack of scope builder for creating binding into.
#[derive(Debug)]
struct ScopeBuilderStack {
    stack: Vec<ScopeBuilder>,

    /// Stack of lists of names that is
    ///   1. defined in the scope
    ///   2. closed over by inner script
    ///
    /// Each list is delimited by `None`, for each scope.
    ///
    /// The order of scopes is depth-first post-order, and the order of names
    /// inside each scope is in not defined.
    ///
    /// When entering a function, empty list is pushed to this stack, and
    /// when leaving each function, top-most list is popped, and
    /// added to gcthings of the function, and this list is reset to empty.
    closed_over_bindings_for_lazy: Vec<Vec<Option<SourceAtomSetIndex>>>,
}

impl ScopeBuilderStack {
    fn new() -> Self {
        Self {
            stack: Vec::new(),
            closed_over_bindings_for_lazy: Vec::new(),
        }
    }

    fn innermost_var<'a>(&'a mut self) -> &'a mut ScopeBuilder {
        for builder in self.stack.iter_mut().rev() {
            match builder {
                ScopeBuilder::Global(_) => return builder,
                ScopeBuilder::SyntaxOnlyGlobal(_) => return builder,
                // NOTE: Function's body-level variable goes to
                // `FunctionBodyScopeBuilder`, regardless of the existence of
                // extra body var scope.
                // See `FunctionParametersScopeBuilder::into_scope_data_set`
                // for how those vars are stored into either function scope or
                // extra body var scope.
                ScopeBuilder::FunctionBody(_) => return builder,
                ScopeBuilder::SyntaxOnlyFunctionBody(_) => return builder,
                _ => {}
            }
        }

        panic!("There should be at least one scope on the stack");
    }

    fn maybe_innermost_function_parameters<'a>(
        &'a mut self,
    ) -> Option<&'a mut FunctionParametersScopeBuilder> {
        for builder in self.stack.iter_mut().rev() {
            match builder {
                ScopeBuilder::FunctionParameters(builder) => return Some(builder),
                _ => {}
            }
        }

        None
    }

    fn innermost_lexical<'a>(&'a mut self) -> &'a mut ScopeBuilder {
        // FIXME: If there's no other case, merge with innermost.
        self.innermost()
    }

    fn innermost<'a>(&'a mut self) -> &'a mut ScopeBuilder {
        self.stack
            .last_mut()
            .expect("There should be at least one scope on the stack")
    }

    fn maybe_innermost<'a>(&'a mut self) -> Option<&'a mut ScopeBuilder> {
        self.stack.last_mut()
    }

    fn current_scope_index(&self) -> ScopeIndex {
        self.maybe_current_scope_index()
            .expect("Shouldn't be in syntax-only mode")
    }

    fn maybe_current_scope_index(&self) -> Option<ScopeIndex> {
        self.stack
            .last()
            .expect("There should be at least one scope on the stack")
            .get_scope_index()
    }

    fn push_global(&mut self, builder: GlobalScopeBuilder) {
        self.stack.push(ScopeBuilder::Global(builder))
    }
    fn push_syntax_only_global(&mut self, builder: BaseScopeBuilder) {
        self.stack.push(ScopeBuilder::SyntaxOnlyGlobal(builder))
    }

    fn pop_global(&mut self) -> GlobalScopeBuilder {
        match self.pop() {
            ScopeBuilder::Global(builder) => {
                debug_assert!(self.stack.is_empty());
                builder
            }
            _ => panic!("unmatching scope builder"),
        }
    }
    fn pop_syntax_only_global(&mut self) {
        match self.pop() {
            ScopeBuilder::SyntaxOnlyGlobal(_) => {
                debug_assert!(self.stack.is_empty());
            }
            _ => panic!("unmatching scope builder"),
        }
    }

    fn push_block(&mut self, builder: BlockScopeBuilder) {
        self.stack.push(ScopeBuilder::Block(builder))
    }
    fn push_syntax_only_block(&mut self, builder: BaseScopeBuilder) {
        self.stack.push(ScopeBuilder::SyntaxOnlyBlock(builder))
    }

    fn handle_popped_block(&mut self, builder: &BaseScopeBuilder) {
        self.innermost()
            .base_mut()
            .propagate_from_inner_non_script(builder);

        self.update_closed_over_bindings_for_lazy(builder);
    }

    fn pop_block(&mut self) -> BlockScopeBuilder {
        match self.pop() {
            ScopeBuilder::Block(builder) => {
                self.handle_popped_block(&builder.base);
                builder
            }
            _ => panic!("unmatching scope builder"),
        }
    }
    fn pop_syntax_only_block(&mut self) {
        match self.pop() {
            ScopeBuilder::SyntaxOnlyBlock(builder) => self.handle_popped_block(&builder),
            _ => panic!("unmatching scope builder"),
        }
    }

    fn push_function_expression(&mut self, builder: FunctionExpressionScopeBuilder) {
        self.stack.push(ScopeBuilder::FunctionExpression(builder))
    }
    fn push_syntax_only_function_expression(&mut self, builder: BaseScopeBuilder) {
        self.stack
            .push(ScopeBuilder::SyntaxOnlyFunctionExpression(builder))
    }

    fn handle_popped_function_expression(&mut self, builder: &BaseScopeBuilder) {
        if let Some(outer) = self.maybe_innermost() {
            // NOTE: Function expression's name cannot have any
            //       used free variables.
            //       We can treat it as non-script here, so that
            //       any closed-over free variables inside this
            //       function is propagated from FunctionParameters
            //       to enclosing scope builder.
            outer.base_mut().propagate_from_inner_non_script(builder);
        }

        self.update_closed_over_bindings_for_lazy(builder);
    }

    fn pop_function_expression(&mut self) -> FunctionExpressionScopeBuilder {
        match self.pop() {
            ScopeBuilder::FunctionExpression(builder) => {
                self.handle_popped_function_expression(&builder.base);
                builder
            }
            _ => panic!("unmatching scope builder"),
        }
    }
    fn pop_syntax_only_function_expression(&mut self) {
        match self.pop() {
            ScopeBuilder::SyntaxOnlyFunctionExpression(builder) => {
                self.handle_popped_function_expression(&builder);
            }
            _ => panic!("unmatching scope builder"),
        }
    }

    fn push_function_parameters(&mut self, builder: FunctionParametersScopeBuilder) {
        self.stack.push(ScopeBuilder::FunctionParameters(builder))
    }
    fn push_syntax_only_function_parameters(
        &mut self,
        builder: SharedFunctionParametersScopeBuilder,
    ) {
        self.stack
            .push(ScopeBuilder::SyntaxOnlyFunctionParameters(builder))
    }

    fn handle_popped_function_parameters_and_body(
        &mut self,
        function_declaration_properties: &mut FunctionDeclarationPropertyMap,
        possibly_annex_b_functions: &mut PossiblyAnnexBFunctionList,
        parameter_scope_builder: &mut SharedFunctionParametersScopeBuilder,
        body_scope_builder: &mut SharedFunctionBodyScopeBuilder,
    ) {
        // FunctionDeclarationInstantiation ( func, argumentsList )
        // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
        //
        // Step 29. NOTE: Annex B adds additional steps at this point.
        //
        // NOTE: Reordered here in order to reflect Annex B functions
        //       to FreeNameTracker.
        parameter_scope_builder.perform_annex_b(
            function_declaration_properties,
            possibly_annex_b_functions,
            body_scope_builder,
        );

        parameter_scope_builder
            .base
            .propagate_from_inner_non_script(&body_scope_builder.base);
        if let Some(outer) = self.maybe_innermost() {
            outer
                .base_mut()
                .propagate_from_inner_script(¶meter_scope_builder.base);
        }

        let has_extra_body_var_scope = parameter_scope_builder.has_parameter_expressions;
        if has_extra_body_var_scope {
            self.update_closed_over_bindings_for_lazy(&body_scope_builder.base);
            self.update_closed_over_bindings_for_lazy(¶meter_scope_builder.base);
        } else {
            self.update_closed_over_bindings_for_lazy_with_parameters_and_body(
                ¶meter_scope_builder.base,
                &body_scope_builder.base,
            );
        }
    }

    fn pop_function_parameters_and_body(
        &mut self,
        function_declaration_properties: &mut FunctionDeclarationPropertyMap,
        possibly_annex_b_functions: &mut PossiblyAnnexBFunctionList,
    ) -> (FunctionParametersScopeBuilder, FunctionBodyScopeBuilder) {
        let mut body_scope_builder = match self.pop() {
            ScopeBuilder::FunctionBody(builder) => builder,
            _ => panic!("unmatching scope builder"),
        };

        let mut parameter_scope_builder = match self.pop() {
            ScopeBuilder::FunctionParameters(builder) => builder,
            _ => panic!("unmatching scope builder"),
        };

        self.handle_popped_function_parameters_and_body(
            function_declaration_properties,
            possibly_annex_b_functions,
            &mut parameter_scope_builder.shared,
            &mut body_scope_builder.shared,
        );

        (parameter_scope_builder, body_scope_builder)
    }
    fn pop_syntax_only_function_parameters_and_body(
        &mut self,
        function_declaration_properties: &mut FunctionDeclarationPropertyMap,
        possibly_annex_b_functions: &mut PossiblyAnnexBFunctionList,
    ) -> (
        SharedFunctionParametersScopeBuilder,
        SharedFunctionBodyScopeBuilder,
    ) {
        let mut body_scope_builder = match self.pop() {
            ScopeBuilder::SyntaxOnlyFunctionBody(builder) => builder,
            _ => panic!("unmatching scope builder"),
        };

        let mut parameter_scope_builder = match self.pop() {
            ScopeBuilder::SyntaxOnlyFunctionParameters(builder) => builder,
            _ => panic!("unmatching scope builder"),
        };

        self.handle_popped_function_parameters_and_body(
            function_declaration_properties,
            possibly_annex_b_functions,
            &mut parameter_scope_builder,
            &mut body_scope_builder,
        );

        (parameter_scope_builder, body_scope_builder)
    }

    fn get_function_parameters<'a>(&'a mut self) -> &'a mut FunctionParametersScopeBuilder {
        match self.innermost() {
            ScopeBuilder::FunctionParameters(builder) => builder,
            _ => panic!("unmatching scope builder"),
        }
    }

    fn get_syntax_only_function_parameters<'a>(
        &'a mut self,
    ) -> &'a mut SharedFunctionParametersScopeBuilder {
        match self.innermost() {
            ScopeBuilder::SyntaxOnlyFunctionParameters(builder) => builder,
            _ => panic!("unmatching scope builder"),
        }
    }

    fn push_function_body(&mut self, builder: FunctionBodyScopeBuilder) {
        self.stack.push(ScopeBuilder::FunctionBody(builder))
    }
    fn push_syntax_only_function_body(&mut self, builder: SharedFunctionBodyScopeBuilder) {
        self.stack
            .push(ScopeBuilder::SyntaxOnlyFunctionBody(builder))
    }

    fn update_closed_over_bindings_for_lazy(&mut self, builder: &BaseScopeBuilder) {
        match self.closed_over_bindings_for_lazy.last_mut() {
            Some(bindings) => {
                for name in builder.name_tracker.defined_and_closed_over_vars() {
                    bindings.push(Some(*name));
                }
                bindings.push(None);
            }
            None => {
                // We're leaving lexical scope in top-level script.
            }
        }
    }

    // Just like update_closed_over_bindings_for_lazy, but merge
    // 2 builders for parameters and body, in case the function doesn't have
    // extra body scope.
    fn update_closed_over_bindings_for_lazy_with_parameters_and_body(
        &mut self,
        builder1: &BaseScopeBuilder,
        builder2: &BaseScopeBuilder,
    ) {
        match self.closed_over_bindings_for_lazy.last_mut() {
            Some(bindings) => {
                for name in builder1.name_tracker.defined_and_closed_over_vars() {
                    bindings.push(Some(*name));
                }
                for name in builder2.name_tracker.defined_and_closed_over_vars() {
                    bindings.push(Some(*name));
                }
                bindings.push(None);
            }
            None => {
                // We're leaving lexical scope in top-level script.
            }
        }
    }

    /// Pop the current scope.
    fn pop(&mut self) -> ScopeBuilder {
        self.stack.pop().expect("unmatching scope builder")
    }

    fn depth(&self) -> usize {
        self.stack.len()
    }
}

/// Builds `ScriptStencil` for all functions (both non-lazy and lazy).
/// The script is set to lazy function, with inner functions and
/// closed over bindings populated in gcthings list.
///
/// TODO: For non-lazy function, gcthings list should be populated in the
///       emitter pass, not here.
#[derive(Debug)]
pub struct FunctionScriptStencilBuilder {
    /// The map from function node to ScriptStencil.
    ///
    /// The map is separated into `function_stencil_indicies` and `functions`,
    /// because it can be referred to in different ways from multiple places:
    ///   * map from Function AST node (`function_stencil_indices`)
    ///   * enclosing script/function, to list inner functions
    function_stencil_indices: AssociatedData<ScriptStencilIndex>,
    scripts: ScriptStencilList,

    /// The stack of functions that the current context is in.
    ///
    /// The last element in this stack represents the current function, where
    /// the inner function will be stored
    function_stack: Vec<ScriptStencilIndex>,
}

impl FunctionScriptStencilBuilder {
    fn new() -> Self {
        let scripts = ScriptStencilList::new_with_empty_top_level();

        Self {
            function_stencil_indices: AssociatedData::new(),
            scripts,
            function_stack: Vec::new(),
        }
    }

    /// Enter a function.
    ///
    /// This creates `ScriptStencil` for the function, and adds it to
    /// enclosing function if exists.
    fn enter<T>(
        &mut self,
        fun: &T,
        syntax_kind: FunctionSyntaxKind,
        enclosing_scope_index: Option<ScopeIndex>,
    ) -> ScriptStencilIndex
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        let loc = fun.get_loc();
        let source_start = loc.start as u32;

        // FIXME: Map from offset to line/column.
        let lineno = 1;
        let column = 0;

        let function_stencil = ScriptStencil::lazy_function(
            SourceExtent {
                source_start,
                source_end: 0,
                to_string_start: source_start,
                to_string_end: 0,
                lineno,
                column,
            },
            None,
            syntax_kind.is_generator(),
            syntax_kind.is_async(),
            FunctionFlags::interpreted(syntax_kind),
            enclosing_scope_index,
        );
        let index = self.scripts.push(function_stencil);
        self.function_stencil_indices.insert(fun, index);

        match self.maybe_current_mut() {
            Some(enclosing) => {
                enclosing.push_inner_function(index);
            }
            None => {}
        }

        self.function_stack.push(index);

        index
    }

    /// Leave a function, setting its source location.
    fn leave<T>(&mut self, fun: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        let loc = fun.get_loc();
        let source_end = loc.end;

        self.current_mut().set_source_end(source_end);
        self.current_mut().set_to_string_end(source_end);

        self.function_stack.pop();
    }

    /// Returns the current function's index.
    /// Panics if no current function is found.
    fn current_index(&self) -> ScriptStencilIndex {
        *self
            .function_stack
            .last()
            .expect("should be inside function")
    }

    /// Returns a immutable reference to the innermost function. None otherwise.
    fn maybe_current<'a>(&'a self) -> Option<&'a ScriptStencil> {
        let maybe_index = self.function_stack.last();
        maybe_index.map(move |index| self.scripts.get(*index))
    }

    /// Returns a immutable reference to the current function.
    /// Panics if no current function is found.
    fn current<'a>(&'a self) -> &'a ScriptStencil {
        self.maybe_current().expect("should be inside function")
    }

    /// Returns a mutable reference to the innermost function. None otherwise.
    fn maybe_current_mut<'a>(&'a mut self) -> Option<&'a mut ScriptStencil> {
        let maybe_index = self.function_stack.last().cloned();
        maybe_index.map(move |index| self.scripts.get_mut(index))
    }

    /// Returns a mutable reference to the current function.
    /// Panics if no current function is found.
    fn current_mut<'a>(&'a mut self) -> &'a mut ScriptStencil {
        self.maybe_current_mut().expect("should be inside function")
    }

    /// Sets the name of the current function.
    fn set_function_name(&mut self, name: SourceAtomSetIndex) {
        self.current_mut().set_fun_name(name);
    }

    /// Sets the position of the function parameters.
    /// `params` should point to the `(` of the function parameters.
    /// If it's an arrow function without parenthesis, `params` should point
    /// the parameter binding.
    fn on_function_parameters<T>(&mut self, params: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        let loc = params.get_loc();
        let params_start = loc.start;
        self.current_mut().set_source_starts(params_start);
    }

    fn on_non_rest_parameter(&mut self) {
        let fun = self.current_mut();
        fun.add_fun_nargs();
    }

    /// Flags that the current function has rest parameter.
    fn on_rest_parameter(&mut self) {
        let fun = self.current_mut();
        fun.add_fun_nargs();
        fun.set_has_rest();
    }

    fn add_closed_over_bindings(
        &mut self,
        mut closed_over_bindings_for_lazy: Vec<Option<SourceAtomSetIndex>>,
    ) {
        // Remove trailing `None`s.
        loop {
            match closed_over_bindings_for_lazy.last() {
                Some(Some(_)) => {
                    // The last item isn't None.
                    break;
                }

                Some(None) => {
                    // The last item is None, remove it
                    closed_over_bindings_for_lazy.pop();
                }

                None => {
                    // List is empty.
                    break;
                }
            }
        }

        let current = self.current_mut();
        for name in closed_over_bindings_for_lazy {
            match name {
                Some(name) => current.push_closed_over_bindings(name),
                None => current.push_closed_over_bindings_delimiter(),
            }
        }
    }
}

/// Scope builder shouldn't raise any error except not-implemented.
/// This struct should eventually be removed.
#[derive(Clone, Debug)]
pub enum ScopeBuildError {
    NotImplemented(&'static str),
}

/// Receives method calls telling about a JS script and builds a
/// `ScopeDataMap`.
///
/// Usage: This struct's public methods must be called for each scope,
/// declaration, and identifier in a JS script, in source order. Then use
/// `ScopeDataMap::from()` to extract the results. Currently this object is
/// driven by method calls from a `pass::ScopePass`.
#[derive(Debug)]
pub struct ScopeDataMapBuilder {
    scope_kind_stack: ScopeKindStack,
    builder_stack: ScopeBuilderStack,
    scopes: ScopeDataList,

    /// The global scope information.
    /// Using `Option` to make this field populated later.
    global: Option<ScopeIndex>,

    /// The map from non-global AST node to scope information.
    non_global: AssociatedData<ScopeIndex>,

    function_stencil_builder: FunctionScriptStencilBuilder,

    function_declaration_properties: FunctionDeclarationPropertyMap,

    possibly_annex_b_functions: PossiblyAnnexBFunctionList,

    error: Option<ScopeBuildError>,

    // The depth of `builder_stack` where syntax-only mode started.
    // The pointed `builder_stack` item should be enclosing scope of
    // function.
    //
    // None if not in syntax-only mode.
    syntax_only_depth: Option<usize>,
}

impl ScopeDataMapBuilder {
    pub fn new() -> Self {
        Self {
            scope_kind_stack: ScopeKindStack::new(),
            builder_stack: ScopeBuilderStack::new(),
            scopes: ScopeDataList::new(),
            global: None,
            non_global: AssociatedData::new(),
            function_stencil_builder: FunctionScriptStencilBuilder::new(),
            function_declaration_properties: FunctionDeclarationPropertyMap::new(),
            possibly_annex_b_functions: PossiblyAnnexBFunctionList::new(),
            error: None,
            syntax_only_depth: None,
        }
    }

    fn set_error(&mut self, e: ScopeBuildError) {
        if self.error.is_none() {
            self.error = Some(e);
        }
    }

    pub fn enter_syntax_only_mode(&mut self) {
        assert!(self.syntax_only_depth.is_none());
        self.syntax_only_depth = Some(self.builder_stack.depth());
    }

    fn maybe_exit_syntax_only_mode(&mut self) {
        if self.syntax_only_depth.is_none() {
            return;
        }

        let depth = self.syntax_only_depth.unwrap();
        if self.builder_stack.depth() == depth {
            self.syntax_only_depth = None;
            return;
        }

        debug_assert!(self.builder_stack.depth() > depth);
    }

    pub fn is_syntax_only_mode(&self) -> bool {
        self.syntax_only_depth.is_some()
    }

    pub fn before_script(&mut self) {
        if self.is_syntax_only_mode() {
            let builder = BaseScopeBuilder::new();
            self.builder_stack.push_syntax_only_global(builder);
            return;
        }

        // SetRealmGlobalObject ( realmRec, globalObj, thisValue )
        // https://tc39.es/ecma262/#sec-setrealmglobalobject
        //
        // Steps 1-4, 7.
        // (done in runtime)

        // Step 5. Let newGlobalEnv be
        //         NewGlobalEnvironment(globalObj, thisValue).
        let index = self.scopes.allocate();
        let builder = GlobalScopeBuilder::new(index);
        self.global = Some(index);

        // Step 6. Set realmRec.[[GlobalEnv]] to newGlobalEnv.
        // (implicit)

        // ScriptEvaluation ( scriptRecord )
        // https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
        //
        // Step 1. Let globalEnv be scriptRecord.[[Realm]].[[GlobalEnv]].
        // (implicit)

        // Step 2. Let scriptContext be a new ECMAScript code execution context.
        // (implicit)

        // Steps 3-5.
        // (done in runtime)

        // Step 6. Set the VariableEnvironment of scriptContext to globalEnv.
        // Step 7. Set the LexicalEnvironment of scriptContext to globalEnv.
        self.builder_stack.push_global(builder);

        // Steps 8-17.
        // (done in runtime)
    }

    pub fn after_script(&mut self) {
        if self.is_syntax_only_mode() {
            self.builder_stack.pop_syntax_only_global();
            self.maybe_exit_syntax_only_mode();
            return;
        }

        let builder = self.builder_stack.pop_global();

        // Runtime Semantics: GlobalDeclarationInstantiation ( script, env )
        // https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
        //
        // Steps 3-6.
        // (done in runtime)

        // Steps 12-18.
        let scope_index = builder.scope_index;
        let scope = builder.into_scope_data(
            &mut self.function_declaration_properties,
            &mut self.possibly_annex_b_functions,
        );
        self.scopes.populate(scope_index, scope);
    }

    pub fn before_block_statement<T>(&mut self, block: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        if self.is_syntax_only_mode() {
            let builder = BaseScopeBuilder::new();
            self.builder_stack.push_syntax_only_block(builder);
            return;
        }

        // Runtime Semantics: Evaluation
        // https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
        //
        // Block : { StatementList }
        //
        // Step 1. Let oldEnv be the running execution context's
        //         LexicalEnvironment.
        // (implicit)

        // Step 2. Let blockEnv be NewDeclarativeEnvironment(oldEnv).
        let index = self.scopes.allocate();
        let builder = BlockScopeBuilder::new(index);
        self.non_global.insert(block, index);

        // Step 3. Perform
        //         BlockDeclarationInstantiation(StatementList, blockEnv).
        // (done in leave_enum_statement_variant_block_statement)

        // Step 4. Set the running execution context's LexicalEnvironment to
        //         blockEnv.
        self.builder_stack.push_block(builder);

        // Step 5. Let blockValue be the result of evaluating StatementList.
        // (done in runtime)
    }

    pub fn after_block_statement(&mut self) {
        if self.is_syntax_only_mode() {
            self.builder_stack.pop_syntax_only_block();
            return;
        }

        let builder = self.builder_stack.pop_block();
        let enclosing = self.builder_stack.current_scope_index();

        // Runtime Semantics: Evaluation
        // https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
        //
        // Block : { StatementList }
        //
        // Step 3. Perform
        //         BlockDeclarationInstantiation(StatementList, blockEnv).
        self.scopes.populate(
            builder.scope_index,
            builder.into_scope_data(enclosing, &mut self.possibly_annex_b_functions),
        );

        // Step 6. Set the running execution context's LexicalEnvironment to
        //         oldEnv.

        // Step 7. Return blockValue.
        // (implicit)
    }

    pub fn before_var_declaration(&mut self) {
        self.scope_kind_stack.push(ScopeKind::Var);
    }

    pub fn after_var_declaration(&mut self) {
        self.scope_kind_stack.pop(ScopeKind::Var);
    }

    pub fn before_let_declaration(&mut self) {
        self.scope_kind_stack.push(ScopeKind::Let);
    }

    pub fn after_let_declaration(&mut self) {
        self.scope_kind_stack.pop(ScopeKind::Let);
    }

    pub fn before_const_declaration(&mut self) {
        self.scope_kind_stack.push(ScopeKind::Const);
    }

    pub fn after_const_declaration(&mut self) {
        self.scope_kind_stack.pop(ScopeKind::Const);
    }

    pub fn on_binding_identifier(&mut self, name: SourceAtomSetIndex) {
        if self.scope_kind_stack.is_empty() {
            // FIXME
            // Do nothing for unsupported case.
            self.set_error(ScopeBuildError::NotImplemented(
                "Unsupported binding identifier",
            ));
            return;
        }

        match self.scope_kind_stack.innermost() {
            ScopeKind::Var => self.builder_stack.innermost_var().declare_var(name),
            ScopeKind::Let => self.builder_stack.innermost_lexical().declare_let(name),
            ScopeKind::Const => self.builder_stack.innermost_lexical().declare_const(name),
            ScopeKind::FunctionName => {
                self.builder_stack.innermost().set_function_name(name);
                self.function_stencil_builder.set_function_name(name);
            }
            ScopeKind::FunctionParametersAndBody => {
                // FIXME
                // Do nothing for unsupported case.
                self.set_error(ScopeBuildError::NotImplemented(
                    "Unsupported binding identifier",
                ));
                return;
            }
            ScopeKind::FormalParameter => self.builder_stack.innermost().declare_param(name),
            _ => panic!("Not implemeneted"),
        }
    }

    pub fn on_non_binding_identifier(&mut self, name: SourceAtomSetIndex) {
        self.builder_stack
            .innermost()
            .base_mut()
            .name_tracker
            .note_use(name);
    }

    pub fn before_function_declaration<T>(
        &mut self,
        name: SourceAtomSetIndex,
        fun: &T,
        is_generator: bool,
        is_async: bool,
    ) -> ScriptStencilIndex
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        if is_generator || is_async {
            // FIXME: Generator and async should mark all bindings closed over.
            self.set_error(ScopeBuildError::NotImplemented(
                "Generator or async function",
            ));
        }

        let fun_index = self.function_stencil_builder.enter(
            fun,
            FunctionSyntaxKind::function_declaration(is_generator, is_async),
            self.builder_stack.maybe_current_scope_index(),
        );

        match self.builder_stack.innermost_lexical() {
            ScopeBuilder::Global(ref mut builder) => builder.declare_function(name, fun_index),
            ScopeBuilder::SyntaxOnlyGlobal(ref mut builder) => builder.declare_function(name),
            ScopeBuilder::Block(ref mut builder) => builder.declare_function(name, fun_index),
            ScopeBuilder::SyntaxOnlyBlock(ref mut builder) => builder.declare_function(name),
            ScopeBuilder::FunctionBody(ref mut builder) => {
                builder.declare_function(name, fun_index)
            }
            ScopeBuilder::SyntaxOnlyFunctionBody(ref mut builder) => builder.declare_function(name),
            _ => panic!("unexpected lexical for FunctionDeclaration"),
        }

        self.scope_kind_stack.push(ScopeKind::FunctionName);

        fun_index
    }

    pub fn after_function_declaration<T>(&mut self, fun: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.function_stencil_builder.leave(fun);

        self.scope_kind_stack.pop(ScopeKind::FunctionName);
    }

    pub fn before_function_expression<T>(&mut self, fun: &T, is_generator: bool, is_async: bool)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        // FIXME: Anonymous function expression needs inferred name.
        self.set_error(ScopeBuildError::NotImplemented(
            "Function expression (name analysis)",
        ));

        self.scope_kind_stack.push(ScopeKind::FunctionName);

        self.function_stencil_builder.enter(
            fun,
            FunctionSyntaxKind::function_expression(is_generator, is_async),
            self.builder_stack.maybe_current_scope_index(),
        );

        if self.is_syntax_only_mode() {
            let builder = BaseScopeBuilder::new();
            self.builder_stack
                .push_syntax_only_function_expression(builder);
            return;
        }

        let index = self.scopes.allocate();
        let builder = FunctionExpressionScopeBuilder::new(index);
        self.non_global.insert(fun, index);

        self.builder_stack.push_function_expression(builder);
    }

    pub fn after_function_expression<T>(&mut self, fun: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.function_stencil_builder.leave(fun);

        self.scope_kind_stack.pop(ScopeKind::FunctionName);

        if self.is_syntax_only_mode() {
            self.builder_stack.pop_syntax_only_function_expression();
            self.maybe_exit_syntax_only_mode();
            return;
        }

        let builder = self.builder_stack.pop_function_expression();
        let enclosing = self.builder_stack.current_scope_index();

        self.scopes
            .populate(builder.scope_index, builder.into_scope_data(enclosing));
    }

    pub fn before_method<T>(&mut self, fun: &T, is_generator: bool, is_async: bool)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        // FIXME: Support PropertyName as function name.
        self.set_error(ScopeBuildError::NotImplemented("Method (name calculation)"));

        self.function_stencil_builder.enter(
            fun,
            FunctionSyntaxKind::method(is_generator, is_async),
            self.builder_stack.maybe_current_scope_index(),
        );
    }

    pub fn after_method<T>(&mut self, fun: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.function_stencil_builder.leave(fun);
    }

    pub fn before_getter<T>(&mut self, fun: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        // FIXME: Support PropertyName as function name.
        self.set_error(ScopeBuildError::NotImplemented("Getter (name calculation)"));

        self.function_stencil_builder.enter(
            fun,
            FunctionSyntaxKind::getter(),
            self.builder_stack.maybe_current_scope_index(),
        );
    }

    pub fn on_getter_parameter<T>(&mut self, param: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.before_function_parameters(param);
        self.after_function_parameters();
    }

    pub fn after_getter<T>(&mut self, fun: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.function_stencil_builder.leave(fun);
    }

    pub fn before_setter<T>(&mut self, fun: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        // FIXME: Support PropertyName as function name.
        self.set_error(ScopeBuildError::NotImplemented("Setter (name calculation)"));

        self.function_stencil_builder.enter(
            fun,
            FunctionSyntaxKind::setter(),
            self.builder_stack.maybe_current_scope_index(),
        );
    }

    pub fn before_setter_parameter<T>(&mut self, param: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.before_function_parameters(param);
        self.before_parameter();
    }

    pub fn after_setter_parameter(&mut self) {
        self.after_function_parameters();
    }

    pub fn after_setter<T>(&mut self, fun: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.function_stencil_builder.leave(fun);
    }

    pub fn before_arrow_function<T>(&mut self, is_async: bool, params: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        // FIXME: Arrow function needs to access enclosing scope's
        //        `this` and `arguments`.
        self.set_error(ScopeBuildError::NotImplemented(
            "Arrow function (special name handling)",
        ));

        self.function_stencil_builder.enter(
            params,
            FunctionSyntaxKind::arrow(is_async),
            self.builder_stack.maybe_current_scope_index(),
        );
    }

    pub fn after_arrow_function<T>(&mut self, body: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.function_stencil_builder.leave(body);
    }

    pub fn before_function_parameters<T>(&mut self, params: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.scope_kind_stack
            .push(ScopeKind::FunctionParametersAndBody);

        self.builder_stack
            .closed_over_bindings_for_lazy
            .push(Vec::new());

        self.function_stencil_builder.on_function_parameters(params);

        self.scope_kind_stack.push(ScopeKind::FormalParameter);

        let is_arrow = self.function_stencil_builder.current().is_arrow_function();

        if self.is_syntax_only_mode() {
            let builder = SharedFunctionParametersScopeBuilder::new(is_arrow);
            self.builder_stack
                .push_syntax_only_function_parameters(builder);
            return;
        }

        let index = self.scopes.allocate();

        let builder = FunctionParametersScopeBuilder::new(
            index,
            is_arrow,
            self.function_stencil_builder.current_index(),
        );
        self.non_global.insert(params, index);

        self.builder_stack.push_function_parameters(builder);
    }

    pub fn after_function_parameters(&mut self) {
        self.scope_kind_stack.pop(ScopeKind::FormalParameter);
    }

    pub fn before_parameter(&mut self) {
        self.function_stencil_builder.on_non_rest_parameter();

        if self.is_syntax_only_mode() {
            return;
        }

        let builder = self.builder_stack.get_function_parameters();
        builder.before_parameter();
    }

    pub fn before_binding_pattern(&mut self) {
        match self.builder_stack.innermost() {
            ScopeBuilder::FunctionParameters(builder) => {
                builder.before_binding_pattern();
            }
            ScopeBuilder::SyntaxOnlyFunctionParameters(builder) => {
                builder.before_binding_pattern();
            }
            _ => {}
        }
    }

    pub fn after_initializer(&mut self) {
        match self.builder_stack.innermost() {
            ScopeBuilder::FunctionParameters(builder) => {
                builder.after_initializer();
            }
            ScopeBuilder::SyntaxOnlyFunctionParameters(builder) => {
                builder.after_initializer();
            }
            _ => {}
        }
    }

    pub fn before_computed_property_name(&mut self) {
        match self.builder_stack.innermost() {
            ScopeBuilder::FunctionParameters(builder) => {
                builder.before_computed_property_name();
            }
            ScopeBuilder::SyntaxOnlyFunctionParameters(builder) => {
                builder.before_computed_property_name();
            }
            _ => {}
        }
    }

    pub fn before_rest_parameter(&mut self) {
        self.function_stencil_builder.on_rest_parameter();

        if self.is_syntax_only_mode() {
            let builder = self.builder_stack.get_syntax_only_function_parameters();
            builder.before_rest_parameter();
            return;
        }

        let builder = self.builder_stack.get_function_parameters();
        builder.before_rest_parameter();
    }

    pub fn before_function_body<T>(&mut self, body: &T)
    where
        T: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        if self.is_syntax_only_mode() {
            let builder = SharedFunctionBodyScopeBuilder::new();
            self.builder_stack.push_syntax_only_function_body(builder);
            return;
        }

        let var_index = self.scopes.allocate();
        let lexical_index = self.scopes.allocate();
        debug_assert!(lexical_index == var_index.next());

        let builder = FunctionBodyScopeBuilder::new(var_index, lexical_index);
        self.non_global.insert(body, var_index);

        self.builder_stack.push_function_body(builder);
    }

    fn add_closed_over_bindings(&mut self) {
        self.function_stencil_builder.add_closed_over_bindings(
            self.builder_stack
                .closed_over_bindings_for_lazy
                .pop()
                .expect("Vector should be pushed by before_function_parameters"),
        );
    }

    fn update_function_stencil(
        &mut self,
        parameter_scope_builder: &SharedFunctionParametersScopeBuilder,
        body_scope_builder: &SharedFunctionBodyScopeBuilder,
    ) {
        let has_extra_body_var_scope = parameter_scope_builder.has_parameter_expressions;

        let bindings_accessed_dynamically =
            parameter_scope_builder.base.bindings_accessed_dynamically;

        let needs_environment_object = if has_extra_body_var_scope {
            bindings_accessed_dynamically || parameter_scope_builder.is_parameter_closed_over()
        } else {
            bindings_accessed_dynamically
                || parameter_scope_builder.is_parameter_closed_over()
                || body_scope_builder.is_var_closed_over()
        };

        let fun_stencil = self.function_stencil_builder.current_mut();

        if needs_environment_object {
            fun_stencil.set_needs_function_environment_objects();
        }

        if has_extra_body_var_scope {
            if body_scope_builder.var_names.len() > 0 {
                fun_stencil.set_function_has_extra_body_var_scope();
            }
        }

        let strict = parameter_scope_builder.strict;
        let simple_parameter_list = parameter_scope_builder.simple_parameter_list;
        let has_mapped_arguments = !strict && simple_parameter_list;
        if has_mapped_arguments {
            fun_stencil.set_has_mapped_args_obj();
        }

        if !fun_stencil.is_arrow_function() {
            let has_used_this = parameter_scope_builder
                .base
                .name_tracker
                .is_used_or_closed_over(CommonSourceAtomSetIndices::this())
                || bindings_accessed_dynamically;

            if has_used_this {
                fun_stencil.set_function_has_this_binding();
            }

            let has_used_arguments = parameter_scope_builder
                .base
                .name_tracker
                .is_used_or_closed_over(CommonSourceAtomSetIndices::arguments())
                || bindings_accessed_dynamically;

            let mut uses_arguments = false;
            let mut try_declare_arguments = has_used_arguments;

            let parameter_has_arguments = parameter_scope_builder.parameter_has_arguments;

            let var_names_has_arguments = body_scope_builder
                .var_names
                .contains(&CommonSourceAtomSetIndices::arguments());

            let body_has_defined_arguments =
                var_names_has_arguments || body_scope_builder.function_or_lexical_has_arguments;

            // FunctionDeclarationInstantiation ( func, argumentsList )
            // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
            //
            // Step 17 (Else if "arguments" is an element of parameterNames...)
            // and step 18 (Else if hasParameterExpressions is false...) say
            // formal parameters, lexical bindings, and body-level functions
            // named 'arguments' shadow the arguments object.
            //
            // So even if there wasn't a free use of 'arguments' but there is a
            // var binding of 'arguments', we still might need the arguments
            // object.
            //
            // If we have an extra var scope due to parameter expressions and
            // the body declared 'var arguments', we still need to declare
            // 'arguments' in the function scope.
            //
            // NOTE: This is implementation-specfic optimization, and has
            //       no corresponding steps in the spec.
            if var_names_has_arguments {
                if has_extra_body_var_scope {
                    try_declare_arguments = true;
                } else if !parameter_has_arguments {
                    uses_arguments = true;
                }
            }

            if try_declare_arguments {
                // if extra body var scope exists, the existence of `arguments`
                // binding in function body doesn't affect.
                let declare_arguments = !parameter_has_arguments
                    && (has_extra_body_var_scope || !body_has_defined_arguments);

                if declare_arguments {
                    fun_stencil.set_should_declare_arguments();
                    uses_arguments = true;
                }
            }

            if uses_arguments {
                fun_stencil.set_needs_args_obj();

                if has_used_this {
                    // FIXME
                    // IsLikelyConstructorWrapper should be set if
                    // `.apply()` is used and `return` isn't used.
                    self.set_error(ScopeBuildError::NotImplemented(
                        "IsLikelyConstructorWrapper condition",
                    ));
                }
            }
        }
    }

    pub fn after_function_body(&mut self) {
        self.scope_kind_stack
            .pop(ScopeKind::FunctionParametersAndBody);

        if self.is_syntax_only_mode() {
            let (parameter_scope_builder, body_scope_builder) = self
                .builder_stack
                .pop_syntax_only_function_parameters_and_body(
                    &mut self.function_declaration_properties,
                    &mut self.possibly_annex_b_functions,
                );
            self.possibly_annex_b_functions.clear();
            self.add_closed_over_bindings();
            self.update_function_stencil(¶meter_scope_builder, &body_scope_builder);

            self.maybe_exit_syntax_only_mode();
            return;
        }

        let (parameter_scope_builder, body_scope_builder) =
            self.builder_stack.pop_function_parameters_and_body(
                &mut self.function_declaration_properties,
                &mut self.possibly_annex_b_functions,
            );
        self.possibly_annex_b_functions.clear();
        self.add_closed_over_bindings();
        self.update_function_stencil(¶meter_scope_builder.shared, &body_scope_builder.shared);

        let enclosing = self.builder_stack.current_scope_index();

        let function_scope_index = parameter_scope_builder.scope_index;
        let var_scope_index = body_scope_builder.var_scope_index;
        let lexical_scope_index = body_scope_builder.lexical_scope_index;

        // Runtime Semantics: EvaluateBody
        // https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-evaluatebody
        //
        // With parameters functionObject and List argumentsList.
        //
        // FunctionBody : FunctionStatementList
        //
        // Step 1. Perform ? FunctionDeclarationInstantiation(functionObject,
        //         argumentsList).
        let scope_data_set =
            parameter_scope_builder.into_scope_data_set(enclosing, body_scope_builder);

        self.scopes
            .populate(function_scope_index, scope_data_set.function);
        self.scopes
            .populate(var_scope_index, scope_data_set.extra_body_var);
        self.scopes
            .populate(lexical_scope_index, scope_data_set.lexical);
    }

    pub fn before_catch_clause(&mut self) {
        // FIXME: NewDeclarativeEnvironment for catch parameter.
        self.set_error(ScopeBuildError::NotImplemented("try-catch"));
    }

    pub fn on_direct_eval(&mut self) {
        // FIXME: Propagate to script flags.
        self.set_error(ScopeBuildError::NotImplemented(
            "direct eval (script flags)",
        ));

        if let Some(parameter_scope_builder) =
            self.builder_stack.maybe_innermost_function_parameters()
        {
            parameter_scope_builder.has_direct_eval = true;
        }

        self.builder_stack
            .innermost()
            .base_mut()
            .bindings_accessed_dynamically = true;
    }

    pub fn on_class(&mut self) {
        // FIXME: NewDeclarativeEnvironment for class tail.
        self.set_error(ScopeBuildError::NotImplemented("class"));
    }

    pub fn on_with(&mut self) {
        // FIXME: Propagate to script flags.
        self.set_error(ScopeBuildError::NotImplemented("with statement"));
    }

    pub fn on_delete(&mut self) {
        // FIXME: Propagate to script flags.
        self.set_error(ScopeBuildError::NotImplemented("delete operator"));
    }

    pub fn on_lexical_for(&mut self) {
        // FIXME: NewDeclarativeEnvironment in for statement
        self.set_error(ScopeBuildError::NotImplemented("lexical for"));
    }

    pub fn on_switch(&mut self) {
        // FIXME: NewDeclarativeEnvironment in for case block
        self.set_error(ScopeBuildError::NotImplemented("switch"));
    }

    pub fn on_new_target(&mut self) {
        if let Some(fun_stencil) = self.function_stencil_builder.maybe_current_mut() {
            fun_stencil.set_function_has_new_target_binding()
        }
    }
}

pub struct ScopeDataMapAndScriptStencilList {
    pub scope_data_map: ScopeDataMap,
    pub function_stencil_indices: AssociatedData<ScriptStencilIndex>,
    pub function_declaration_properties: FunctionDeclarationPropertyMap,
    pub scripts: ScriptStencilList,
    pub error: Option<ScopeBuildError>,
}

impl From<ScopeDataMapBuilder> for ScopeDataMapAndScriptStencilList {
    fn from(builder: ScopeDataMapBuilder) -> ScopeDataMapAndScriptStencilList {
        ScopeDataMapAndScriptStencilList {
            scope_data_map: ScopeDataMap::new(
                builder.scopes,
                builder.global.expect("There should be global scope data"),
                builder.non_global,
            ),
            function_stencil_indices: builder.function_stencil_builder.function_stencil_indices,
            function_declaration_properties: builder.function_declaration_properties,
            scripts: builder.function_stencil_builder.scripts,
            error: builder.error,
        }
    }
}

[Dauer der Verarbeitung: 0.80 Sekunden, vorverarbeitet 2026-04-25]