Skip to content

Commit

Permalink
Implement scope analysis and local variables (#3988)
Browse files Browse the repository at this point in the history
* Remove unnecessary compile time environment clones

* Remove compile time environments from every runtime DeclarativeEnvironment

* Implement scope analysis and local variables

* fix docs and fuzzer errors

* Apply suggestions

* Align `parse_script` and `parse_module` arguments

* Fix fuzzer
  • Loading branch information
raskad authored Sep 22, 2024
1 parent 8438ad2 commit aa09d98
Show file tree
Hide file tree
Showing 89 changed files with 5,004 additions and 2,053 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,9 @@ where
let mut parser = boa_parser::Parser::new(Source::from_bytes(src));
let dump =
if args.module {
let scope = context.realm().scope().clone();
let module = parser
.parse_module(context.interner_mut())
.parse_module(&scope, context.interner_mut())
.map_err(|e| format!("Uncaught SyntaxError: {e}"))?;

match arg {
Expand All @@ -202,8 +203,9 @@ where
DumpFormat::Debug => format!("{module:#?}"),
}
} else {
let scope = context.realm().scope().clone();
let mut script = parser
.parse_script(context.interner_mut())
.parse_script(&scope, context.interner_mut())
.map_err(|e| format!("Uncaught SyntaxError: {e}"))?;

if args.optimize {
Expand Down
2 changes: 2 additions & 0 deletions core/ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ repository.workspace = true
rust-version.workspace = true

[features]
annex-b = []
serde = ["dep:serde", "boa_interner/serde", "bitflags/serde", "num-bigint/serde"]
arbitrary = ["dep:arbitrary", "boa_interner/arbitrary", "num-bigint/arbitrary"]

[dependencies]
boa_interner.workspace = true
boa_macros.workspace = true
boa_string.workspace = true
rustc-hash = { workspace = true, features = ["std"] }
bitflags.workspace = true
num-bigint.workspace = true
Expand Down
31 changes: 24 additions & 7 deletions core/ast/src/expression/literal/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ use crate::{
},
function::{FormalParameterList, FunctionBody},
join_nodes,
operations::{contains, ContainsSymbol},
pattern::{ObjectPattern, ObjectPatternElement},
property::{MethodDefinitionKind, PropertyName},
scope::FunctionScopes,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
Expand Down Expand Up @@ -401,27 +403,35 @@ impl VisitWith for PropertyDefinition {
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectMethodDefinition {
name: PropertyName,
parameters: FormalParameterList,
body: FunctionBody,
pub(crate) name: PropertyName,
pub(crate) parameters: FormalParameterList,
pub(crate) body: FunctionBody,
pub(crate) contains_direct_eval: bool,
kind: MethodDefinitionKind,

#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) scopes: FunctionScopes,
}

impl ObjectMethodDefinition {
/// Creates a new object method definition.
#[inline]
#[must_use]
pub const fn new(
pub fn new(
name: PropertyName,
parameters: FormalParameterList,
body: FunctionBody,
kind: MethodDefinitionKind,
) -> Self {
let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
|| contains(&body, ContainsSymbol::DirectEval);
Self {
name,
parameters,
body,
contains_direct_eval,
kind,
scopes: FunctionScopes::default(),
}
}

Expand Down Expand Up @@ -452,6 +462,13 @@ impl ObjectMethodDefinition {
pub const fn kind(&self) -> MethodDefinitionKind {
self.kind
}

/// Gets the scopes of the object method definition.
#[inline]
#[must_use]
pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes
}
}

impl ToIndentedString for ObjectMethodDefinition {
Expand All @@ -467,7 +484,7 @@ impl ToIndentedString for ObjectMethodDefinition {
};
let name = self.name.to_interned_string(interner);
let parameters = join_nodes(interner, self.parameters.as_ref());
let body = block_to_string(self.body.statements(), interner, indent_n + 1);
let body = block_to_string(&self.body.statements, interner, indent_n + 1);
format!("{indentation}{prefix}{name}({parameters}) {body},\n")
}
}
Expand All @@ -479,7 +496,7 @@ impl VisitWith for ObjectMethodDefinition {
{
try_break!(visitor.visit_property_name(&self.name));
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
visitor.visit_function_body(&self.body)
}

fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
Expand All @@ -488,6 +505,6 @@ impl VisitWith for ObjectMethodDefinition {
{
try_break!(visitor.visit_property_name_mut(&mut self.name));
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
visitor.visit_function_body_mut(&mut self.body)
}
}
35 changes: 26 additions & 9 deletions core/ast/src/function/arrow_function.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::operations::{contains, ContainsSymbol};
use crate::scope::FunctionScopes;
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
Expand All @@ -23,34 +25,42 @@ use super::{FormalParameterList, FunctionBody};
#[derive(Clone, Debug, PartialEq)]
pub struct ArrowFunction {
pub(crate) name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
pub(crate) parameters: FormalParameterList,
pub(crate) body: FunctionBody,
pub(crate) contains_direct_eval: bool,

#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) scopes: FunctionScopes,
}

impl ArrowFunction {
/// Creates a new `ArrowFunctionDecl` AST Expression.
#[inline]
#[must_use]
pub const fn new(
pub fn new(
name: Option<Identifier>,
params: FormalParameterList,
parameters: FormalParameterList,
body: FunctionBody,
) -> Self {
let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
|| contains(&body, ContainsSymbol::DirectEval);
Self {
name,
parameters: params,
parameters,
body,
contains_direct_eval,
scopes: FunctionScopes::default(),
}
}

/// Gets the name of the function declaration.
/// Gets the name of the arrow function.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
self.name
}

/// Sets the name of the function declaration.
/// Sets the name of the arrow function.
#[inline]
pub fn set_name(&mut self, name: Option<Identifier>) {
self.name = name;
Expand All @@ -69,6 +79,13 @@ impl ArrowFunction {
pub const fn body(&self) -> &FunctionBody {
&self.body
}

/// Returns the scopes of the arrow function.
#[inline]
#[must_use]
pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes
}
}

impl ToIndentedString for ArrowFunction {
Expand Down Expand Up @@ -102,7 +119,7 @@ impl VisitWith for ArrowFunction {
try_break!(visitor.visit_identifier(ident));
}
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
visitor.visit_function_body(&self.body)
}

fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
Expand All @@ -113,6 +130,6 @@ impl VisitWith for ArrowFunction {
try_break!(visitor.visit_identifier_mut(ident));
}
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
visitor.visit_function_body_mut(&mut self.body)
}
}
35 changes: 26 additions & 9 deletions core/ast/src/function/async_arrow_function.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::ops::ControlFlow;

use super::{FormalParameterList, FunctionBody};
use crate::operations::{contains, ContainsSymbol};
use crate::scope::FunctionScopes;
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
Expand All @@ -23,52 +25,67 @@ use boa_interner::{Interner, ToIndentedString};
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncArrowFunction {
pub(crate) name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
pub(crate) parameters: FormalParameterList,
pub(crate) body: FunctionBody,
pub(crate) contains_direct_eval: bool,

#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) scopes: FunctionScopes,
}

impl AsyncArrowFunction {
/// Creates a new `AsyncArrowFunction` AST Expression.
#[inline]
#[must_use]
pub const fn new(
pub fn new(
name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
) -> Self {
let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
|| contains(&body, ContainsSymbol::DirectEval);
Self {
name,
parameters,
body,
contains_direct_eval,
scopes: FunctionScopes::default(),
}
}

/// Gets the name of the function declaration.
/// Gets the name of the async arrow function.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
self.name
}

/// Sets the name of the function declaration.
/// Sets the name of the async arrow function.
#[inline]
pub fn set_name(&mut self, name: Option<Identifier>) {
self.name = name;
}

/// Gets the list of parameters of the arrow function.
/// Gets the list of parameters of the async arrow function.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}

/// Gets the body of the arrow function.
/// Gets the body of the async arrow function.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}

/// Returns the scopes of the async arrow function.
#[inline]
#[must_use]
pub const fn scopes(&self) -> &FunctionScopes {
&self.scopes
}
}

impl ToIndentedString for AsyncArrowFunction {
Expand Down Expand Up @@ -102,7 +119,7 @@ impl VisitWith for AsyncArrowFunction {
try_break!(visitor.visit_identifier(ident));
}
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
visitor.visit_function_body(&self.body)
}

fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
Expand All @@ -113,6 +130,6 @@ impl VisitWith for AsyncArrowFunction {
try_break!(visitor.visit_identifier_mut(ident));
}
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
visitor.visit_function_body_mut(&mut self.body)
}
}
Loading

0 comments on commit aa09d98

Please sign in to comment.