Source code

Revision control

Copy as Markdown

Other Tools

//! 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.
//!
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
///
/// 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
///
/// 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
///
/// FunctionStatementList : StatementList
///
/// 1. Return the TopLevelVarScopedDeclarations of StatementList.
/// Static Semantics: VarScopedDeclarations
///
/// ScriptBody : StatementList
///
/// 1. Return TopLevelVarScopedDeclarations of StatementList.
/// 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
///
/// 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
///
/// LabelledItem : FunctionDeclaration
///
/// 1. Return a new List containing FunctionDeclaration.
/* FunctionDeclaration */
/// Annex B 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
///
/// StatementListItem : Declaration
///
/// 1. Return a new List containing DeclarationPart of Declaration.
/// 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
///
/// LabelledItem : FunctionDeclaration
///
/// 1. Return a new List containing FunctionDeclaration.
/* FunctionDeclaration */
/// Static Semantics: LexicallyScopedDeclarations
///
/// FunctionStatementList : StatementList
///
/// 1. Return the TopLevelLexicallyScopedDeclarations of StatementList.
/// Static Semantics: LexicallyScopedDeclarations
///
/// ScriptBody : StatementList
///
/// 1. Return TopLevelLexicallyScopedDeclarations of StatementList.
/// Static Semantics: TopLevelLexicallyScopedDeclarations
///
/// StatementListItem : Declaration
///
/// 1. If Declaration is Declaration : HoistableDeclaration, then
/// a. Return « ».
/// 2. Return a new List containing Declaration.
/// 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 )
///
/// 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 )
//
// 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 )
//
// 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 )
//
// 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 )
//
// 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 )
//
// 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
//
// 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?
// 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
// 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 )
//
// 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 )
///
/// Step 3. Let declarations be the LexicallyScopedDeclarations of code.
let_names: Vec<SourceAtomSetIndex>,
const_names: Vec<SourceAtomSetIndex>,
/// Runtime Semantics: BlockDeclarationInstantiation ( code, env )
///
/// 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 )
//
// 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 )
//
// 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 )
//
// 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 )
//
// 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
//
// 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.
///
/// 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 )
///
/// 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
//
// 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
// 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
//
// 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
//
// FormalParameters : FunctionRestParameter
//
// 1. Return false.
//
// FormalParameters : FormalParameterList , FunctionRestParameter
//
// 1. Return false.
self.simple_parameter_list = false;
}
fn after_initializer(&mut self) {
// Static Semantics: IsSimpleParameterList
//
// BindingElement : BindingPattern Initializer
//
// 1. Return false.
//
// SingleNameBinding : BindingIdentifier Initializer
//
// 1. Return false.
self.simple_parameter_list = false;
// FunctionDeclarationInstantiation ( func, argumentsList )
//
// Step 8. Let hasParameterExpressions be ContainsExpression of formals.
// 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 )
//
// Step 8. Let hasParameterExpressions be ContainsExpression of formals.
// 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 )
///
/// 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 )
//
// 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 )
//
// 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 )
//
// 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 )
///
/// 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 )
//
// 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 )
//
// 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 )
//
// 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 )
//
// 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 )
//
// 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 )
///
/// 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 )
//
// 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,