diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/jsparagus-scope | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/jsparagus-scope')
-rw-r--r-- | third_party/rust/jsparagus-scope/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/jsparagus-scope/Cargo.toml | 26 | ||||
-rw-r--r-- | third_party/rust/jsparagus-scope/src/builder.rs | 3485 | ||||
-rw-r--r-- | third_party/rust/jsparagus-scope/src/data.rs | 65 | ||||
-rw-r--r-- | third_party/rust/jsparagus-scope/src/free_name_tracker.rs | 129 | ||||
-rw-r--r-- | third_party/rust/jsparagus-scope/src/lib.rs | 36 | ||||
-rw-r--r-- | third_party/rust/jsparagus-scope/src/pass.rs | 373 |
7 files changed, 4115 insertions, 0 deletions
diff --git a/third_party/rust/jsparagus-scope/.cargo-checksum.json b/third_party/rust/jsparagus-scope/.cargo-checksum.json new file mode 100644 index 0000000000..b73043f980 --- /dev/null +++ b/third_party/rust/jsparagus-scope/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"d66c27f46f6da2bd7a84f762b938be6f4d0f3e20bdcd22862aba780c1039b5ee","src/builder.rs":"3aa261f329a6f4ee45683f4a69f85408d63e6bd5531b74dbbb4b4abbe8093b2f","src/data.rs":"a3cf1e7b1a96a619bcb8bd87f39bbd44dda0fc554c70a5d4d45b70eb03e69401","src/free_name_tracker.rs":"322228be4262d79d148f954a656b4f09fe953b324393fcc5925675c0e7777828","src/lib.rs":"529f7598a3034b347a20307a752b467091d820df6be67ebc4a3bd8e02568511b","src/pass.rs":"e13b2677ddf03b504efd287963caaa28b8a16c5fc0896f97bea2584f8280063d"},"package":null}
\ No newline at end of file diff --git a/third_party/rust/jsparagus-scope/Cargo.toml b/third_party/rust/jsparagus-scope/Cargo.toml new file mode 100644 index 0000000000..32b17df1c0 --- /dev/null +++ b/third_party/rust/jsparagus-scope/Cargo.toml @@ -0,0 +1,26 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "jsparagus-scope" +version = "0.1.0" +authors = ["The jsparagus Project Developers"] +license = "MIT/Apache-2.0" + +[dependencies] +indexmap = "2.0" + +[dependencies.jsparagus-ast] +path = "../ast" + +[dependencies.jsparagus-stencil] +path = "../stencil" diff --git a/third_party/rust/jsparagus-scope/src/builder.rs b/third_party/rust/jsparagus-scope/src/builder.rs new file mode 100644 index 0000000000..e170194166 --- /dev/null +++ b/third_party/rust/jsparagus-scope/src/builder.rs @@ -0,0 +1,3485 @@ +//! 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, + } + } +} diff --git a/third_party/rust/jsparagus-scope/src/data.rs b/third_party/rust/jsparagus-scope/src/data.rs new file mode 100644 index 0000000000..1baeffe097 --- /dev/null +++ b/third_party/rust/jsparagus-scope/src/data.rs @@ -0,0 +1,65 @@ +//! Data generated by scope analysis, and consumed only by emitter + +use std::collections::HashMap; +use stencil::script::ScriptStencilIndex; + +/// Data associated to a function. +#[derive(Debug)] +pub struct FunctionProperty { + is_annex_b: bool, +} + +impl FunctionProperty { + fn is_annex_b_default() -> bool { + false + } + + pub fn new() -> Self { + Self { + is_annex_b: Self::is_annex_b_default(), + } + } + + pub fn mark_annex_b(&mut self) { + self.is_annex_b = true; + } + + pub fn is_annex_b(&self) -> bool { + self.is_annex_b + } +} + +/// Map from function to associated data. +#[derive(Debug)] +pub struct FunctionDeclarationPropertyMap { + /// This map is populated lazily, only for functions with non-default + /// property. + props: HashMap<ScriptStencilIndex, FunctionProperty>, +} + +impl FunctionDeclarationPropertyMap { + pub fn new() -> Self { + Self { + props: HashMap::new(), + } + } + + pub fn mark_annex_b(&mut self, index: ScriptStencilIndex) { + if !self.props.contains_key(&index) { + self.props.insert(index, FunctionProperty::new()); + } + + self.props + .get_mut(&index) + .expect("Should exist") + .mark_annex_b(); + } + + pub fn is_annex_b(&self, index: ScriptStencilIndex) -> bool { + if !self.props.contains_key(&index) { + return FunctionProperty::is_annex_b_default(); + } + + self.props.get(&index).expect("Should exist").is_annex_b() + } +} diff --git a/third_party/rust/jsparagus-scope/src/free_name_tracker.rs b/third_party/rust/jsparagus-scope/src/free_name_tracker.rs new file mode 100644 index 0000000000..e376e5a955 --- /dev/null +++ b/third_party/rust/jsparagus-scope/src/free_name_tracker.rs @@ -0,0 +1,129 @@ +//! Code to track free names in scopes, to compute which bindings are closed +//! over. +//! +//! A binding is closed-over if it is defined in a scope in one `JSScript` and +//! can be used in another `JSScript` (a nested function or direct eval). +//! +//! In our single pass over the AST, we compute which bindings are closed-over +//! using a `FreeNameTracker` for each scope. + +use ast::source_atom_set::SourceAtomSetIndex; +use std::collections::hash_map::RandomState; +use std::collections::hash_set::{Difference, Intersection}; +use std::collections::HashSet; + +/// Tracks free names in a scope. +/// +/// Usage: +/// +/// * Create this when entering a scope. +/// +/// * Call `.note_use(name)` when a name is used and `.note_def(name)` when +/// one is defined. +/// +/// * Call `.propagate_from_inner_script(inner)` or +/// `.propagate_from_inner_non_script(inner)` when leaving an inner +/// scope. These methods do the real work of marking bindings as +/// closed-over. +/// +/// The gist of the algorithm is tracking name *uses* and propagating them +/// outward to the scope where the name is *defined*. When we propagate a use +/// out of a function, we put it in `closed_over_names`; when that use is +/// matched with a binding, the binding is closed-over. +/// +/// **Why we do all the work when exiting a scope** - It would save a lot of +/// work if we could mark a def as closed-over as soon as we see a matching +/// use. We could deal with each use as we encounter it. We wouldn't have to +/// track them. The problem is hoisting. Bindings can be used anywhere in a +/// scope, even before their definition. We can't mark a def that we haven't +/// seen yet. The end of scope is the first point where we have definitely seen +/// all uses and defs in that scope. +/// +#[derive(Debug)] +pub struct FreeNameTracker { + /// Names closed over in inner script-scopes, that's not yet + /// defined up to (excluding) the current scope. + /// The name might be defined in the current scope. + /// + /// The name defined in this or outer scope is marked "closed over", and + /// no more propagated to outer scope. + closed_over_names: HashSet<SourceAtomSetIndex>, + + /// Names used in the current scope, and nested non-script scopes. + /// The name might be defined in the current scope. + used_names: HashSet<SourceAtomSetIndex>, + + /// Names defined in the current scope. + def_names: HashSet<SourceAtomSetIndex>, +} + +impl FreeNameTracker { + pub fn new() -> Self { + Self { + closed_over_names: HashSet::new(), + used_names: HashSet::new(), + def_names: HashSet::new(), + } + } + + fn note_closed_over(&mut self, atom: SourceAtomSetIndex) { + self.closed_over_names.insert(atom); + } + + /// Note that the given name is used in this scope. + pub fn note_use(&mut self, atom: SourceAtomSetIndex) { + self.used_names.insert(atom); + } + + pub fn note_def(&mut self, atom: SourceAtomSetIndex) { + self.def_names.insert(atom); + } + + pub fn is_used_or_closed_over(&self, atom: SourceAtomSetIndex) -> bool { + self.used_names.contains(&atom) || self.closed_over_names.contains(&atom) + } + + /// Names closed over in inner script-scopes, that's not yet defined. + pub fn closed_over_freevars(&self) -> Difference<'_, SourceAtomSetIndex, RandomState> { + self.closed_over_names.difference(&self.def_names) + } + + /// Names defined in this scope and closed over by inner script-scopes. + pub fn defined_and_closed_over_vars( + &self, + ) -> Intersection<'_, SourceAtomSetIndex, RandomState> { + self.def_names.intersection(&self.closed_over_names) + } + + /// Names used in this and inner non-script scopes, that's not yet defined. + pub fn used_freevars(&self) -> Difference<'_, SourceAtomSetIndex, RandomState> { + self.used_names.difference(&self.def_names) + } + + pub fn is_closed_over_def(&self, atom: &SourceAtomSetIndex) -> bool { + return self.def_names.contains(atom) && self.closed_over_names.contains(atom); + } + + /// Propagate free closed over names and used names from the inner + /// non-script scope. + pub fn propagate_from_inner_non_script(&mut self, inner: &FreeNameTracker) { + for atom in inner.closed_over_freevars() { + self.note_closed_over(*atom); + } + for atom in inner.used_freevars() { + self.note_use(*atom); + } + } + + #[allow(dead_code)] + /// Propagate free closed over names and used names from the inner + /// script scope, converting everything into free closed over names + pub fn propagate_from_inner_script(&mut self, inner: &FreeNameTracker) { + for atom in inner.closed_over_freevars() { + self.note_closed_over(*atom); + } + for atom in inner.used_freevars() { + self.note_closed_over(*atom); + } + } +} diff --git a/third_party/rust/jsparagus-scope/src/lib.rs b/third_party/rust/jsparagus-scope/src/lib.rs new file mode 100644 index 0000000000..919776ee38 --- /dev/null +++ b/third_party/rust/jsparagus-scope/src/lib.rs @@ -0,0 +1,36 @@ +//! Collect information about scopes and bindings, used by the emitter. +//! +//! Scope analysis happens in a separate pass after the AST is built: +//! +//! 1. Parse the script, check for early errors, and build an AST. +//! 2. Traverse the AST and do scope analysis (this crate). +//! 3. Traverse the AST and emit bytecode. +//! +//! The output of this analysis is a `ScopeDataMapAndFunctionMap` +//! describing each scope, binding, and function in the AST. + +mod builder; +pub mod data; +pub mod free_name_tracker; +mod pass; + +extern crate jsparagus_ast as ast; +extern crate jsparagus_stencil as stencil; + +use ast::visit::Pass; + +pub use builder::ScopeBuildError; +pub use pass::ScopePassResult; + +/// Visit all nodes in the AST, and create a scope data. +/// +/// `ast` must already have been checked for early errors. This analysis does +/// not check for errors, even scope-related errors like redeclaration of a +/// `let` variable. +pub fn generate_scope_data<'alloc, 'a>( + ast: &'alloc ast::types::Program<'alloc>, +) -> ScopePassResult { + let mut scope_pass = pass::ScopePass::new(); + scope_pass.visit_program(ast); + scope_pass.into() +} diff --git a/third_party/rust/jsparagus-scope/src/pass.rs b/third_party/rust/jsparagus-scope/src/pass.rs new file mode 100644 index 0000000000..34bede940e --- /dev/null +++ b/third_party/rust/jsparagus-scope/src/pass.rs @@ -0,0 +1,373 @@ +//! Code to traverse the AST and drive the rest of scope analysis. +//! +//! This module is responsible for walking the AST. Other modules do the actual +//! analysis. +//! +//! Scope analysis is currently a second pass, after parsing, using the AST, +//! but the goal is to do this analysis as part of the parse phase, even when +//! no AST is built. So we try to keep AST use separate from the analysis code. + +use crate::builder::{ScopeBuildError, ScopeDataMapAndScriptStencilList, ScopeDataMapBuilder}; +use crate::data::FunctionDeclarationPropertyMap; +use ast::arena; +use ast::associated_data::AssociatedData; +use ast::source_atom_set::CommonSourceAtomSetIndices; +use ast::{types::*, visit::Pass}; +use std::collections::HashMap; +use stencil::scope::ScopeDataMap; +use stencil::script::{ScriptStencilIndex, ScriptStencilList}; + +/// The result of scope analysis. +pub struct ScopePassResult<'alloc> { + pub scope_data_map: ScopeDataMap, + pub function_declarations: HashMap<ScriptStencilIndex, &'alloc Function<'alloc>>, + pub function_stencil_indices: AssociatedData<ScriptStencilIndex>, + pub function_declaration_properties: FunctionDeclarationPropertyMap, + pub scripts: ScriptStencilList, + pub error: Option<ScopeBuildError>, +} + +/// The top-level struct responsible for extracting the necessary information +/// from the AST. The analysis itself is done mainly by the `ScopeDataMapBuilder`, +/// which has very limited interaction with the AST. +/// +/// FIXME: This should be rewritten as a grammar extension. +#[derive(Debug)] +pub struct ScopePass<'alloc> { + builder: ScopeDataMapBuilder, + function_declarations: HashMap<ScriptStencilIndex, &'alloc Function<'alloc>>, +} + +impl<'alloc> ScopePass<'alloc> { + pub fn new() -> Self { + Self { + builder: ScopeDataMapBuilder::new(), + function_declarations: HashMap::new(), + } + } +} + +impl<'alloc> From<ScopePass<'alloc>> for ScopePassResult<'alloc> { + fn from(pass: ScopePass<'alloc>) -> ScopePassResult<'alloc> { + let ScopeDataMapAndScriptStencilList { + scope_data_map, + function_stencil_indices, + function_declaration_properties, + scripts, + error, + } = pass.builder.into(); + ScopePassResult { + scope_data_map, + function_declarations: pass.function_declarations, + function_stencil_indices, + function_declaration_properties, + scripts, + error, + } + } +} + +impl<'alloc> Pass<'alloc> for ScopePass<'alloc> { + fn enter_script(&mut self, _ast: &'alloc Script<'alloc>) { + self.builder.before_script(); + } + + fn leave_script(&mut self, _ast: &'alloc Script<'alloc>) { + self.builder.after_script(); + } + + fn enter_enum_statement_variant_block_statement(&mut self, block: &'alloc Block<'alloc>) { + self.builder.before_block_statement(block); + } + + fn leave_enum_statement_variant_block_statement(&mut self, _block: &'alloc Block<'alloc>) { + self.builder.after_block_statement(); + } + + fn enter_variable_declaration(&mut self, ast: &'alloc VariableDeclaration<'alloc>) { + match ast.kind { + VariableDeclarationKind::Var { .. } => { + self.builder.before_var_declaration(); + } + VariableDeclarationKind::Let { .. } => { + self.builder.before_let_declaration(); + } + VariableDeclarationKind::Const { .. } => { + self.builder.before_const_declaration(); + } + } + } + + fn leave_variable_declaration(&mut self, ast: &'alloc VariableDeclaration<'alloc>) { + match ast.kind { + VariableDeclarationKind::Var { .. } => { + self.builder.after_var_declaration(); + } + VariableDeclarationKind::Let { .. } => { + self.builder.after_let_declaration(); + } + VariableDeclarationKind::Const { .. } => { + self.builder.after_const_declaration(); + } + } + } + + fn visit_binding_identifier(&mut self, ast: &'alloc BindingIdentifier) { + self.builder.on_binding_identifier(ast.name.value); + } + + // Given we override `visit_binding_identifier` above, + // visit_identifier is not called for Identifier inside BindingIdentifier, + // and this is called only for references, either + // IdentifierExpression or AssignmentTargetIdentifier. + fn visit_identifier(&mut self, ast: &'alloc Identifier) { + self.builder.on_non_binding_identifier(ast.value); + } + + fn visit_enum_expression_variant_this_expression(&mut self) { + self.builder + .on_non_binding_identifier(CommonSourceAtomSetIndices::this()); + } + + fn enter_enum_statement_variant_function_declaration(&mut self, ast: &'alloc Function<'alloc>) { + if !self.builder.is_syntax_only_mode() { + self.builder.enter_syntax_only_mode(); + } + + let name = if let Some(ref name) = ast.name { + name.name.value + } else { + panic!("FunctionDeclaration should have name"); + }; + let fun_index = + self.builder + .before_function_declaration(name, ast, ast.is_generator, ast.is_async); + self.function_declarations.insert(fun_index, ast); + } + + fn leave_enum_statement_variant_function_declaration(&mut self, ast: &'alloc Function<'alloc>) { + self.builder.after_function_declaration(ast); + } + + fn enter_enum_expression_variant_function_expression(&mut self, ast: &'alloc Function<'alloc>) { + if !self.builder.is_syntax_only_mode() { + self.builder.enter_syntax_only_mode(); + } + + self.builder + .before_function_expression(ast, ast.is_generator, ast.is_async); + } + + fn leave_enum_expression_variant_function_expression(&mut self, ast: &'alloc Function<'alloc>) { + self.builder.after_function_expression(ast); + } + + fn visit_formal_parameters(&mut self, ast: &'alloc FormalParameters<'alloc>) { + self.builder.before_function_parameters(ast); + + self.enter_formal_parameters(ast); + for item in &ast.items { + self.builder.before_parameter(); + self.visit_parameter(item); + } + if let Some(item) = &ast.rest { + self.builder.before_rest_parameter(); + self.visit_binding(item); + } + self.leave_formal_parameters(ast); + + self.builder.after_function_parameters(); + } + + fn enter_enum_method_definition_variant_method(&mut self, ast: &'alloc Method<'alloc>) { + self.builder + .before_method(ast, ast.is_generator, ast.is_async); + // FIXME: Call self.builder.on_function_name + } + + fn leave_enum_method_definition_variant_method(&mut self, ast: &'alloc Method<'alloc>) { + self.builder.after_method(ast); + } + + /// Getter doesn't have FormalParameters. + /// Call builder methods just before body. + fn visit_getter(&mut self, ast: &'alloc Getter<'alloc>) { + self.builder.before_getter(ast); + // FIXME: Call self.builder.on_function_name + + self.enter_getter(ast); + self.visit_class_element_name(&ast.property_name); + + // FIXME: Pass something that points `()` part of getter. + self.builder.on_getter_parameter(ast); + + self.visit_function_body(&ast.body); + self.leave_getter(ast); + + self.builder.after_getter(ast); + } + + /// Setter doesn't have FormalParameters, but single Parameter. + /// Call builder methods around it. + fn visit_setter(&mut self, ast: &'alloc Setter<'alloc>) { + self.builder.before_setter(ast); + // FIXME: Call self.builder.on_function_name + + self.enter_setter(ast); + self.visit_class_element_name(&ast.property_name); + + // FIXME: Pass something that points `(param)` part of setter, + // including `(` and `)`. + self.builder.before_setter_parameter(&ast.param); + self.visit_parameter(&ast.param); + self.builder.after_setter_parameter(); + + self.visit_function_body(&ast.body); + self.leave_setter(ast); + + self.builder.after_setter(ast); + } + + fn leave_binding_with_default(&mut self, _ast: &'alloc BindingWithDefault<'alloc>) { + self.builder.after_initializer(); + } + + fn enter_enum_property_name_variant_computed_property_name( + &mut self, + _ast: &'alloc ComputedPropertyName<'alloc>, + ) { + self.builder.before_computed_property_name(); + } + + fn enter_binding_pattern(&mut self, _ast: &'alloc BindingPattern<'alloc>) { + self.builder.before_binding_pattern(); + } + + fn enter_function_body(&mut self, ast: &'alloc FunctionBody<'alloc>) { + self.builder.before_function_body(ast); + } + + fn leave_function_body(&mut self, _ast: &'alloc FunctionBody<'alloc>) { + self.builder.after_function_body(); + } + + fn enter_enum_expression_variant_arrow_expression( + &mut self, + is_async: &'alloc bool, + params: &'alloc FormalParameters<'alloc>, + _body: &'alloc ArrowExpressionBody<'alloc>, + ) { + self.builder.before_arrow_function(*is_async, params); + } + + fn leave_enum_expression_variant_arrow_expression( + &mut self, + _is_async: &'alloc bool, + _params: &'alloc FormalParameters<'alloc>, + body: &'alloc ArrowExpressionBody<'alloc>, + ) { + self.builder.after_arrow_function(body); + } + + /// Arrow function with expression body. + /// Use the expression as the node for function body. + fn enter_enum_arrow_expression_body_variant_expression( + &mut self, + ast: &'alloc arena::Box<'alloc, Expression<'alloc>>, + ) { + let expr: &Expression = ast; + self.builder.before_function_body(expr); + } + + fn leave_enum_arrow_expression_body_variant_expression( + &mut self, + _ast: &'alloc arena::Box<'alloc, Expression<'alloc>>, + ) { + self.builder.after_function_body(); + } + + fn enter_catch_clause(&mut self, _ast: &'alloc CatchClause<'alloc>) { + self.builder.before_catch_clause(); + } + + fn enter_call_expression(&mut self, ast: &'alloc CallExpression<'alloc>) { + match &ast.callee { + ExpressionOrSuper::Expression(expr) => match &**expr { + Expression::IdentifierExpression(IdentifierExpression { name, .. }) => { + if name.value == CommonSourceAtomSetIndices::eval() { + self.builder.on_direct_eval(); + } + } + _ => {} + }, + _ => {} + } + } + + fn enter_class_declaration(&mut self, _ast: &'alloc ClassDeclaration<'alloc>) { + self.builder.on_class(); + } + + fn enter_class_expression(&mut self, _ast: &'alloc ClassExpression<'alloc>) { + self.builder.on_class(); + } + + fn enter_enum_statement_variant_with_statement( + &mut self, + _object: &'alloc arena::Box<'alloc, Expression<'alloc>>, + _body: &'alloc arena::Box<'alloc, Statement<'alloc>>, + ) { + self.builder.on_with(); + } + + fn visit_enum_unary_operator_variant_delete(&mut self) { + self.builder.on_delete(); + } + + fn enter_enum_statement_variant_for_statement( + &mut self, + init: &'alloc Option<VariableDeclarationOrExpression<'alloc>>, + _test: &'alloc Option<arena::Box<'alloc, Expression<'alloc>>>, + _update: &'alloc Option<arena::Box<'alloc, Expression<'alloc>>>, + _block: &'alloc arena::Box<'alloc, Statement<'alloc>>, + ) { + match init { + Some(VariableDeclarationOrExpression::VariableDeclaration(decl)) => match decl.kind { + VariableDeclarationKind::Let { .. } | VariableDeclarationKind::Const { .. } => { + self.builder.on_lexical_for(); + } + _ => {} + }, + _ => {} + } + } + + fn enter_enum_statement_variant_for_in_statement( + &mut self, + left: &'alloc VariableDeclarationOrAssignmentTarget<'alloc>, + _right: &'alloc arena::Box<'alloc, Expression<'alloc>>, + _block: &'alloc arena::Box<'alloc, Statement<'alloc>>, + ) { + match left { + VariableDeclarationOrAssignmentTarget::VariableDeclaration(decl) => match decl.kind { + VariableDeclarationKind::Let { .. } | VariableDeclarationKind::Const { .. } => { + self.builder.on_lexical_for(); + } + _ => {} + }, + _ => {} + } + } + + fn enter_enum_statement_variant_switch_statement( + &mut self, + _discriminant: &'alloc arena::Box<'alloc, Expression<'alloc>>, + _cases: &'alloc arena::Vec<'alloc, SwitchCase<'alloc>>, + ) { + self.builder.on_switch(); + } + + fn visit_enum_expression_variant_new_target_expression(&mut self) { + self.builder.on_new_target(); + } +} |