Skip to content

Commit

Permalink
Rewrite scope analysis operations using visitors (#2408)
Browse files Browse the repository at this point in the history
This PR rewrites all syntax-directed operations that find declared names and variables using visitors.

Hopefully, this should be the last step before finally being able to separate the parser from the engine.

I checked the failing [tests](https://github.com/tc39/test262/blob/85373b4ce12a908f8fc517093d95cf2ed2f5ee6a/test/language/statements/for-await-of/async-gen-decl-dstr-obj-prop-elem-target-yield-expr.js#L49) and they're apparently false positives, since they return `Promise { <rejected> ReferenceError: x is not initialized }` on the main branch.
  • Loading branch information
jedel1043 committed Nov 5, 2022
1 parent aad7815 commit 8e14d76
Show file tree
Hide file tree
Showing 36 changed files with 1,084 additions and 917 deletions.
71 changes: 1 addition & 70 deletions boa_ast/src/declaration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
//! [class]: https://tc39.es/ecma262/#prod-ClassDeclaration
//! [diff]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#difference_between_statements_and_declarations
use super::{
expression::Identifier,
function::{AsyncFunction, AsyncGenerator, Class, Function, Generator},
};
use super::function::{AsyncFunction, AsyncGenerator, Class, Function, Generator};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;

Expand Down Expand Up @@ -51,72 +48,6 @@ pub enum Declaration {
Lexical(LexicalDeclaration),
}

impl Declaration {
/// Return the lexically declared names of a `Declaration`.
///
/// The returned list may contain duplicates.
///
/// If a declared name originates from a function declaration it is flagged as `true` in the returned list.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames
pub(crate) fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> {
match self {
Declaration::Function(f) => {
if let Some(name) = f.name() {
vec![(name, true)]
} else {
Vec::new()
}
}
Declaration::Generator(g) => {
if let Some(name) = g.name() {
vec![(name, false)]
} else {
Vec::new()
}
}
Declaration::AsyncFunction(af) => {
if let Some(name) = af.name() {
vec![(name, false)]
} else {
Vec::new()
}
}
Declaration::AsyncGenerator(ag) => {
if let Some(name) = ag.name() {
vec![(name, false)]
} else {
Vec::new()
}
}
Declaration::Class(cl) => {
if let Some(name) = cl.name() {
vec![(name, false)]
} else {
Vec::new()
}
}
Declaration::Lexical(lexical) => {
let mut names = Vec::new();
for decl in lexical.variable_list().as_ref() {
match decl.binding() {
Binding::Identifier(ident) => {
names.push((*ident, false));
}
Binding::Pattern(pattern) => {
names.extend(pattern.idents().into_iter().map(|name| (name, false)));
}
}
}
names
}
}
}
}

impl ToIndentedString for Declaration {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
match self {
Expand Down
16 changes: 0 additions & 16 deletions boa_ast/src/declaration/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,12 +299,6 @@ impl Variable {
pub fn init(&self) -> Option<&Expression> {
self.init.as_ref()
}

/// Gets the list of declared identifiers.
#[must_use]
pub fn idents(&self) -> Vec<Identifier> {
self.binding.idents()
}
}

impl VisitWith for Variable {
Expand Down Expand Up @@ -358,16 +352,6 @@ impl From<Pattern> for Binding {
}
}

impl Binding {
/// Gets the list of declared identifiers.
pub(crate) fn idents(&self) -> Vec<Identifier> {
match self {
Binding::Identifier(id) => vec![*id],
Binding::Pattern(ref pat) => pat.idents(),
}
}
}

impl ToInternedString for Binding {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
Expand Down
2 changes: 1 addition & 1 deletion boa_ast/src/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub enum Expression {
/// A FormalParameterList.
///
/// This is only used in the parser itself.
/// It is not a valid AST node.
/// It is not a valid expression node.
#[doc(hidden)]
FormalParameterList(FormalParameterList),
}
Expand Down
21 changes: 4 additions & 17 deletions boa_ast/src/function/parameters.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
declaration::{Binding, Variable},
expression::{Expression, Identifier},
pattern::Pattern,
expression::Expression,
operations::bound_names,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
Expand Down Expand Up @@ -40,7 +40,7 @@ impl FormalParameterList {
let mut names = FxHashSet::default();

for parameter in &parameters {
let parameter_names = parameter.names();
let parameter_names = bound_names(parameter);

for name in parameter_names {
if name == Sym::ARGUMENTS {
Expand Down Expand Up @@ -224,19 +224,6 @@ impl FormalParameter {
}
}

/// Gets the name of the formal parameter.
#[must_use]
pub fn names(&self) -> Vec<Identifier> {
match self.variable.binding() {
Binding::Identifier(ident) => vec![*ident],
Binding::Pattern(pattern) => match pattern {
Pattern::Object(object_pattern) => object_pattern.idents(),

Pattern::Array(array_pattern) => array_pattern.idents(),
},
}
}

/// Gets the variable of the formal parameter
#[must_use]
pub fn variable(&self) -> &Variable {
Expand All @@ -255,7 +242,7 @@ impl FormalParameter {
self.is_rest_param
}

/// Returns `true` if the parameter is a simple [`Identifier`].
/// Returns `true` if the parameter is an identifier.
#[must_use]
pub fn is_identifier(&self) -> bool {
matches!(&self.variable.binding(), Binding::Identifier(_))
Expand Down
Loading

0 comments on commit 8e14d76

Please sign in to comment.