Skip to content

Commit

Permalink
Implement with and object environments (#2692)
Browse files Browse the repository at this point in the history
This Pull Request changes the following:

- Implement `with` statement parsing, ast node, compilation and excution.
- Implement object environments that are used in the `with` statement excution.

The implementation of object environments can probably be optimized further by using more compile-time information about when object environments can exist. Maybe there could also be a separate environment stack for object environments to reduce the filtering and iteration that is needed with the current implementation.
This does not fix all tests in the `test/language/statements/with` suite yet. But for most failing tests that I have looked at we are missing other features / have bugs elsewhere.

As a note for the review:
The functions in the `impl Context` block in `boa_engine/src/environments/runtime.rs` are mostly copied / moved from the existing functions. The only change there should be the addition of the object environment logic. They had to be moved to `Context` because of borrow semantics.
  • Loading branch information
raskad committed Mar 20, 2023
1 parent 7b2564e commit 431a358
Show file tree
Hide file tree
Showing 20 changed files with 729 additions and 219 deletions.
1 change: 1 addition & 0 deletions boa_ast/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,7 @@ where
Statement::Try(node) => self.visit_try(node),
Statement::Continue(node) => self.visit_continue(node),
Statement::Break(node) => self.visit_break(node),
Statement::With(with) => self.visit_with(with),
}
}

Expand Down
9 changes: 8 additions & 1 deletion boa_ast/src/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod r#return;
mod switch;
mod throw;
mod r#try;
mod with;

pub mod iteration;

Expand All @@ -26,6 +27,7 @@ pub use self::{
r#try::{Catch, ErrorHandler, Finally, Try},
switch::{Case, Switch},
throw::Throw,
with::With,
};
use core::ops::ControlFlow;

Expand Down Expand Up @@ -92,7 +94,6 @@ pub enum Statement {
/// See [`Return`].
Return(Return),

// TODO: Possibly add `with` statements.
/// See [`Labelled`].
Labelled(Labelled),

Expand All @@ -101,6 +102,9 @@ pub enum Statement {

/// See [`Try`].
Try(Try),

/// See [`With`].
With(With),
}

impl Statement {
Expand Down Expand Up @@ -129,6 +133,7 @@ impl Statement {
Self::Labelled(labelled) => return labelled.to_interned_string(interner),
Self::Throw(throw) => throw.to_interned_string(interner),
Self::Try(try_catch) => return try_catch.to_indented_string(interner, indentation),
Self::With(with) => return with.to_interned_string(interner),
};
s.push(';');
s
Expand Down Expand Up @@ -208,6 +213,7 @@ impl VisitWith for Statement {
Self::Labelled(l) => visitor.visit_labelled(l),
Self::Throw(th) => visitor.visit_throw(th),
Self::Try(tr) => visitor.visit_try(tr),
Self::With(with) => visitor.visit_with(with),
}
}

Expand Down Expand Up @@ -236,6 +242,7 @@ impl VisitWith for Statement {
Self::Labelled(l) => visitor.visit_labelled_mut(l),
Self::Throw(th) => visitor.visit_throw_mut(th),
Self::Try(tr) => visitor.visit_try_mut(tr),
Self::With(with) => visitor.visit_with_mut(with),
}
}
}
81 changes: 81 additions & 0 deletions boa_ast/src/statement/with.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use crate::{
expression::Expression,
statement::Statement,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;

/// The `with` statement extends the scope chain for a statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
/// [spec]: https://tc39.es/ecma262/#prod-WithStatement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct With {
expression: Expression,
statement: Box<Statement>,
}

impl With {
/// Creates a `With` AST node.
#[must_use]
pub fn new(expression: Expression, statement: Statement) -> Self {
Self {
expression,
statement: Box::new(statement),
}
}

/// Gets the expression value of this `With` statement.
#[must_use]
pub const fn expression(&self) -> &Expression {
&self.expression
}

/// Gets the statement value of this `With` statement.
#[must_use]
pub const fn statement(&self) -> &Statement {
&self.statement
}
}

impl From<With> for Statement {
fn from(with: With) -> Self {
Self::With(with)
}
}

impl ToInternedString for With {
fn to_interned_string(&self, interner: &Interner) -> String {
format!(
"with ({}) {{{}}}",
self.expression.to_interned_string(interner),
self.statement.to_interned_string(interner)
)
}
}

impl VisitWith for With {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_expression(&self.expression));
visitor.visit_statement(&self.statement)
}

fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_expression_mut(&mut self.expression));
visitor.visit_statement_mut(&mut self.statement)
}
}
4 changes: 3 additions & 1 deletion boa_ast/src/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use crate::{
IterableLoopInitializer, WhileLoop,
},
Block, Case, Catch, Finally, If, Labelled, LabelledItem, Return, Statement, Switch, Throw,
Try,
Try, With,
},
ModuleItem, ModuleItemList, StatementList, StatementListItem,
};
Expand Down Expand Up @@ -241,6 +241,7 @@ pub trait Visitor<'ast>: Sized {
define_visit!(visit_labelled, Labelled);
define_visit!(visit_throw, Throw);
define_visit!(visit_try, Try);
define_visit!(visit_with, With);
define_visit!(visit_identifier, Identifier);
define_visit!(visit_formal_parameter_list, FormalParameterList);
define_visit!(visit_class_element, ClassElement);
Expand Down Expand Up @@ -434,6 +435,7 @@ pub trait VisitorMut<'ast>: Sized {
define_visit_mut!(visit_labelled_mut, Labelled);
define_visit_mut!(visit_throw_mut, Throw);
define_visit_mut!(visit_try_mut, Try);
define_visit_mut!(visit_with_mut, With);
define_visit_mut!(visit_identifier_mut, Identifier);
define_visit_mut!(visit_formal_parameter_list_mut, FormalParameterList);
define_visit_mut!(visit_class_element_mut, ClassElement);
Expand Down
5 changes: 2 additions & 3 deletions boa_engine/src/builtins/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
use crate::{
builtins::BuiltInObject, context::intrinsics::Intrinsics, environments::DeclarativeEnvironment,
builtins::BuiltInObject, context::intrinsics::Intrinsics, environments::Environment,
error::JsNativeError, object::JsObject, Context, JsArgs, JsResult, JsString, JsValue,
};
use boa_ast::operations::{
contains, contains_arguments, top_level_var_declared_names, ContainsSymbol,
};
use boa_gc::Gc;
use boa_parser::{Parser, Source};
use boa_profiler::Profiler;

Expand Down Expand Up @@ -85,7 +84,7 @@ impl Eval {
#[derive(Debug)]
enum EnvStackAction {
Truncate(usize),
Restore(Vec<Gc<DeclarativeEnvironment>>),
Restore(Vec<Environment>),
}

/// Restores the environment after calling `eval` or after throwing an error.
Expand Down
4 changes: 3 additions & 1 deletion boa_engine/src/bytecompiler/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod labelled;
mod r#loop;
mod switch;
mod r#try;
mod with;

impl ByteCompiler<'_, '_> {
/// Compiles a [`Statement`] `boa_ast` node.
Expand Down Expand Up @@ -56,8 +57,9 @@ impl ByteCompiler<'_, '_> {
self.emit(Opcode::Return, &[]);
}
Statement::Try(t) => self.compile_try(t, use_expr, configurable_globals),
Statement::Empty => {}
Statement::Expression(expr) => self.compile_expr(expr, use_expr),
Statement::With(with) => self.compile_with(with, configurable_globals),
Statement::Empty => {}
}
}
}
14 changes: 14 additions & 0 deletions boa_engine/src/bytecompiler/statement/with.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::{bytecompiler::ByteCompiler, vm::Opcode};
use boa_ast::statement::With;

impl ByteCompiler<'_, '_> {
/// Compile a [`With`] `boa_ast` node
pub(crate) fn compile_with(&mut self, with: &With, configurable_globals: bool) {
self.compile_expr(with.expression(), true);
self.context.push_compile_time_environment(false);
self.emit_opcode(Opcode::PushObjectEnvironment);
self.compile_stmt(with.statement(), false, configurable_globals);
self.context.pop_compile_time_environment();
self.emit_opcode(Opcode::PopEnvironment);
}
}
3 changes: 2 additions & 1 deletion boa_engine/src/environments/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ mod runtime;
pub(crate) use {
compile::CompileTimeEnvironment,
runtime::{
BindingLocator, DeclarativeEnvironment, DeclarativeEnvironmentStack, EnvironmentSlots,
BindingLocator, DeclarativeEnvironment, DeclarativeEnvironmentStack, Environment,
EnvironmentSlots,
},
};

Expand Down
Loading

0 comments on commit 431a358

Please sign in to comment.