//! 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
usecrate::data::FunctionDeclarationPropertyMap; usecrate::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: 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.
/// 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.
/// 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,
/// 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,
fn mark_annex_b(&self, function_declaration_properties: &style='color:red'>mut FunctionDeclarationPropertyMap) { for functions in &mutself.functions.values() { for fun in functions {
function_declaration_properties.mark_annex_b(fun.script_index);
}
}
}
fn propagate_common(&mutself, 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;
}
/// 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>,
fn declare_var(&mutself, 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(&mutself, 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(&mutself, 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(&mutself, 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(&mutself) { // 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(
&mutself,
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( mutself,
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();
// 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))
}
/// 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,
}
fn declare_let(&mutself, 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(&mutself, 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(&mutself, 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,
});
// 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,
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. letmut 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));
/// 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,
}
/// 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,
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,
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_computed_property_name(&mutself) { // 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(&mutself, name: SourceAtomSetIndex) { // Step 17. Else if "arguments" is an element of parameterNames, // then if name == CommonSourceAtomSetIndices::arguments() { self.parameter_has_arguments = true;
}
fn is_parameter_closed_over(&self) -> bool { for name in &self.parameter_names { ifself.base.name_tracker.is_closed_over_def(name) { returntrue;
}
}
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>,
// Step 6. If parameterNames has any duplicate entries, let // hasDuplicates be true. Otherwise, let hasDuplicates be // false. ifself.shared.parameter_names.contains(&name) { self.has_duplicates = true;
}
// Step 16. If func.[[ThisMode]] is lexical, then ifself.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 elseifself.shared.parameter_has_arguments { // Step 17.a. Set argumentsObjectNeeded to false.
arguments_object_needed = false;
} // Step 18. Else if hasParameterExpressions is false, then elseif !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 ifself.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()
};
// 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. ifself.shared.parameter_names.contains(n)
|| (arguments_object_needed && *n == CommonSourceAtomSetIndices::arguments())
{ continue;
}
// 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. letmut 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.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
{ letmut 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))
}
/// Variables declared/used in FunctionBody. /// Shared part between full-parse and syntax-only parse. #[derive(Debug)] struct SharedFunctionBodyScopeBuilder {
base: BaseScopeBuilder,
/// 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,
}
fn declare_var(&mutself, 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(&mutself, 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(&mutself, 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(&mutself, 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(&mutself, 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 { ifself.base.name_tracker.is_closed_over_def(name) { returntrue;
}
}
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 14.a.iii.3. Insert d as the first element of // functionsToInitialize. self.functions_to_initialize.push(fun_index);
}
}
/// 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>>>,
}
fn innermost_var<'a>(&'a mutself) -> &'a mut ScopeBuilder { for builder inself.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 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 handle_popped_function_expression(&mutself, builder: &BaseScopeBuilder) { iflet 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);
}
fn update_closed_over_bindings_for_lazy(&mutself, builder: &BaseScopeBuilder) { matchself.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(
&mutself,
builder1: &BaseScopeBuilder,
builder2: &BaseScopeBuilder,
) { matchself.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(&mutself) -> 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)] pubstruct 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>,
}
/// Enter a function. /// /// This creates `ScriptStencil` for the function, and adds it to /// enclosing function if exists. fn enter<T>(
&mutself,
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);
/// Leave a function, setting its source location. fn leave<T>(&mutself, fun: &T) where
T: SourceLocationAccessor + NodeTypeIdAccessor,
{ let loc = fun.get_loc(); let source_end = loc.end;
/// 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 mutself) -> 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 mutself) -> &'a mut ScriptStencil { self.maybe_current_mut().expect("should be inside function")
}
/// Sets the name of the current function. fn set_function_name(&mutself, 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>(&mutself, 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(&mutself) { let fun = self.current_mut();
fun.add_fun_nargs();
}
/// Flags that the current function has rest parameter. fn on_rest_parameter(&mutself) { let fun = self.current_mut();
fun.add_fun_nargs();
fun.set_has_rest();
}
fn add_closed_over_bindings(
&mutself, 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)] pubenum 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)] pubstruct 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>,
// 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>,
}
// 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)
// 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);
// Step 2. Let blockEnv be NewDeclarativeEnvironment(oldEnv). let index = self.scopes.allocate(); let builder = BlockScopeBuilder::new(index); self.non_global.insert(block, index);
pubfn before_getter<T>(&mutself, fun: &T) where
T: SourceLocationAccessor + NodeTypeIdAccessor,
{ // FIXME: Support PropertyName as function name. self.set_error(ScopeBuildError::NotImplemented("Getter (name calculation)"));
pubfn before_setter<T>(&mutself, fun: &T) where
T: SourceLocationAccessor + NodeTypeIdAccessor,
{ // FIXME: Support PropertyName as function name. self.set_error(ScopeBuildError::NotImplemented("Setter (name calculation)"));
ifself.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();
}
pubfn before_function_body<T>(&mutself, body: &T) where
T: SourceLocationAccessor + NodeTypeIdAccessor,
{ ifself.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(&mutself) { 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"),
);
}
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 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;
} elseif !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",
));
}
}
}
}
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);
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,
}
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.92Bemerkung:
(vorverarbeitet am 2026-06-23)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.