Skip to content

Commit aa09d98

Browse files
authored
Implement scope analysis and local variables (#3988)
* 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
1 parent 8438ad2 commit aa09d98

File tree

89 files changed

+5004
-2053
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+5004
-2053
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/src/main.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,9 @@ where
190190
let mut parser = boa_parser::Parser::new(Source::from_bytes(src));
191191
let dump =
192192
if args.module {
193+
let scope = context.realm().scope().clone();
193194
let module = parser
194-
.parse_module(context.interner_mut())
195+
.parse_module(&scope, context.interner_mut())
195196
.map_err(|e| format!("Uncaught SyntaxError: {e}"))?;
196197

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

209211
if args.optimize {

core/ast/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ repository.workspace = true
1111
rust-version.workspace = true
1212

1313
[features]
14+
annex-b = []
1415
serde = ["dep:serde", "boa_interner/serde", "bitflags/serde", "num-bigint/serde"]
1516
arbitrary = ["dep:arbitrary", "boa_interner/arbitrary", "num-bigint/arbitrary"]
1617

1718
[dependencies]
1819
boa_interner.workspace = true
1920
boa_macros.workspace = true
21+
boa_string.workspace = true
2022
rustc-hash = { workspace = true, features = ["std"] }
2123
bitflags.workspace = true
2224
num-bigint.workspace = true

core/ast/src/expression/literal/object.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ use crate::{
88
},
99
function::{FormalParameterList, FunctionBody},
1010
join_nodes,
11+
operations::{contains, ContainsSymbol},
1112
pattern::{ObjectPattern, ObjectPatternElement},
1213
property::{MethodDefinitionKind, PropertyName},
14+
scope::FunctionScopes,
1315
try_break,
1416
visitor::{VisitWith, Visitor, VisitorMut},
1517
};
@@ -401,27 +403,35 @@ impl VisitWith for PropertyDefinition {
401403
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
402404
#[derive(Clone, Debug, PartialEq)]
403405
pub struct ObjectMethodDefinition {
404-
name: PropertyName,
405-
parameters: FormalParameterList,
406-
body: FunctionBody,
406+
pub(crate) name: PropertyName,
407+
pub(crate) parameters: FormalParameterList,
408+
pub(crate) body: FunctionBody,
409+
pub(crate) contains_direct_eval: bool,
407410
kind: MethodDefinitionKind,
411+
412+
#[cfg_attr(feature = "serde", serde(skip))]
413+
pub(crate) scopes: FunctionScopes,
408414
}
409415

410416
impl ObjectMethodDefinition {
411417
/// Creates a new object method definition.
412418
#[inline]
413419
#[must_use]
414-
pub const fn new(
420+
pub fn new(
415421
name: PropertyName,
416422
parameters: FormalParameterList,
417423
body: FunctionBody,
418424
kind: MethodDefinitionKind,
419425
) -> Self {
426+
let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
427+
|| contains(&body, ContainsSymbol::DirectEval);
420428
Self {
421429
name,
422430
parameters,
423431
body,
432+
contains_direct_eval,
424433
kind,
434+
scopes: FunctionScopes::default(),
425435
}
426436
}
427437

@@ -452,6 +462,13 @@ impl ObjectMethodDefinition {
452462
pub const fn kind(&self) -> MethodDefinitionKind {
453463
self.kind
454464
}
465+
466+
/// Gets the scopes of the object method definition.
467+
#[inline]
468+
#[must_use]
469+
pub const fn scopes(&self) -> &FunctionScopes {
470+
&self.scopes
471+
}
455472
}
456473

457474
impl ToIndentedString for ObjectMethodDefinition {
@@ -467,7 +484,7 @@ impl ToIndentedString for ObjectMethodDefinition {
467484
};
468485
let name = self.name.to_interned_string(interner);
469486
let parameters = join_nodes(interner, self.parameters.as_ref());
470-
let body = block_to_string(self.body.statements(), interner, indent_n + 1);
487+
let body = block_to_string(&self.body.statements, interner, indent_n + 1);
471488
format!("{indentation}{prefix}{name}({parameters}) {body},\n")
472489
}
473490
}
@@ -479,7 +496,7 @@ impl VisitWith for ObjectMethodDefinition {
479496
{
480497
try_break!(visitor.visit_property_name(&self.name));
481498
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
482-
visitor.visit_script(&self.body)
499+
visitor.visit_function_body(&self.body)
483500
}
484501

485502
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
@@ -488,6 +505,6 @@ impl VisitWith for ObjectMethodDefinition {
488505
{
489506
try_break!(visitor.visit_property_name_mut(&mut self.name));
490507
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
491-
visitor.visit_script_mut(&mut self.body)
508+
visitor.visit_function_body_mut(&mut self.body)
492509
}
493510
}

core/ast/src/function/arrow_function.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::operations::{contains, ContainsSymbol};
2+
use crate::scope::FunctionScopes;
13
use crate::try_break;
24
use crate::visitor::{VisitWith, Visitor, VisitorMut};
35
use crate::{
@@ -23,34 +25,42 @@ use super::{FormalParameterList, FunctionBody};
2325
#[derive(Clone, Debug, PartialEq)]
2426
pub struct ArrowFunction {
2527
pub(crate) name: Option<Identifier>,
26-
parameters: FormalParameterList,
27-
body: FunctionBody,
28+
pub(crate) parameters: FormalParameterList,
29+
pub(crate) body: FunctionBody,
30+
pub(crate) contains_direct_eval: bool,
31+
32+
#[cfg_attr(feature = "serde", serde(skip))]
33+
pub(crate) scopes: FunctionScopes,
2834
}
2935

3036
impl ArrowFunction {
3137
/// Creates a new `ArrowFunctionDecl` AST Expression.
3238
#[inline]
3339
#[must_use]
34-
pub const fn new(
40+
pub fn new(
3541
name: Option<Identifier>,
36-
params: FormalParameterList,
42+
parameters: FormalParameterList,
3743
body: FunctionBody,
3844
) -> Self {
45+
let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
46+
|| contains(&body, ContainsSymbol::DirectEval);
3947
Self {
4048
name,
41-
parameters: params,
49+
parameters,
4250
body,
51+
contains_direct_eval,
52+
scopes: FunctionScopes::default(),
4353
}
4454
}
4555

46-
/// Gets the name of the function declaration.
56+
/// Gets the name of the arrow function.
4757
#[inline]
4858
#[must_use]
4959
pub const fn name(&self) -> Option<Identifier> {
5060
self.name
5161
}
5262

53-
/// Sets the name of the function declaration.
63+
/// Sets the name of the arrow function.
5464
#[inline]
5565
pub fn set_name(&mut self, name: Option<Identifier>) {
5666
self.name = name;
@@ -69,6 +79,13 @@ impl ArrowFunction {
6979
pub const fn body(&self) -> &FunctionBody {
7080
&self.body
7181
}
82+
83+
/// Returns the scopes of the arrow function.
84+
#[inline]
85+
#[must_use]
86+
pub const fn scopes(&self) -> &FunctionScopes {
87+
&self.scopes
88+
}
7289
}
7390

7491
impl ToIndentedString for ArrowFunction {
@@ -102,7 +119,7 @@ impl VisitWith for ArrowFunction {
102119
try_break!(visitor.visit_identifier(ident));
103120
}
104121
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
105-
visitor.visit_script(&self.body)
122+
visitor.visit_function_body(&self.body)
106123
}
107124

108125
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
@@ -113,6 +130,6 @@ impl VisitWith for ArrowFunction {
113130
try_break!(visitor.visit_identifier_mut(ident));
114131
}
115132
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
116-
visitor.visit_script_mut(&mut self.body)
133+
visitor.visit_function_body_mut(&mut self.body)
117134
}
118135
}

core/ast/src/function/async_arrow_function.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::ops::ControlFlow;
22

33
use super::{FormalParameterList, FunctionBody};
4+
use crate::operations::{contains, ContainsSymbol};
5+
use crate::scope::FunctionScopes;
46
use crate::try_break;
57
use crate::visitor::{VisitWith, Visitor, VisitorMut};
68
use crate::{
@@ -23,52 +25,67 @@ use boa_interner::{Interner, ToIndentedString};
2325
#[derive(Clone, Debug, PartialEq)]
2426
pub struct AsyncArrowFunction {
2527
pub(crate) name: Option<Identifier>,
26-
parameters: FormalParameterList,
27-
body: FunctionBody,
28+
pub(crate) parameters: FormalParameterList,
29+
pub(crate) body: FunctionBody,
30+
pub(crate) contains_direct_eval: bool,
31+
32+
#[cfg_attr(feature = "serde", serde(skip))]
33+
pub(crate) scopes: FunctionScopes,
2834
}
2935

3036
impl AsyncArrowFunction {
3137
/// Creates a new `AsyncArrowFunction` AST Expression.
3238
#[inline]
3339
#[must_use]
34-
pub const fn new(
40+
pub fn new(
3541
name: Option<Identifier>,
3642
parameters: FormalParameterList,
3743
body: FunctionBody,
3844
) -> Self {
45+
let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
46+
|| contains(&body, ContainsSymbol::DirectEval);
3947
Self {
4048
name,
4149
parameters,
4250
body,
51+
contains_direct_eval,
52+
scopes: FunctionScopes::default(),
4353
}
4454
}
4555

46-
/// Gets the name of the function declaration.
56+
/// Gets the name of the async arrow function.
4757
#[inline]
4858
#[must_use]
4959
pub const fn name(&self) -> Option<Identifier> {
5060
self.name
5161
}
5262

53-
/// Sets the name of the function declaration.
63+
/// Sets the name of the async arrow function.
5464
#[inline]
5565
pub fn set_name(&mut self, name: Option<Identifier>) {
5666
self.name = name;
5767
}
5868

59-
/// Gets the list of parameters of the arrow function.
69+
/// Gets the list of parameters of the async arrow function.
6070
#[inline]
6171
#[must_use]
6272
pub const fn parameters(&self) -> &FormalParameterList {
6373
&self.parameters
6474
}
6575

66-
/// Gets the body of the arrow function.
76+
/// Gets the body of the async arrow function.
6777
#[inline]
6878
#[must_use]
6979
pub const fn body(&self) -> &FunctionBody {
7080
&self.body
7181
}
82+
83+
/// Returns the scopes of the async arrow function.
84+
#[inline]
85+
#[must_use]
86+
pub const fn scopes(&self) -> &FunctionScopes {
87+
&self.scopes
88+
}
7289
}
7390

7491
impl ToIndentedString for AsyncArrowFunction {
@@ -102,7 +119,7 @@ impl VisitWith for AsyncArrowFunction {
102119
try_break!(visitor.visit_identifier(ident));
103120
}
104121
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
105-
visitor.visit_script(&self.body)
122+
visitor.visit_function_body(&self.body)
106123
}
107124

108125
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
@@ -113,6 +130,6 @@ impl VisitWith for AsyncArrowFunction {
113130
try_break!(visitor.visit_identifier_mut(ident));
114131
}
115132
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
116-
visitor.visit_script_mut(&mut self.body)
133+
visitor.visit_function_body_mut(&mut self.body)
117134
}
118135
}

0 commit comments

Comments
 (0)