Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  scope.rs   Sprache: unbekannt

 
//! Types for the output of scope analysis.
//!
//! The top-level output of this analysis is the `ScopeDataMap`, which holds
//! the following:
//!   * `LexicalScopeData` for each lexial scope in the AST
//!   * `GlobalScopeData` for the global scope
//!   * `VarScopeData` for extra body var scope in function
//!   * `FunctionScopeData` for the function scope
//!
//! Each scope contains a list of bindings (`BindingName`).

use crate::frame_slot::FrameSlot;
use crate::script::ScriptStencilIndex;
use ast::associated_data::AssociatedData;
use ast::source_atom_set::SourceAtomSetIndex;
use ast::source_location_accessor::SourceLocationAccessor;
use ast::type_id::NodeTypeIdAccessor;

/// Corresponds to js::BindingKind in m-c/js/src/vm/Scope.h.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BindingKind {
    Var,
    Let,
    Const,
}

/// Information about a single binding in a JS script.
///
/// Corresponds to `js::BindingName` in m-c/js/src/vm/Scope.h.
#[derive(Debug)]
pub struct BindingName {
    pub name: SourceAtomSetIndex,
    pub is_closed_over: bool,
    pub is_top_level_function: bool,
}

impl BindingName {
    pub fn new(name: SourceAtomSetIndex, is_closed_over: bool) -> Self {
        Self {
            name,
            is_closed_over,
            is_top_level_function: false,
        }
    }

    pub fn new_top_level_function(name: SourceAtomSetIndex, is_closed_over: bool) -> Self {
        Self {
            name,
            is_closed_over,
            is_top_level_function: true,
        }
    }
}

/// Corresponds to the accessor part of `js::BindingIter` in
/// m-c/js/src/vm/Scope.h.
pub struct BindingIterItem<'a> {
    name: &'a BindingName,
    kind: BindingKind,
}

impl<'a> BindingIterItem<'a> {
    fn new(name: &'a BindingName, kind: BindingKind) -> Self {
        Self { name, kind }
    }

    pub fn name(&self) -> SourceAtomSetIndex {
        self.name.name
    }

    pub fn is_top_level_function(&self) -> bool {
        self.name.is_top_level_function
    }

    pub fn is_closed_over(&self) -> bool {
        self.name.is_closed_over
    }

    pub fn kind(&self) -> BindingKind {
        self.kind
    }
}

/// Accessor for both BindingName/Option<BindingName>.
pub trait MaybeBindingName {
    fn is_closed_over(&self) -> bool;
}
impl MaybeBindingName for BindingName {
    fn is_closed_over(&self) -> bool {
        self.is_closed_over
    }
}
impl MaybeBindingName for Option<BindingName> {
    fn is_closed_over(&self) -> bool {
        match self {
            Some(b) => b.is_closed_over,
            None => false,
        }
    }
}

#[derive(Debug)]
pub struct BaseScopeData<BindingItemT>
where
    BindingItemT: MaybeBindingName,
{
    /// Corresponds to `*Scope::Data.{length, trailingNames}.`
    /// The layout is defined by *ScopeData structs below, that has
    /// this struct.
    pub bindings: Vec<BindingItemT>,
    pub has_eval: bool,
    pub has_with: bool,
}

impl<BindingItemT> BaseScopeData<BindingItemT>
where
    BindingItemT: MaybeBindingName,
{
    pub fn new(bindings_count: usize) -> Self {
        Self {
            bindings: Vec::with_capacity(bindings_count),
            has_eval: false,
            has_with: false,
        }
    }

    pub fn is_all_bindings_closed_over(&self) -> bool {
        // `with` and direct `eval` can dynamically access any binding in this
        // scope.
        self.has_eval || self.has_with
    }

    /// Returns true if this scope needs to be allocated on heap as
    /// EnvironmentObject.
    pub fn needs_environment_object(&self) -> bool {
        // `with` and direct `eval` can dynamically access bindings in this
        // scope.
        if self.is_all_bindings_closed_over() {
            return true;
        }

        // If a binding in this scope is closed over by inner function,
        // it can be accessed when this frame isn't on the stack.
        for binding in &self.bindings {
            if binding.is_closed_over() {
                return true;
            }
        }

        false
    }
}

/// Bindings created in global environment.
///
/// Maps to js::GlobalScope::Data in m-c/js/src/vm/Scope.h.
#[derive(Debug)]
pub struct GlobalScopeData {
    /// Bindings are sorted by kind:
    ///
    /// * `base.bindings[0..let_start]` - `var`s
    /// * `base.bindings[let_start..const_start]` - `let`s
    /// * `base.bindings[const_start..]` - `const`s
    pub base: BaseScopeData<BindingName>,

    pub let_start: usize,
    pub const_start: usize,

    /// The global functions in this script.
    pub functions: Vec<ScriptStencilIndex>,
}

impl GlobalScopeData {
    pub fn new(
        var_count: usize,
        let_count: usize,
        const_count: usize,
        functions: Vec<ScriptStencilIndex>,
    ) -> Self {
        let capacity = var_count + let_count + const_count;

        Self {
            base: BaseScopeData::new(capacity),
            let_start: var_count,
            const_start: var_count + let_count,
            functions,
        }
    }

    pub fn iter<'a>(&'a self) -> GlobalBindingIter<'a> {
        GlobalBindingIter::new(self)
    }
}

/// Corresponds to the iteration part of js::BindingIter
/// in m-c/js/src/vm/Scope.h.
pub struct GlobalBindingIter<'a> {
    data: &'a GlobalScopeData,
    i: usize,
}

impl<'a> GlobalBindingIter<'a> {
    fn new(data: &'a GlobalScopeData) -> Self {
        Self { data, i: 0 }
    }
}

impl<'a> Iterator for GlobalBindingIter<'a> {
    type Item = BindingIterItem<'a>;

    fn next(&mut self) -> Option<BindingIterItem<'a>> {
        if self.i == self.data.base.bindings.len() {
            return None;
        }

        let kind = if self.i < self.data.let_start {
            BindingKind::Var
        } else if self.i < self.data.const_start {
            BindingKind::Let
        } else {
            BindingKind::Const
        };

        let name = &self.data.base.bindings[self.i];

        self.i += 1;

        Some(BindingIterItem::new(name, kind))
    }
}

/// Bindings created in var environment.
///
/// Maps to js::VarScope::Data in m-c/js/src/vm/Scope.h,
/// and the parameter for js::frontend::ScopeCreationData::create
/// in m-c/js/src/frontend/Stencil.h
#[derive(Debug)]
pub struct VarScopeData {
    /// All bindings are `var`.
    pub base: BaseScopeData<BindingName>,

    /// The first frame slot of this scope.
    ///
    /// Unlike VarScope::Data, this struct holds the first frame slot,
    /// instead of the next frame slot.
    ///
    /// This is because ScopeCreationData::create receives the first frame slot
    /// and VarScope::Data.nextFrameSlot is calculated there.
    pub first_frame_slot: FrameSlot,

    pub function_has_extensible_scope: bool,

    /// ScopeIndex of the enclosing scope.
    ///
    /// A parameter for ScopeCreationData::create.
    pub enclosing: ScopeIndex,
}

impl VarScopeData {
    pub fn new(
        var_count: usize,
        function_has_extensible_scope: bool,
        enclosing: ScopeIndex,
    ) -> Self {
        let capacity = var_count;

        Self {
            base: BaseScopeData::new(capacity),
            // Set to the correct value in EmitterScopeStack::enter_lexical.
            first_frame_slot: FrameSlot::new(0),
            function_has_extensible_scope,
            enclosing,
        }
    }
}

/// Bindings created in lexical environment.
///
/// Maps to js::LexicalScope::Data in m-c/js/src/vm/Scope.h,
/// and the parameter for js::frontend::ScopeCreationData::create
/// in m-c/js/src/frontend/Stencil.h
#[derive(Debug)]
pub struct LexicalScopeData {
    /// Bindings are sorted by kind:
    ///
    /// * `base.bindings[0..const_start]` - `let`s
    /// * `base.bindings[const_start..]` - `const`s
    pub base: BaseScopeData<BindingName>,

    pub const_start: usize,

    /// The first frame slot of this scope.
    ///
    /// Unlike LexicalScope::Data, this struct holds the first frame slot,
    /// instead of the next frame slot.
    ///
    /// This is because ScopeCreationData::create receives the first frame slot
    /// and LexicalScope::Data.nextFrameSlot is calculated there.
    pub first_frame_slot: FrameSlot,

    /// ScopeIndex of the enclosing scope.
    ///
    /// A parameter for ScopeCreationData::create.
    pub enclosing: ScopeIndex,

    /// Functions in this scope.
    pub functions: Vec<ScriptStencilIndex>,
}

impl LexicalScopeData {
    fn new(
        let_count: usize,
        const_count: usize,
        enclosing: ScopeIndex,
        functions: Vec<ScriptStencilIndex>,
    ) -> Self {
        let capacity = let_count + const_count;

        Self {
            base: BaseScopeData::new(capacity),
            const_start: let_count,
            // Set to the correct value in EmitterScopeStack::enter_lexical.
            first_frame_slot: FrameSlot::new(0),
            enclosing,
            functions,
        }
    }

    pub fn new_block(
        let_count: usize,
        const_count: usize,
        enclosing: ScopeIndex,
        functions: Vec<ScriptStencilIndex>,
    ) -> Self {
        Self::new(let_count, const_count, enclosing, functions)
    }

    pub fn new_named_lambda(enclosing: ScopeIndex) -> Self {
        Self::new(0, 1, enclosing, Vec::new())
    }

    pub fn new_function_lexical(
        let_count: usize,
        const_count: usize,
        enclosing: ScopeIndex,
    ) -> Self {
        Self::new(let_count, const_count, enclosing, Vec::new())
    }

    pub fn iter<'a>(&'a self) -> LexicalBindingIter<'a> {
        LexicalBindingIter::new(self)
    }
}

/// Corresponds to the iteration part of js::BindingIter
/// in m-c/js/src/vm/Scope.h.
pub struct LexicalBindingIter<'a> {
    data: &'a LexicalScopeData,
    i: usize,
}

impl<'a> LexicalBindingIter<'a> {
    fn new(data: &'a LexicalScopeData) -> Self {
        Self { data, i: 0 }
    }
}

impl<'a> Iterator for LexicalBindingIter<'a> {
    type Item = BindingIterItem<'a>;

    fn next(&mut self) -> Option<BindingIterItem<'a>> {
        if self.i == self.data.base.bindings.len() {
            return None;
        }

        let kind = if self.i < self.data.const_start {
            BindingKind::Let
        } else {
            BindingKind::Const
        };

        let name = &self.data.base.bindings[self.i];

        self.i += 1;

        Some(BindingIterItem::new(name, kind))
    }
}

/// Bindings created in function environment.
///
/// Maps to js::FunctionScope::Data in m-c/js/src/vm/Scope.h,
/// and the parameter for js::frontend::ScopeCreationData::create
/// in m-c/js/src/frontend/Stencil.h
#[derive(Debug)]
pub struct FunctionScopeData {
    /// Bindings are sorted by kind:
    ///
    /// * `base.bindings[0..non_positional_formal_start]` -
    ///    positional foparameters:
    ///      - single binding parameter with/without default
    ///      - single binding rest parameter
    /// * `base.bindings[non_positional_formal_start..var_start]` -
    ///    non positional parameters:
    ///      - destructuring parameter
    ///      - destructuring rest parameter
    /// * `base.bindings[var_start..]` - `var`s
    ///
    /// Given positional parameters range can have null slot for destructuring,
    /// use Vec of Option<BindingName>, instead of BindingName like others.
    pub base: BaseScopeData<Option<BindingName>>,

    pub has_parameter_exprs: bool,

    pub non_positional_formal_start: usize,
    pub var_start: usize,

    /// The first frame slot of this scope.
    ///
    /// Unlike FunctionScope::Data, this struct holds the first frame slot,
    /// instead of the next frame slot.
    ///
    /// This is because ScopeCreationData::create receives the first frame slot
    /// and FunctionScope::Data.nextFrameSlot is calculated there.
    pub first_frame_slot: FrameSlot,

    /// ScopeIndex of the enclosing scope.
    ///
    /// A parameter for ScopeCreationData::create.
    pub enclosing: ScopeIndex,

    pub function_index: ScriptStencilIndex,

    /// True if the function is an arrow function.
    pub is_arrow: bool,
}

impl FunctionScopeData {
    pub fn new(
        has_parameter_exprs: bool,
        positional_parameter_count: usize,
        non_positional_formal_start: usize,
        max_var_count: usize,
        enclosing: ScopeIndex,
        function_index: ScriptStencilIndex,
        is_arrow: bool,
    ) -> Self {
        let capacity = positional_parameter_count + non_positional_formal_start + max_var_count;

        Self {
            base: BaseScopeData::new(capacity),
            has_parameter_exprs,
            non_positional_formal_start: positional_parameter_count,
            var_start: positional_parameter_count + non_positional_formal_start,
            // Set to the correct value in EmitterScopeStack::enter_function.
            first_frame_slot: FrameSlot::new(0),
            enclosing,
            function_index,
            is_arrow,
        }
    }
}

#[derive(Debug)]
pub enum ScopeData {
    /// No scope should be generated, but this scope becomes an alias to
    /// enclosing scope. This is used, for example, when we see a function,
    /// and set aside a ScopeData for its lexical bindings, but upon
    /// reaching the end of the function body, we find that there were no
    /// lexical bindings and the spec actually says not to generate a Lexical
    /// Environment when this function is called.
    ///
    /// In other places, the spec does say to create a Lexical Environment, but
    /// it turns out it doesn't have any bindings in it and we can optimize it
    /// away.
    ///
    /// NOTE: Alias can be chained.
    Alias(ScopeIndex),

    Global(GlobalScopeData),
    Var(VarScopeData),
    Lexical(LexicalScopeData),
    Function(FunctionScopeData),
}

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

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

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

/// A vector of scopes, incrementally populated during analysis.
/// The goal is to build a `Vec<ScopeData>`.
#[derive(Debug)]
pub struct ScopeDataList {
    /// Uses Option to allow `allocate()` and `populate()` to be called
    /// separately.
    scopes: Vec<Option<ScopeData>>,
}

impl ScopeDataList {
    pub fn new() -> Self {
        Self { scopes: Vec::new() }
    }

    pub fn push(&mut self, scope: ScopeData) -> ScopeIndex {
        let index = self.scopes.len();
        self.scopes.push(Some(scope));
        ScopeIndex::new(index)
    }

    pub fn allocate(&mut self) -> ScopeIndex {
        let index = self.scopes.len();
        self.scopes.push(None);
        ScopeIndex::new(index)
    }

    pub fn populate(&mut self, index: ScopeIndex, scope: ScopeData) {
        self.scopes[usize::from(index)].replace(scope);
    }

    fn get(&self, index: ScopeIndex) -> &ScopeData {
        self.scopes[usize::from(index)]
            .as_ref()
            .expect("Should be populated")
    }

    pub fn get_mut(&mut self, index: ScopeIndex) -> &mut ScopeData {
        self.scopes[usize::from(index)]
            .as_mut()
            .expect("Should be populated")
    }
}

impl From<ScopeDataList> for Vec<ScopeData> {
    fn from(list: ScopeDataList) -> Vec<ScopeData> {
        list.scopes
            .into_iter()
            .map(|g| g.expect("Should be populated"))
            .collect()
    }
}

/// The collection of all scope data associated with bindings and scopes in the
/// AST.
#[derive(Debug)]
pub struct ScopeDataMap {
    scopes: ScopeDataList,
    global: ScopeIndex,

    /// Associates every AST node that's a scope with an index into `scopes`.
    non_global: AssociatedData<ScopeIndex>,
}

impl ScopeDataMap {
    pub fn new(
        scopes: ScopeDataList,
        global: ScopeIndex,
        non_global: AssociatedData<ScopeIndex>,
    ) -> Self {
        Self {
            scopes,
            global,
            non_global,
        }
    }

    pub fn get_global_index(&self) -> ScopeIndex {
        self.global
    }

    pub fn get_global_at(&self, index: ScopeIndex) -> &GlobalScopeData {
        match self.scopes.get(index) {
            ScopeData::Global(scope) => scope,
            _ => panic!("Unexpected scope data for global"),
        }
    }

    pub fn get_index<NodeT>(&self, node: &NodeT) -> ScopeIndex
    where
        NodeT: SourceLocationAccessor + NodeTypeIdAccessor,
    {
        self.non_global
            .get(node)
            .expect("There should be a scope data associated")
            .clone()
    }

    pub fn get_lexical_at(&self, index: ScopeIndex) -> &LexicalScopeData {
        match self.scopes.get(index) {
            ScopeData::Lexical(scope) => scope,
            _ => panic!("Unexpected scope data for lexical"),
        }
    }

    pub fn get_lexical_at_mut(&mut self, index: ScopeIndex) -> &mut LexicalScopeData {
        match self.scopes.get_mut(index) {
            ScopeData::Lexical(scope) => scope,
            _ => panic!("Unexpected scope data for lexical"),
        }
    }

    pub fn get_function_at_mut(&mut self, index: ScopeIndex) -> &mut FunctionScopeData {
        match self.scopes.get_mut(index) {
            ScopeData::Function(scope) => scope,
            _ => panic!("Unexpected scope data for function"),
        }
    }

    pub fn is_alias(&mut self, index: ScopeIndex) -> bool {
        match self.scopes.get(index) {
            ScopeData::Alias(_) => true,
            _ => false,
        }
    }
}

impl From<ScopeDataMap> for Vec<ScopeData> {
    fn from(map: ScopeDataMap) -> Vec<ScopeData> {
        map.scopes.into()
    }
}

[ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge