Browse Source

Move exec implementations together with AST node structs (#735)

* Move exec implementations together with AST node structs
pull/767/head
George Roman 4 years ago committed by GitHub
parent
commit
767a8cc9c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      boa/src/exec/array/mod.rs
  2. 51
      boa/src/exec/block/mod.rs
  3. 28
      boa/src/exec/break_node/mod.rs
  4. 53
      boa/src/exec/call/mod.rs
  5. 27
      boa/src/exec/conditional/mod.rs
  6. 131
      boa/src/exec/declaration/mod.rs
  7. 29
      boa/src/exec/field/mod.rs
  8. 12
      boa/src/exec/identifier/mod.rs
  9. 168
      boa/src/exec/iteration/mod.rs
  10. 77
      boa/src/exec/mod.rs
  11. 19
      boa/src/exec/new/mod.rs
  12. 39
      boa/src/exec/object/mod.rs
  13. 16
      boa/src/exec/return_smt/mod.rs
  14. 9
      boa/src/exec/spread/mod.rs
  15. 42
      boa/src/exec/statement_list.rs
  16. 83
      boa/src/exec/switch/mod.rs
  17. 9
      boa/src/exec/throw/mod.rs
  18. 55
      boa/src/exec/try_node/mod.rs
  19. 21
      boa/src/syntax/ast/node/array/mod.rs
  20. 48
      boa/src/syntax/ast/node/block/mod.rs
  21. 14
      boa/src/syntax/ast/node/break_node/mod.rs
  22. 7
      boa/src/syntax/ast/node/break_node/tests.rs
  23. 84
      boa/src/syntax/ast/node/conditional/conditional_op/mod.rs
  24. 88
      boa/src/syntax/ast/node/conditional/if_node/mod.rs
  25. 6
      boa/src/syntax/ast/node/conditional/mod.rs
  26. 550
      boa/src/syntax/ast/node/declaration.rs
  27. 91
      boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs
  28. 133
      boa/src/syntax/ast/node/declaration/const_decl_list/mod.rs
  29. 122
      boa/src/syntax/ast/node/declaration/function_decl/mod.rs
  30. 113
      boa/src/syntax/ast/node/declaration/function_expr/mod.rs
  31. 138
      boa/src/syntax/ast/node/declaration/let_decl_list/mod.rs
  32. 17
      boa/src/syntax/ast/node/declaration/mod.rs
  33. 144
      boa/src/syntax/ast/node/declaration/var_decl_list/mod.rs
  34. 118
      boa/src/syntax/ast/node/expression.rs
  35. 118
      boa/src/syntax/ast/node/expression/call/mod.rs
  36. 6
      boa/src/syntax/ast/node/expression/mod.rs
  37. 79
      boa/src/syntax/ast/node/expression/new/mod.rs
  38. 83
      boa/src/syntax/ast/node/field/get_const_field/mod.rs
  39. 86
      boa/src/syntax/ast/node/field/get_field/mod.rs
  40. 6
      boa/src/syntax/ast/node/field/mod.rs
  41. 12
      boa/src/syntax/ast/node/identifier/mod.rs
  42. 333
      boa/src/syntax/ast/node/iteration.rs
  43. 76
      boa/src/syntax/ast/node/iteration/continue_node/mod.rs
  44. 138
      boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs
  45. 210
      boa/src/syntax/ast/node/iteration/for_loop/mod.rs
  46. 35
      boa/src/syntax/ast/node/iteration/mod.rs
  47. 0
      boa/src/syntax/ast/node/iteration/tests.rs
  48. 102
      boa/src/syntax/ast/node/iteration/while_loop/mod.rs
  49. 54
      boa/src/syntax/ast/node/mod.rs
  50. 45
      boa/src/syntax/ast/node/object/mod.rs
  51. 168
      boa/src/syntax/ast/node/operator.rs
  52. 99
      boa/src/syntax/ast/node/operator/assign/mod.rs
  53. 192
      boa/src/syntax/ast/node/operator/bin_op/mod.rs
  54. 10
      boa/src/syntax/ast/node/operator/mod.rs
  55. 0
      boa/src/syntax/ast/node/operator/tests.rs
  56. 127
      boa/src/syntax/ast/node/operator/unary_op/mod.rs
  57. 20
      boa/src/syntax/ast/node/return_smt/mod.rs
  58. 9
      boa/src/syntax/ast/node/spread/mod.rs
  59. 50
      boa/src/syntax/ast/node/statement_list/mod.rs
  60. 93
      boa/src/syntax/ast/node/switch/mod.rs
  61. 0
      boa/src/syntax/ast/node/switch/tests.rs
  62. 9
      boa/src/syntax/ast/node/throw/mod.rs
  63. 60
      boa/src/syntax/ast/node/try_node/mod.rs
  64. 0
      boa/src/syntax/ast/node/try_node/tests.rs

28
boa/src/exec/array/mod.rs

@ -1,28 +0,0 @@
//! Array declaration execution.
use super::{Context, Executable};
use crate::{
builtins::Array,
syntax::ast::node::{ArrayDecl, Node},
BoaProfiler, Result, Value,
};
impl Executable for ArrayDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("ArrayDecl", "exec");
let array = Array::new_array(interpreter)?;
let mut elements = Vec::new();
for elem in self.as_ref() {
if let Node::Spread(ref x) = elem {
let val = x.run(interpreter)?;
let mut vals = interpreter.extract_array_properties(&val).unwrap();
elements.append(&mut vals);
continue; // Don't push array after spread
}
elements.push(elem.run(interpreter)?);
}
Array::add_to_array_object(&array, &elements)?;
Ok(array)
}
}

51
boa/src/exec/block/mod.rs

@ -1,51 +0,0 @@
//! Block statement execution.
use super::{Context, Executable, InterpreterState};
use crate::{
environment::lexical_environment::new_declarative_environment, syntax::ast::node::Block,
BoaProfiler, Result, Value,
};
impl Executable for Block {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Block", "exec");
{
let env = &mut interpreter.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
}
// https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
// The return value is uninitialized, which means it defaults to Value::Undefined
let mut obj = Value::default();
for statement in self.statements() {
obj = statement.run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
break;
}
InterpreterState::Break(_label) => {
// TODO, break to a label.
// Early break.
break;
}
InterpreterState::Continue(_label) => {
// TODO, continue to a label
break;
}
InterpreterState::Executing => {
// Continue execution
}
}
}
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
Ok(obj)
}
}

28
boa/src/exec/break_node/mod.rs

@ -1,28 +0,0 @@
use super::{Context, Executable, InterpreterState};
use crate::{
syntax::ast::node::{Break, Continue},
Result, Value,
};
#[cfg(test)]
mod tests;
impl Executable for Break {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.executor()
.set_current_state(InterpreterState::Break(self.label().map(Box::from)));
Ok(Value::undefined())
}
}
impl Executable for Continue {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.executor()
.set_current_state(InterpreterState::Continue(self.label().map(Box::from)));
Ok(Value::undefined())
}
}

53
boa/src/exec/call/mod.rs

@ -1,53 +0,0 @@
use super::{Context, Executable, InterpreterState};
use crate::{
syntax::ast::node::{Call, Node},
value::{Type, Value},
BoaProfiler, Result,
};
impl Executable for Call {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Call", "exec");
let (this, func) = match self.expr() {
Node::GetConstField(ref get_const_field) => {
let mut obj = get_const_field.obj().run(interpreter)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
}
(obj.clone(), obj.get_field(get_const_field.field()))
}
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(interpreter)?;
let field = get_field.field().run(interpreter)?;
(
obj.clone(),
obj.get_field(field.to_property_key(interpreter)?),
)
}
_ => (
interpreter.realm().global_obj.clone(),
self.expr().run(interpreter)?,
), // 'this' binding should come from the function's self-contained environment
};
let mut v_args = Vec::with_capacity(self.args().len());
for arg in self.args() {
if let Node::Spread(ref x) = arg {
let val = x.run(interpreter)?;
let mut vals = interpreter.extract_array_properties(&val).unwrap();
v_args.append(&mut vals);
break; // after spread we don't accept any new arguments
}
v_args.push(arg.run(interpreter)?);
}
// execute the function call itself
let fnct_result = interpreter.call(&func, &this, &v_args);
// unset the early return flag
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
fnct_result
}
}

27
boa/src/exec/conditional/mod.rs

@ -1,27 +0,0 @@
use super::{Context, Executable};
use crate::{
syntax::ast::node::{ConditionalOp, If},
Result, Value,
};
impl Executable for If {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.to_boolean() {
self.body().run(interpreter)?
} else if let Some(ref else_e) = self.else_node() {
else_e.run(interpreter)?
} else {
Value::undefined()
})
}
}
impl Executable for ConditionalOp {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.to_boolean() {
self.if_true().run(interpreter)?
} else {
self.if_false().run(interpreter)?
})
}
}

131
boa/src/exec/declaration/mod.rs

@ -1,131 +0,0 @@
//! Declaration execution.
use super::{Context, Executable};
use crate::{
builtins::function::FunctionFlags,
environment::lexical_environment::VariableScope,
syntax::ast::node::{
ArrowFunctionDecl, ConstDeclList, FunctionDecl, FunctionExpr, LetDeclList, VarDeclList,
},
BoaProfiler, Result, Value,
};
impl Executable for FunctionDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec");
let val = interpreter.create_function(
self.parameters().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
);
// Set the name and assign it in the current environment
val.set_field("name", self.name());
interpreter.realm_mut().environment.create_mutable_binding(
self.name().to_owned(),
false,
VariableScope::Function,
);
interpreter
.realm_mut()
.environment
.initialize_binding(self.name(), val);
Ok(Value::undefined())
}
}
impl Executable for FunctionExpr {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let val = interpreter.create_function(
self.parameters().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
);
if let Some(name) = self.name() {
val.set_field("name", Value::from(name));
}
Ok(val)
}
}
impl Executable for VarDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for var in self.as_ref() {
let val = match var.init() {
Some(v) => v.run(interpreter)?,
None => Value::undefined(),
};
let environment = &mut interpreter.realm_mut().environment;
if environment.has_binding(var.name()) {
if var.init().is_some() {
environment.set_mutable_binding(var.name(), val, true);
}
} else {
environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Function,
);
environment.initialize_binding(var.name(), val);
}
}
Ok(Value::undefined())
}
}
impl Executable for ConstDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for decl in self.as_ref() {
let val = decl.init().run(interpreter)?;
interpreter
.realm_mut()
.environment
.create_immutable_binding(decl.name().to_owned(), false, VariableScope::Block);
interpreter
.realm_mut()
.environment
.initialize_binding(decl.name(), val);
}
Ok(Value::undefined())
}
}
impl Executable for LetDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for var in self.as_ref() {
let val = match var.init() {
Some(v) => v.run(interpreter)?,
None => Value::undefined(),
};
interpreter.realm_mut().environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
);
interpreter
.realm_mut()
.environment
.initialize_binding(var.name(), val);
}
Ok(Value::undefined())
}
}
impl Executable for ArrowFunctionDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(interpreter.create_function(
self.params().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE
| FunctionFlags::CONSTRUCTABLE
| FunctionFlags::LEXICAL_THIS_MODE,
))
}
}

29
boa/src/exec/field/mod.rs

@ -1,29 +0,0 @@
use super::{Context, Executable};
use crate::{
syntax::ast::node::{GetConstField, GetField},
value::{Type, Value},
Result,
};
impl Executable for GetConstField {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(interpreter)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
}
Ok(obj.get_field(self.field()))
}
}
impl Executable for GetField {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(interpreter)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
}
let field = self.field().run(interpreter)?;
Ok(obj.get_field(field.to_property_key(interpreter)?))
}
}

12
boa/src/exec/identifier/mod.rs

@ -1,12 +0,0 @@
use super::{Context, Executable};
use crate::{syntax::ast::node::identifier::Identifier, Result, Value};
impl Executable for Identifier {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.realm()
.environment
.get_binding_value(self.as_ref())
.ok_or_else(|| interpreter.construct_reference_error(self.as_ref()))
}
}

168
boa/src/exec/iteration/mod.rs

@ -1,168 +0,0 @@
//! Iteration node execution.
use super::{Context, Executable, InterpreterState};
use crate::{
environment::lexical_environment::new_declarative_environment,
syntax::ast::node::{DoWhileLoop, ForLoop, WhileLoop},
BoaProfiler, Result, Value,
};
#[cfg(test)]
mod tests;
// Checking labels for break and continue is the same operation for `ForLoop`, `While` and `DoWhile`
macro_rules! handle_state_with_labels {
($self:ident, $label:ident, $interpreter:ident, $state:tt) => {{
if let Some(brk_label) = $label {
if let Some(stmt_label) = $self.label() {
// Break from where we are, keeping "continue" set as the state
if stmt_label != brk_label.as_ref() {
break;
}
} else {
// if a label is set but the current block has no label, break
break;
}
}
$interpreter
.executor()
.set_current_state(InterpreterState::Executing);
}};
}
impl Executable for ForLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
// Create the block environment.
let _timer = BoaProfiler::global().start_event("ForLoop", "exec");
{
let env = &mut interpreter.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
}
if let Some(init) = self.init() {
init.run(interpreter)?;
}
while self
.condition()
.map(|cond| cond.run(interpreter).map(|v| v.to_boolean()))
.transpose()?
.unwrap_or(true)
{
let result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, interpreter, break);
break;
}
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, interpreter, continue);
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
if let Some(final_expr) = self.final_expr() {
final_expr.run(interpreter)?;
}
}
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
Ok(Value::undefined())
}
}
impl Executable for WhileLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut result = Value::undefined();
while self.cond().run(interpreter)?.to_boolean() {
result = self.expr().run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, interpreter, break);
break;
}
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, interpreter, continue)
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
}
Ok(result)
}
}
impl Executable for DoWhileLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
return Ok(result);
}
InterpreterState::Continue(_label) => {
// TODO continue to label;
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
while self.cond().run(interpreter)?.to_boolean() {
result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
}
Ok(result)
}
}

77
boa/src/exec/mod.rs

@ -1,31 +1,9 @@
//! Execution of the AST, this is where the interpreter actually runs
mod array;
mod block;
mod break_node;
mod call;
mod conditional;
mod declaration;
mod field;
mod identifier;
mod iteration;
mod new;
mod object;
mod operator;
mod return_smt;
mod spread;
mod statement_list;
mod switch;
mod throw;
mod try_node;
#[cfg(test)]
mod tests;
use crate::{
syntax::ast::{constant::Const, node::Node},
BoaProfiler, Context, Result, Value,
};
use crate::{Context, Result, Value};
pub trait Executable {
/// Runs this executable in the given context.
@ -71,56 +49,3 @@ impl Interpreter {
&self.state
}
}
impl Executable for Node {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Executable", "exec");
match *self {
Node::Const(Const::Null) => Ok(Value::null()),
Node::Const(Const::Num(num)) => Ok(Value::rational(num)),
Node::Const(Const::Int(num)) => Ok(Value::integer(num)),
Node::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())),
Node::Const(Const::Undefined) => Ok(Value::Undefined),
// we can't move String from Const into value, because const is a garbage collected value
// Which means Drop() get's called on Const, but str will be gone at that point.
// Do Const values need to be garbage collected? We no longer need them once we've generated Values
Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())),
Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)),
Node::Block(ref block) => block.run(interpreter),
Node::Identifier(ref identifier) => identifier.run(interpreter),
Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(interpreter),
Node::GetField(ref get_field) => get_field.run(interpreter),
Node::Call(ref call) => call.run(interpreter),
Node::WhileLoop(ref while_loop) => while_loop.run(interpreter),
Node::DoWhileLoop(ref do_while) => do_while.run(interpreter),
Node::ForLoop(ref for_loop) => for_loop.run(interpreter),
Node::If(ref if_smt) => if_smt.run(interpreter),
Node::ConditionalOp(ref op) => op.run(interpreter),
Node::Switch(ref switch) => switch.run(interpreter),
Node::Object(ref obj) => obj.run(interpreter),
Node::ArrayDecl(ref arr) => arr.run(interpreter),
// <https://tc39.es/ecma262/#sec-createdynamicfunction>
Node::FunctionDecl(ref decl) => decl.run(interpreter),
// <https://tc39.es/ecma262/#sec-createdynamicfunction>
Node::FunctionExpr(ref function_expr) => function_expr.run(interpreter),
Node::ArrowFunctionDecl(ref decl) => decl.run(interpreter),
Node::BinOp(ref op) => op.run(interpreter),
Node::UnaryOp(ref op) => op.run(interpreter),
Node::New(ref call) => call.run(interpreter),
Node::Return(ref ret) => ret.run(interpreter),
Node::Throw(ref throw) => throw.run(interpreter),
Node::Assign(ref op) => op.run(interpreter),
Node::VarDeclList(ref decl) => decl.run(interpreter),
Node::LetDeclList(ref decl) => decl.run(interpreter),
Node::ConstDeclList(ref decl) => decl.run(interpreter),
Node::Spread(ref spread) => spread.run(interpreter),
Node::This => {
// Will either return `this` binding or undefined
Ok(interpreter.realm().environment.get_this_binding())
}
Node::Try(ref try_node) => try_node.run(interpreter),
Node::Break(ref break_node) => break_node.run(interpreter),
Node::Continue(ref continue_node) => continue_node.run(interpreter),
}
}
}

19
boa/src/exec/new/mod.rs

@ -1,19 +0,0 @@
use super::{Context, Executable};
use crate::{syntax::ast::node::New, BoaProfiler, Result, Value};
impl Executable for New {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("New", "exec");
let func_object = self.expr().run(interpreter)?;
let mut v_args = Vec::with_capacity(self.args().len());
for arg in self.args() {
v_args.push(arg.run(interpreter)?);
}
match func_object {
Value::Object(ref object) => object.construct(&v_args, interpreter),
_ => Ok(Value::undefined()),
}
}
}

39
boa/src/exec/object/mod.rs

@ -1,39 +0,0 @@
//! Object execution.
use super::{Context, Executable};
use crate::{
syntax::ast::node::MethodDefinitionKind,
syntax::ast::node::{Object, PropertyDefinition},
Result, Value,
};
impl Executable for Object {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let global_val = &interpreter
.realm()
.environment
.get_global_object()
.expect("Could not get the global object");
let obj = Value::new_object(Some(global_val));
// TODO: Implement the rest of the property types.
for property in self.properties().iter() {
match property {
PropertyDefinition::Property(key, value) => {
obj.set_field(key.clone(), value.run(interpreter)?);
}
PropertyDefinition::MethodDefinition(kind, name, func) => {
if let MethodDefinitionKind::Ordinary = kind {
obj.set_field(name.clone(), func.run(interpreter)?);
} else {
// TODO: Implement other types of MethodDefinitionKinds.
unimplemented!("other types of property method definitions.");
}
}
i => unimplemented!("{:?} type of property", i),
}
}
Ok(obj)
}
}

16
boa/src/exec/return_smt/mod.rs

@ -1,16 +0,0 @@
use super::{Context, Executable, InterpreterState};
use crate::{syntax::ast::node::Return, Result, Value};
impl Executable for Return {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let result = match self.expr() {
Some(ref v) => v.run(interpreter),
None => Ok(Value::undefined()),
};
// Set flag for return
interpreter
.executor()
.set_current_state(InterpreterState::Return);
result
}
}

9
boa/src/exec/spread/mod.rs

@ -1,9 +0,0 @@
use super::{Context, Executable};
use crate::{syntax::ast::node::Spread, Result, Value};
impl Executable for Spread {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
// TODO: for now we can do nothing but return the value as-is
self.val().run(interpreter)
}
}

42
boa/src/exec/statement_list.rs

@ -1,42 +0,0 @@
//! Statement list execution.
use super::{Context, Executable, InterpreterState};
use crate::{syntax::ast::node::StatementList, BoaProfiler, Result, Value};
impl Executable for StatementList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("StatementList", "exec");
// https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
// The return value is uninitialized, which means it defaults to Value::Undefined
let mut obj = Value::default();
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in self.statements().iter().enumerate() {
let val = item.run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
obj = val;
break;
}
InterpreterState::Break(_label) => {
// Early break.
break;
}
InterpreterState::Continue(_label) => {
break;
}
InterpreterState::Executing => {
// Continue execution
}
}
if i + 1 == self.statements().len() {
obj = val;
}
}
Ok(obj)
}
}

83
boa/src/exec/switch/mod.rs

@ -1,83 +0,0 @@
use super::{Context, Executable, InterpreterState};
use crate::{syntax::ast::node::Switch, Result, Value};
#[cfg(test)]
mod tests;
impl Executable for Switch {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let val = self.val().run(interpreter)?;
let mut result = Value::null();
let mut matched = false;
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// If a case block does not end with a break statement then subsequent cases will be run without
// checking their conditions until a break is encountered.
let mut fall_through: bool = false;
for case in self.cases().iter() {
let cond = case.condition();
let block = case.body();
if fall_through || val.strict_equals(&cond.run(interpreter)?) {
matched = true;
let result = block.run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
return Ok(result);
}
InterpreterState::Break(_label) => {
// TODO, break to a label.
// Break statement encountered so therefore end switch statement.
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO, continue to a label.
break;
}
InterpreterState::Executing => {
// Continuing execution / falling through to next case statement(s).
fall_through = true;
}
}
}
}
if !matched {
if let Some(default) = self.default() {
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in default.iter().enumerate() {
let val = item.run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
result = val;
break;
}
InterpreterState::Break(_label) => {
// TODO, break to a label.
// Early break.
break;
}
_ => {
// Continue execution
}
}
if i == default.len() - 1 {
result = val;
}
}
}
}
Ok(result)
}
}

9
boa/src/exec/throw/mod.rs

@ -1,9 +0,0 @@
use super::{Context, Executable};
use crate::{syntax::ast::node::Throw, Result, Value};
impl Executable for Throw {
#[inline]
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Err(self.expr().run(interpreter)?)
}
}

55
boa/src/exec/try_node/mod.rs

@ -1,55 +0,0 @@
//! Try..catch node execution.
use super::{Context, Executable};
use crate::{
environment::lexical_environment::{new_declarative_environment, VariableScope},
syntax::ast::node::Try,
BoaProfiler, Result, Value,
};
#[cfg(test)]
mod tests;
impl Executable for Try {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Try", "exec");
let res = self.block().run(interpreter).map_or_else(
|err| {
if let Some(catch) = self.catch() {
{
let env = &mut interpreter.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
if let Some(param) = catch.parameter() {
env.create_mutable_binding(
param.to_owned(),
false,
VariableScope::Block,
);
env.initialize_binding(param, err);
}
}
let res = catch.block().run(interpreter);
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
res
} else {
Err(err)
}
},
Ok,
);
if let Some(finally) = self.finally() {
finally.run(interpreter)?;
}
res
}
}

21
boa/src/syntax/ast/node/array.rs → boa/src/syntax/ast/node/array/mod.rs

@ -1,6 +1,7 @@
//! Array declaration node.
use super::{join_nodes, Node};
use crate::{builtins::Array, exec::Executable, BoaProfiler, Context, Result, Value};
use gc::{Finalize, Trace};
use std::fmt;
@ -30,6 +31,26 @@ pub struct ArrayDecl {
arr: Box<[Node]>,
}
impl Executable for ArrayDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("ArrayDecl", "exec");
let array = Array::new_array(interpreter)?;
let mut elements = Vec::new();
for elem in self.as_ref() {
if let Node::Spread(ref x) = elem {
let val = x.run(interpreter)?;
let mut vals = interpreter.extract_array_properties(&val).unwrap();
elements.append(&mut vals);
continue; // Don't push array after spread
}
elements.push(elem.run(interpreter)?);
}
Array::add_to_array_object(&array, &elements)?;
Ok(array)
}
}
impl AsRef<[Node]> for ArrayDecl {
fn as_ref(&self) -> &[Node] {
&self.arr

48
boa/src/syntax/ast/node/block.rs → boa/src/syntax/ast/node/block/mod.rs

@ -1,6 +1,10 @@
//! Block AST node.
use super::{Node, StatementList};
use crate::{
environment::lexical_environment::new_declarative_environment, exec::Executable,
exec::InterpreterState, BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
@ -44,6 +48,50 @@ impl Block {
}
}
impl Executable for Block {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Block", "exec");
{
let env = &mut interpreter.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
}
// https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
// The return value is uninitialized, which means it defaults to Value::Undefined
let mut obj = Value::default();
for statement in self.statements() {
obj = statement.run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
break;
}
InterpreterState::Break(_label) => {
// TODO, break to a label.
// Early break.
break;
}
InterpreterState::Continue(_label) => {
// TODO, continue to a label
break;
}
InterpreterState::Executing => {
// Continue execution
}
}
}
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
Ok(obj)
}
}
impl<T> From<T> for Block
where
T: Into<StatementList>,

14
boa/src/syntax/ast/node/break_node.rs → boa/src/syntax/ast/node/break_node/mod.rs

@ -1,10 +1,14 @@
use super::Node;
use crate::{exec::Executable, exec::InterpreterState, Context, Result, Value};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// The `break` statement terminates the current loop, switch, or label statement and transfers
/// program control to the statement following the terminated statement.
///
@ -43,6 +47,16 @@ impl Break {
}
}
impl Executable for Break {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.executor()
.set_current_state(InterpreterState::Break(self.label().map(Box::from)));
Ok(Value::undefined())
}
}
impl fmt::Display for Break {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(

7
boa/src/exec/break_node/tests.rs → boa/src/syntax/ast/node/break_node/tests.rs

@ -1,5 +1,8 @@
use super::{Context, InterpreterState};
use crate::{exec::Executable, syntax::ast::node::Break};
use crate::{
exec::{Executable, InterpreterState},
syntax::ast::node::Break,
Context,
};
#[test]
fn check_post_state() {

84
boa/src/syntax/ast/node/conditional/conditional_op/mod.rs

@ -0,0 +1,84 @@
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `conditional` (ternary) operator is the only JavaScript operator that takes three
/// operands.
///
/// This operator is the only JavaScript operator that takes three operands: a condition
/// followed by a question mark (`?`), then an expression to execute `if` the condition is
/// truthy followed by a colon (`:`), and finally the expression to execute if the condition
/// is `false`. This operator is frequently used as a shortcut for the `if` statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ConditionalOp {
condition: Box<Node>,
if_true: Box<Node>,
if_false: Box<Node>,
}
impl ConditionalOp {
pub fn cond(&self) -> &Node {
&self.condition
}
pub fn if_true(&self) -> &Node {
&self.if_true
}
pub fn if_false(&self) -> &Node {
&self.if_false
}
/// Creates a `ConditionalOp` AST node.
pub fn new<C, T, F>(condition: C, if_true: T, if_false: F) -> Self
where
C: Into<Node>,
T: Into<Node>,
F: Into<Node>,
{
Self {
condition: Box::new(condition.into()),
if_true: Box::new(if_true.into()),
if_false: Box::new(if_false.into()),
}
}
}
impl Executable for ConditionalOp {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.to_boolean() {
self.if_true().run(interpreter)?
} else {
self.if_false().run(interpreter)?
})
}
}
impl fmt::Display for ConditionalOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} ? {} : {}",
self.cond(),
self.if_true(),
self.if_false()
)
}
}
impl From<ConditionalOp> for Node {
fn from(cond_op: ConditionalOp) -> Node {
Self::ConditionalOp(cond_op)
}
}

88
boa/src/syntax/ast/node/conditional.rs → boa/src/syntax/ast/node/conditional/if_node/mod.rs

@ -1,4 +1,4 @@
use super::Node;
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use std::fmt;
@ -57,7 +57,11 @@ impl If {
}
}
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indent: usize,
) -> fmt::Result {
write!(f, "if ({}) ", self.cond())?;
match self.else_node() {
Some(else_e) => {
@ -70,6 +74,18 @@ impl If {
}
}
impl Executable for If {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.to_boolean() {
self.body().run(interpreter)?
} else if let Some(ref else_e) = self.else_node() {
else_e.run(interpreter)?
} else {
Value::undefined()
})
}
}
impl fmt::Display for If {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
@ -81,71 +97,3 @@ impl From<If> for Node {
Self::If(if_stm)
}
}
/// The `conditional` (ternary) operator is the only JavaScript operator that takes three
/// operands.
///
/// This operator is the only JavaScript operator that takes three operands: a condition
/// followed by a question mark (`?`), then an expression to execute `if` the condition is
/// truthy followed by a colon (`:`), and finally the expression to execute if the condition
/// is `false`. This operator is frequently used as a shortcut for the `if` statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ConditionalOp {
condition: Box<Node>,
if_true: Box<Node>,
if_false: Box<Node>,
}
impl ConditionalOp {
pub fn cond(&self) -> &Node {
&self.condition
}
pub fn if_true(&self) -> &Node {
&self.if_true
}
pub fn if_false(&self) -> &Node {
&self.if_false
}
/// Creates a `ConditionalOp` AST node.
pub fn new<C, T, F>(condition: C, if_true: T, if_false: F) -> Self
where
C: Into<Node>,
T: Into<Node>,
F: Into<Node>,
{
Self {
condition: Box::new(condition.into()),
if_true: Box::new(if_true.into()),
if_false: Box::new(if_false.into()),
}
}
}
impl fmt::Display for ConditionalOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} ? {} : {}",
self.cond(),
self.if_true(),
self.if_false()
)
}
}
impl From<ConditionalOp> for Node {
fn from(cond_op: ConditionalOp) -> Node {
Self::ConditionalOp(cond_op)
}
}

6
boa/src/syntax/ast/node/conditional/mod.rs

@ -0,0 +1,6 @@
//! Conditional nodes
pub mod conditional_op;
pub mod if_node;
pub use self::{conditional_op::ConditionalOp, if_node::If};

550
boa/src/syntax/ast/node/declaration.rs

@ -1,550 +0,0 @@
//! Declaration nodes.
use super::{join_nodes, FormalParameter, Identifier, Node, StatementList};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `var` statement declares a variable, optionally initializing it to a value.
///
/// var declarations, wherever they occur, are processed before any code is executed. This is
/// called hoisting, and is discussed further below.
///
/// The scope of a variable declared with var is its current execution context, which is either
/// the enclosing function or, for variables declared outside any function, global. If you
/// re-declare a JavaScript variable, it will not lose its value.
///
/// Assigning a value to an undeclared variable implicitly creates it as a global variable (it
/// becomes a property of the global object) when the assignment is executed.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-VariableStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct VarDeclList {
#[cfg_attr(feature = "serde", serde(flatten))]
vars: Box<[VarDecl]>,
}
impl<T> From<T> for VarDeclList
where
T: Into<Box<[VarDecl]>>,
{
fn from(list: T) -> Self {
Self { vars: list.into() }
}
}
impl From<VarDecl> for VarDeclList {
fn from(decl: VarDecl) -> Self {
Self {
vars: Box::new([decl]),
}
}
}
impl AsRef<[VarDecl]> for VarDeclList {
fn as_ref(&self) -> &[VarDecl] {
&self.vars
}
}
impl fmt::Display for VarDeclList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.vars.is_empty() {
write!(f, "var ")?;
join_nodes(f, &self.vars)
} else {
Ok(())
}
}
}
impl From<VarDeclList> for Node {
fn from(list: VarDeclList) -> Self {
Self::VarDeclList(list)
}
}
/// Individual variable declaration.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct VarDecl {
name: Identifier,
init: Option<Node>,
}
impl fmt::Display for VarDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.name, f)?;
if let Some(ref init) = self.init {
write!(f, " = {}", init)?;
}
Ok(())
}
}
impl VarDecl {
/// Creates a new variable declaration.
pub(in crate::syntax) fn new<N, I>(name: N, init: I) -> Self
where
N: Into<Identifier>,
I: Into<Option<Node>>,
{
Self {
name: name.into(),
init: init.into(),
}
}
/// Gets the name of the variable.
pub fn name(&self) -> &str {
self.name.as_ref()
}
/// Gets the initialization node for the variable, if any.
pub fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
}
/// The `function` expression defines a function with the specified parameters.
///
/// A function created with a function expression is a `Function` object and has all the
/// properties, methods and behavior of `Function`.
///
/// A function can also be created using a declaration (see function expression).
///
/// By default, functions return `undefined`. To return any other value, the function must have
/// a return statement that specifies the value to return.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct FunctionExpr {
name: Option<Box<str>>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
impl FunctionExpr {
/// Creates a new function expression
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self
where
N: Into<Option<Box<str>>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
name: name.into(),
parameters: parameters.into(),
body: body.into(),
}
}
/// Gets the name of the function declaration.
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(Box::as_ref)
}
/// Gets the list of parameters of the function declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}
/// Gets the body of the function declaration.
pub fn body(&self) -> &[Node] {
self.body.statements()
}
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
f.write_str("function")?;
if let Some(ref name) = self.name {
write!(f, " {}", name)?;
}
f.write_str("(")?;
join_nodes(f, &self.parameters)?;
f.write_str(") {{")?;
self.body.display(f, indentation + 1)?;
writeln!(f, "}}")
}
}
impl fmt::Display for FunctionExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<FunctionExpr> for Node {
fn from(expr: FunctionExpr) -> Self {
Self::FunctionExpr(expr)
}
}
/// The `function` declaration (function statement) defines a function with the specified
/// parameters.
///
/// A function created with a function declaration is a `Function` object and has all the
/// properties, methods and behavior of `Function`.
///
/// A function can also be created using an expression (see [function expression][func_expr]).
///
/// By default, functions return `undefined`. To return any other value, the function must have
/// a return statement that specifies the value to return.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
/// [func_expr]: ../enum.Node.html#variant.FunctionExpr
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct FunctionDecl {
name: Box<str>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
impl FunctionDecl {
/// Creates a new function declaration.
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self
where
N: Into<Box<str>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
name: name.into(),
parameters: parameters.into(),
body: body.into(),
}
}
/// Gets the name of the function declaration.
pub fn name(&self) -> &str {
&self.name
}
/// Gets the list of parameters of the function declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}
/// Gets the body of the function declaration.
pub fn body(&self) -> &[Node] {
self.body.statements()
}
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
write!(f, "function {}(", self.name)?;
join_nodes(f, &self.parameters)?;
f.write_str(") {{")?;
self.body.display(f, indentation + 1)?;
writeln!(f, "}}")
}
}
impl From<FunctionDecl> for Node {
fn from(decl: FunctionDecl) -> Self {
Self::FunctionDecl(decl)
}
}
impl fmt::Display for FunctionDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
/// An arrow function expression is a syntactically compact alternative to a regular function
/// expression.
///
/// Arrow function expressions are ill suited as methods, and they cannot be used as
/// constructors. Arrow functions cannot be used as constructors and will throw an error when
/// used with new.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ArrowFunctionDecl {
params: Box<[FormalParameter]>,
body: StatementList,
}
impl ArrowFunctionDecl {
/// Creates a new `ArrowFunctionDecl` AST node.
pub(in crate::syntax) fn new<P, B>(params: P, body: B) -> Self
where
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
params: params.into(),
body: body.into(),
}
}
/// Gets the list of parameters of the arrow function.
pub(crate) fn params(&self) -> &[FormalParameter] {
&self.params
}
/// Gets the body of the arrow function.
pub(crate) fn body(&self) -> &[Node] {
&self.body.statements()
}
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
write!(f, "(")?;
join_nodes(f, &self.params)?;
f.write_str(") => ")?;
self.body.display(f, indentation)
}
}
impl fmt::Display for ArrowFunctionDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<ArrowFunctionDecl> for Node {
fn from(decl: ArrowFunctionDecl) -> Self {
Self::ArrowFunctionDecl(decl)
}
}
/// The `const` statements are block-scoped, much like variables defined using the `let`
/// keyword.
///
/// This declaration creates a constant whose scope can be either global or local to the block
/// in which it is declared. Global constants do not become properties of the window object,
/// unlike var variables.
///
/// An initializer for a constant is required. You must specify its value in the same statement
/// in which it's declared. (This makes sense, given that it can't be changed later.)
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
/// [identifier]: https://developer.mozilla.org/en-US/docs/Glossary/identifier
/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ConstDeclList {
#[cfg_attr(feature = "serde", serde(flatten))]
list: Box<[ConstDecl]>,
}
impl<T> From<T> for ConstDeclList
where
T: Into<Box<[ConstDecl]>>,
{
fn from(list: T) -> Self {
Self { list: list.into() }
}
}
impl From<ConstDecl> for ConstDeclList {
fn from(decl: ConstDecl) -> Self {
Self {
list: Box::new([decl]),
}
}
}
impl AsRef<[ConstDecl]> for ConstDeclList {
fn as_ref(&self) -> &[ConstDecl] {
&self.list
}
}
impl fmt::Display for ConstDeclList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.list.is_empty() {
write!(f, "const ")?;
join_nodes(f, &self.list)
} else {
Ok(())
}
}
}
impl From<ConstDeclList> for Node {
fn from(list: ConstDeclList) -> Self {
Self::ConstDeclList(list)
}
}
/// Individual constant declaration.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ConstDecl {
name: Identifier,
init: Node,
}
impl fmt::Display for ConstDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} = {}", self.name, self.init)
}
}
impl ConstDecl {
/// Creates a new variable declaration.
pub(in crate::syntax) fn new<N, I>(name: N, init: I) -> Self
where
N: Into<Identifier>,
I: Into<Node>,
{
Self {
name: name.into(),
init: init.into(),
}
}
/// Gets the name of the variable.
pub fn name(&self) -> &str {
self.name.as_ref()
}
/// Gets the initialization node for the variable, if any.
pub fn init(&self) -> &Node {
&self.init
}
}
/// The `let` statement declares a block scope local variable, optionally initializing it to a
/// value.
///
///
/// `let` allows you to declare variables that are limited to a scope of a block statement, or
/// expression on which it is used, unlike the `var` keyword, which defines a variable
/// globally, or locally to an entire function regardless of block scope.
///
/// Just like const the `let` does not create properties of the window object when declared
/// globally (in the top-most scope).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct LetDeclList {
#[cfg_attr(feature = "serde", serde(flatten))]
list: Box<[LetDecl]>,
}
impl<T> From<T> for LetDeclList
where
T: Into<Box<[LetDecl]>>,
{
fn from(list: T) -> Self {
Self { list: list.into() }
}
}
impl From<LetDecl> for LetDeclList {
fn from(decl: LetDecl) -> Self {
Self {
list: Box::new([decl]),
}
}
}
impl AsRef<[LetDecl]> for LetDeclList {
fn as_ref(&self) -> &[LetDecl] {
&self.list
}
}
impl fmt::Display for LetDeclList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.list.is_empty() {
write!(f, "let ")?;
join_nodes(f, &self.list)
} else {
Ok(())
}
}
}
impl From<LetDeclList> for Node {
fn from(list: LetDeclList) -> Self {
Self::LetDeclList(list)
}
}
/// Individual constant declaration.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct LetDecl {
name: Identifier,
init: Option<Node>,
}
impl fmt::Display for LetDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.name, f)?;
if let Some(ref init) = self.init {
write!(f, " = {}", init)?;
}
Ok(())
}
}
impl LetDecl {
/// Creates a new variable declaration.
pub(in crate::syntax) fn new<N, I>(name: N, init: I) -> Self
where
N: Into<Identifier>,
I: Into<Option<Node>>,
{
Self {
name: name.into(),
init: init.into(),
}
}
/// Gets the name of the variable.
pub fn name(&self) -> &str {
self.name.as_ref()
}
/// Gets the initialization node for the variable, if any.
pub fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
}

91
boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs

@ -0,0 +1,91 @@
use crate::{
builtins::function::FunctionFlags,
exec::Executable,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// An arrow function expression is a syntactically compact alternative to a regular function
/// expression.
///
/// Arrow function expressions are ill suited as methods, and they cannot be used as
/// constructors. Arrow functions cannot be used as constructors and will throw an error when
/// used with new.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ArrowFunctionDecl {
params: Box<[FormalParameter]>,
body: StatementList,
}
impl ArrowFunctionDecl {
/// Creates a new `ArrowFunctionDecl` AST node.
pub(in crate::syntax) fn new<P, B>(params: P, body: B) -> Self
where
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
params: params.into(),
body: body.into(),
}
}
/// Gets the list of parameters of the arrow function.
pub(crate) fn params(&self) -> &[FormalParameter] {
&self.params
}
/// Gets the body of the arrow function.
pub(crate) fn body(&self) -> &[Node] {
&self.body.statements()
}
/// Implements the display formatting with indentation.
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
write!(f, "(")?;
join_nodes(f, &self.params)?;
f.write_str(") => ")?;
self.body.display(f, indentation)
}
}
impl Executable for ArrowFunctionDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(interpreter.create_function(
self.params().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE
| FunctionFlags::CONSTRUCTABLE
| FunctionFlags::LEXICAL_THIS_MODE,
))
}
}
impl fmt::Display for ArrowFunctionDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<ArrowFunctionDecl> for Node {
fn from(decl: ArrowFunctionDecl) -> Self {
Self::ArrowFunctionDecl(decl)
}
}

133
boa/src/syntax/ast/node/declaration/const_decl_list/mod.rs

@ -0,0 +1,133 @@
use crate::{
environment::lexical_environment::VariableScope,
exec::Executable,
syntax::ast::node::{join_nodes, Identifier, Node},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `const` statements are block-scoped, much like variables defined using the `let`
/// keyword.
///
/// This declaration creates a constant whose scope can be either global or local to the block
/// in which it is declared. Global constants do not become properties of the window object,
/// unlike var variables.
///
/// An initializer for a constant is required. You must specify its value in the same statement
/// in which it's declared. (This makes sense, given that it can't be changed later.)
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
/// [identifier]: https://developer.mozilla.org/en-US/docs/Glossary/identifier
/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ConstDeclList {
#[cfg_attr(feature = "serde", serde(flatten))]
list: Box<[ConstDecl]>,
}
impl Executable for ConstDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for decl in self.as_ref() {
let val = decl.init().run(interpreter)?;
interpreter
.realm_mut()
.environment
.create_immutable_binding(decl.name().to_owned(), false, VariableScope::Block);
interpreter
.realm_mut()
.environment
.initialize_binding(decl.name(), val);
}
Ok(Value::undefined())
}
}
impl<T> From<T> for ConstDeclList
where
T: Into<Box<[ConstDecl]>>,
{
fn from(list: T) -> Self {
Self { list: list.into() }
}
}
impl From<ConstDecl> for ConstDeclList {
fn from(decl: ConstDecl) -> Self {
Self {
list: Box::new([decl]),
}
}
}
impl AsRef<[ConstDecl]> for ConstDeclList {
fn as_ref(&self) -> &[ConstDecl] {
&self.list
}
}
impl fmt::Display for ConstDeclList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.list.is_empty() {
write!(f, "const ")?;
join_nodes(f, &self.list)
} else {
Ok(())
}
}
}
impl From<ConstDeclList> for Node {
fn from(list: ConstDeclList) -> Self {
Self::ConstDeclList(list)
}
}
/// Individual constant declaration.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ConstDecl {
name: Identifier,
init: Node,
}
impl fmt::Display for ConstDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} = {}", self.name, self.init)
}
}
impl ConstDecl {
/// Creates a new variable declaration.
pub(in crate::syntax) fn new<N, I>(name: N, init: I) -> Self
where
N: Into<Identifier>,
I: Into<Node>,
{
Self {
name: name.into(),
init: init.into(),
}
}
/// Gets the name of the variable.
pub fn name(&self) -> &str {
self.name.as_ref()
}
/// Gets the initialization node for the variable, if any.
pub fn init(&self) -> &Node {
&self.init
}
}

122
boa/src/syntax/ast/node/declaration/function_decl/mod.rs

@ -0,0 +1,122 @@
use crate::{
builtins::function::FunctionFlags,
environment::lexical_environment::VariableScope,
exec::Executable,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `function` declaration (function statement) defines a function with the specified
/// parameters.
///
/// A function created with a function declaration is a `Function` object and has all the
/// properties, methods and behavior of `Function`.
///
/// A function can also be created using an expression (see [function expression][func_expr]).
///
/// By default, functions return `undefined`. To return any other value, the function must have
/// a return statement that specifies the value to return.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
/// [func_expr]: ../enum.Node.html#variant.FunctionExpr
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct FunctionDecl {
name: Box<str>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
impl FunctionDecl {
/// Creates a new function declaration.
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self
where
N: Into<Box<str>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
name: name.into(),
parameters: parameters.into(),
body: body.into(),
}
}
/// Gets the name of the function declaration.
pub fn name(&self) -> &str {
&self.name
}
/// Gets the list of parameters of the function declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}
/// Gets the body of the function declaration.
pub fn body(&self) -> &[Node] {
self.body.statements()
}
/// Implements the display formatting with indentation.
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
write!(f, "function {}(", self.name)?;
join_nodes(f, &self.parameters)?;
f.write_str(") {{")?;
self.body.display(f, indentation + 1)?;
writeln!(f, "}}")
}
}
impl Executable for FunctionDecl {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec");
let val = interpreter.create_function(
self.parameters().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
);
// Set the name and assign it in the current environment
val.set_field("name", self.name());
interpreter.realm_mut().environment.create_mutable_binding(
self.name().to_owned(),
false,
VariableScope::Function,
);
interpreter
.realm_mut()
.environment
.initialize_binding(self.name(), val);
Ok(Value::undefined())
}
}
impl From<FunctionDecl> for Node {
fn from(decl: FunctionDecl) -> Self {
Self::FunctionDecl(decl)
}
}
impl fmt::Display for FunctionDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}

113
boa/src/syntax/ast/node/declaration/function_expr/mod.rs

@ -0,0 +1,113 @@
use crate::{
builtins::function::FunctionFlags,
exec::Executable,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `function` expression defines a function with the specified parameters.
///
/// A function created with a function expression is a `Function` object and has all the
/// properties, methods and behavior of `Function`.
///
/// A function can also be created using a declaration (see function expression).
///
/// By default, functions return `undefined`. To return any other value, the function must have
/// a return statement that specifies the value to return.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct FunctionExpr {
name: Option<Box<str>>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
impl FunctionExpr {
/// Creates a new function expression
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self
where
N: Into<Option<Box<str>>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
name: name.into(),
parameters: parameters.into(),
body: body.into(),
}
}
/// Gets the name of the function declaration.
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(Box::as_ref)
}
/// Gets the list of parameters of the function declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}
/// Gets the body of the function declaration.
pub fn body(&self) -> &[Node] {
self.body.statements()
}
/// Implements the display formatting with indentation.
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
f.write_str("function")?;
if let Some(ref name) = self.name {
write!(f, " {}", name)?;
}
f.write_str("(")?;
join_nodes(f, &self.parameters)?;
f.write_str(") {{")?;
self.body.display(f, indentation + 1)?;
writeln!(f, "}}")
}
}
impl Executable for FunctionExpr {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let val = interpreter.create_function(
self.parameters().to_vec(),
self.body().to_vec(),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
);
if let Some(name) = self.name() {
val.set_field("name", Value::from(name));
}
Ok(val)
}
}
impl fmt::Display for FunctionExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<FunctionExpr> for Node {
fn from(expr: FunctionExpr) -> Self {
Self::FunctionExpr(expr)
}
}

138
boa/src/syntax/ast/node/declaration/let_decl_list/mod.rs

@ -0,0 +1,138 @@
use crate::{
environment::lexical_environment::VariableScope,
exec::Executable,
syntax::ast::node::{join_nodes, Identifier, Node},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `let` statement declares a block scope local variable, optionally initializing it to a
/// value.
///
///
/// `let` allows you to declare variables that are limited to a scope of a block statement, or
/// expression on which it is used, unlike the `var` keyword, which defines a variable
/// globally, or locally to an entire function regardless of block scope.
///
/// Just like const the `let` does not create properties of the window object when declared
/// globally (in the top-most scope).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct LetDeclList {
#[cfg_attr(feature = "serde", serde(flatten))]
list: Box<[LetDecl]>,
}
impl Executable for LetDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for var in self.as_ref() {
let val = match var.init() {
Some(v) => v.run(interpreter)?,
None => Value::undefined(),
};
interpreter.realm_mut().environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
);
interpreter
.realm_mut()
.environment
.initialize_binding(var.name(), val);
}
Ok(Value::undefined())
}
}
impl<T> From<T> for LetDeclList
where
T: Into<Box<[LetDecl]>>,
{
fn from(list: T) -> Self {
Self { list: list.into() }
}
}
impl From<LetDecl> for LetDeclList {
fn from(decl: LetDecl) -> Self {
Self {
list: Box::new([decl]),
}
}
}
impl AsRef<[LetDecl]> for LetDeclList {
fn as_ref(&self) -> &[LetDecl] {
&self.list
}
}
impl fmt::Display for LetDeclList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.list.is_empty() {
write!(f, "let ")?;
join_nodes(f, &self.list)
} else {
Ok(())
}
}
}
impl From<LetDeclList> for Node {
fn from(list: LetDeclList) -> Self {
Self::LetDeclList(list)
}
}
/// Individual constant declaration.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct LetDecl {
name: Identifier,
init: Option<Node>,
}
impl fmt::Display for LetDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.name, f)?;
if let Some(ref init) = self.init {
write!(f, " = {}", init)?;
}
Ok(())
}
}
impl LetDecl {
/// Creates a new variable declaration.
pub(in crate::syntax) fn new<N, I>(name: N, init: I) -> Self
where
N: Into<Identifier>,
I: Into<Option<Node>>,
{
Self {
name: name.into(),
init: init.into(),
}
}
/// Gets the name of the variable.
pub fn name(&self) -> &str {
self.name.as_ref()
}
/// Gets the initialization node for the variable, if any.
pub fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
}

17
boa/src/syntax/ast/node/declaration/mod.rs

@ -0,0 +1,17 @@
//! Declaration nodes
pub mod arrow_function_decl;
pub mod const_decl_list;
pub mod function_decl;
pub mod function_expr;
pub mod let_decl_list;
pub mod var_decl_list;
pub use self::{
arrow_function_decl::ArrowFunctionDecl,
const_decl_list::{ConstDecl, ConstDeclList},
function_decl::FunctionDecl,
function_expr::FunctionExpr,
let_decl_list::{LetDecl, LetDeclList},
var_decl_list::{VarDecl, VarDeclList},
};

144
boa/src/syntax/ast/node/declaration/var_decl_list/mod.rs

@ -0,0 +1,144 @@
use crate::{
environment::lexical_environment::VariableScope,
exec::Executable,
syntax::ast::node::{join_nodes, Identifier, Node},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `var` statement declares a variable, optionally initializing it to a value.
///
/// var declarations, wherever they occur, are processed before any code is executed. This is
/// called hoisting, and is discussed further below.
///
/// The scope of a variable declared with var is its current execution context, which is either
/// the enclosing function or, for variables declared outside any function, global. If you
/// re-declare a JavaScript variable, it will not lose its value.
///
/// Assigning a value to an undeclared variable implicitly creates it as a global variable (it
/// becomes a property of the global object) when the assignment is executed.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-VariableStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct VarDeclList {
#[cfg_attr(feature = "serde", serde(flatten))]
vars: Box<[VarDecl]>,
}
impl Executable for VarDeclList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for var in self.as_ref() {
let val = match var.init() {
Some(v) => v.run(interpreter)?,
None => Value::undefined(),
};
let environment = &mut interpreter.realm_mut().environment;
if environment.has_binding(var.name()) {
if var.init().is_some() {
environment.set_mutable_binding(var.name(), val, true);
}
} else {
environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Function,
);
environment.initialize_binding(var.name(), val);
}
}
Ok(Value::undefined())
}
}
impl<T> From<T> for VarDeclList
where
T: Into<Box<[VarDecl]>>,
{
fn from(list: T) -> Self {
Self { vars: list.into() }
}
}
impl From<VarDecl> for VarDeclList {
fn from(decl: VarDecl) -> Self {
Self {
vars: Box::new([decl]),
}
}
}
impl AsRef<[VarDecl]> for VarDeclList {
fn as_ref(&self) -> &[VarDecl] {
&self.vars
}
}
impl fmt::Display for VarDeclList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.vars.is_empty() {
write!(f, "var ")?;
join_nodes(f, &self.vars)
} else {
Ok(())
}
}
}
impl From<VarDeclList> for Node {
fn from(list: VarDeclList) -> Self {
Self::VarDeclList(list)
}
}
/// Individual variable declaration.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct VarDecl {
name: Identifier,
init: Option<Node>,
}
impl fmt::Display for VarDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.name, f)?;
if let Some(ref init) = self.init {
write!(f, " = {}", init)?;
}
Ok(())
}
}
impl VarDecl {
/// Creates a new variable declaration.
pub(in crate::syntax) fn new<N, I>(name: N, init: I) -> Self
where
N: Into<Identifier>,
I: Into<Option<Node>>,
{
Self {
name: name.into(),
init: init.into(),
}
}
/// Gets the name of the variable.
pub fn name(&self) -> &str {
self.name.as_ref()
}
/// Gets the initialization node for the variable, if any.
pub fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
}

118
boa/src/syntax/ast/node/expression.rs

@ -1,118 +0,0 @@
//! Expression nodes.
use super::{join_nodes, Node};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Calling the function actually performs the specified actions with the indicated parameters.
///
/// Defining a function does not execute it. Defining it simply names the function and
/// specifies what to do when the function is called. Functions must be in scope when they are
/// called, but the function declaration can be hoisted. The scope of a function is the
/// function in which it is declared (or the entire program, if it is declared at the top
/// level).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-CallExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Call {
expr: Box<Node>,
args: Box<[Node]>,
}
impl Call {
/// Creates a new `Call` AST node.
pub fn new<E, A>(expr: E, args: A) -> Self
where
E: Into<Node>,
A: Into<Box<[Node]>>,
{
Self {
expr: Box::new(expr.into()),
args: args.into(),
}
}
/// Gets the name of the function call.
pub fn expr(&self) -> &Node {
&self.expr
}
/// Retrieves the arguments passed to the function.
pub fn args(&self) -> &[Node] {
&self.args
}
}
impl fmt::Display for Call {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}(", self.expr)?;
join_nodes(f, &self.args)?;
f.write_str(")")
}
}
impl From<Call> for Node {
fn from(call: Call) -> Self {
Self::Call(call)
}
}
/// The `new` operator lets developers create an instance of a user-defined object type or of
/// one of the built-in object types that has a constructor function.
///
/// The new keyword does the following things:
/// - Creates a blank, plain JavaScript object;
/// - Links (sets the constructor of) this object to another object;
/// - Passes the newly created object from Step 1 as the this context;
/// - Returns this if the function doesn't return its own object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-NewExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct New {
call: Call,
}
impl New {
/// Gets the name of the function call.
pub fn expr(&self) -> &Node {
&self.call.expr()
}
/// Retrieves the arguments passed to the function.
pub fn args(&self) -> &[Node] {
&self.call.args()
}
}
impl From<Call> for New {
fn from(call: Call) -> Self {
Self { call }
}
}
impl fmt::Display for New {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "new {}", self.call)
}
}
impl From<New> for Node {
fn from(new: New) -> Self {
Self::New(new)
}
}

118
boa/src/syntax/ast/node/expression/call/mod.rs

@ -0,0 +1,118 @@
use crate::{
exec::Executable,
exec::InterpreterState,
syntax::ast::node::{join_nodes, Node},
value::{Type, Value},
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Calling the function actually performs the specified actions with the indicated parameters.
///
/// Defining a function does not execute it. Defining it simply names the function and
/// specifies what to do when the function is called. Functions must be in scope when they are
/// called, but the function declaration can be hoisted. The scope of a function is the
/// function in which it is declared (or the entire program, if it is declared at the top
/// level).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-CallExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Call {
expr: Box<Node>,
args: Box<[Node]>,
}
impl Call {
/// Creates a new `Call` AST node.
pub fn new<E, A>(expr: E, args: A) -> Self
where
E: Into<Node>,
A: Into<Box<[Node]>>,
{
Self {
expr: Box::new(expr.into()),
args: args.into(),
}
}
/// Gets the name of the function call.
pub fn expr(&self) -> &Node {
&self.expr
}
/// Retrieves the arguments passed to the function.
pub fn args(&self) -> &[Node] {
&self.args
}
}
impl Executable for Call {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Call", "exec");
let (this, func) = match self.expr() {
Node::GetConstField(ref get_const_field) => {
let mut obj = get_const_field.obj().run(interpreter)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
}
(obj.clone(), obj.get_field(get_const_field.field()))
}
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(interpreter)?;
let field = get_field.field().run(interpreter)?;
(
obj.clone(),
obj.get_field(field.to_property_key(interpreter)?),
)
}
_ => (
interpreter.realm().global_obj.clone(),
self.expr().run(interpreter)?,
), // 'this' binding should come from the function's self-contained environment
};
let mut v_args = Vec::with_capacity(self.args().len());
for arg in self.args() {
if let Node::Spread(ref x) = arg {
let val = x.run(interpreter)?;
let mut vals = interpreter.extract_array_properties(&val).unwrap();
v_args.append(&mut vals);
break; // after spread we don't accept any new arguments
}
v_args.push(arg.run(interpreter)?);
}
// execute the function call itself
let fnct_result = interpreter.call(&func, &this, &v_args);
// unset the early return flag
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
fnct_result
}
}
impl fmt::Display for Call {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}(", self.expr)?;
join_nodes(f, &self.args)?;
f.write_str(")")
}
}
impl From<Call> for Node {
fn from(call: Call) -> Self {
Self::Call(call)
}
}

6
boa/src/syntax/ast/node/expression/mod.rs

@ -0,0 +1,6 @@
//! Expression nodes
pub mod call;
pub mod new;
pub use self::{call::Call, new::New};

79
boa/src/syntax/ast/node/expression/new/mod.rs

@ -0,0 +1,79 @@
use crate::{
exec::Executable,
syntax::ast::node::{Call, Node},
value::Value,
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `new` operator lets developers create an instance of a user-defined object type or of
/// one of the built-in object types that has a constructor function.
///
/// The new keyword does the following things:
/// - Creates a blank, plain JavaScript object;
/// - Links (sets the constructor of) this object to another object;
/// - Passes the newly created object from Step 1 as the this context;
/// - Returns this if the function doesn't return its own object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-NewExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct New {
call: Call,
}
impl New {
/// Gets the name of the function call.
pub fn expr(&self) -> &Node {
&self.call.expr()
}
/// Retrieves the arguments passed to the function.
pub fn args(&self) -> &[Node] {
&self.call.args()
}
}
impl Executable for New {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("New", "exec");
let func_object = self.expr().run(interpreter)?;
let mut v_args = Vec::with_capacity(self.args().len());
for arg in self.args() {
v_args.push(arg.run(interpreter)?);
}
match func_object {
Value::Object(ref object) => object.construct(&v_args, interpreter),
_ => Ok(Value::undefined()),
}
}
}
impl From<Call> for New {
fn from(call: Call) -> Self {
Self { call }
}
}
impl fmt::Display for New {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "new {}", self.call)
}
}
impl From<New> for Node {
fn from(new: New) -> Self {
Self::New(new)
}
}

83
boa/src/syntax/ast/node/field.rs → boa/src/syntax/ast/node/field/get_const_field/mod.rs

@ -1,6 +1,9 @@
//! Field AST node.
//!
use super::Node;
use crate::{
exec::Executable,
syntax::ast::node::Node,
value::{Type, Value},
Context, Result,
};
use gc::{Finalize, Trace};
use std::fmt;
@ -59,6 +62,17 @@ impl GetConstField {
}
}
impl Executable for GetConstField {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(interpreter)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
}
Ok(obj.get_field(self.field()))
}
}
impl fmt::Display for GetConstField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.obj(), self.field())
@ -70,66 +84,3 @@ impl From<GetConstField> for Node {
Self::GetConstField(get_const_field)
}
}
/// This property accessor provides access to an object's properties by using the
/// [bracket notation][mdn].
///
/// In the object[property_name] syntax, the property_name is just a string or
/// [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a
/// space).
///
/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup
/// table). The keys in this array are the names of the object's properties.
///
/// It's typical when speaking of an object's properties to make a distinction between
/// properties and methods. However, the property/method distinction is little more than a
/// convention. A method is simply a property that can be called (for example, if it has a
/// reference to a Function instance as its value).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-property-accessors
/// [symbol]: https://developer.mozilla.org/en-US/docs/Glossary/Symbol
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Bracket_notation
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct GetField {
obj: Box<Node>,
field: Box<Node>,
}
impl GetField {
pub fn obj(&self) -> &Node {
&self.obj
}
pub fn field(&self) -> &Node {
&self.field
}
/// Creates a `GetField` AST node.
pub fn new<V, F>(value: V, field: F) -> Self
where
V: Into<Node>,
F: Into<Node>,
{
Self {
obj: Box::new(value.into()),
field: Box::new(field.into()),
}
}
}
impl fmt::Display for GetField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}[{}]", self.obj(), self.field())
}
}
impl From<GetField> for Node {
fn from(get_field: GetField) -> Self {
Self::GetField(get_field)
}
}

86
boa/src/syntax/ast/node/field/get_field/mod.rs

@ -0,0 +1,86 @@
use crate::{
exec::Executable,
syntax::ast::node::Node,
value::{Type, Value},
Context, Result,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// This property accessor provides access to an object's properties by using the
/// [bracket notation][mdn].
///
/// In the object[property_name] syntax, the property_name is just a string or
/// [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a
/// space).
///
/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup
/// table). The keys in this array are the names of the object's properties.
///
/// It's typical when speaking of an object's properties to make a distinction between
/// properties and methods. However, the property/method distinction is little more than a
/// convention. A method is simply a property that can be called (for example, if it has a
/// reference to a Function instance as its value).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-property-accessors
/// [symbol]: https://developer.mozilla.org/en-US/docs/Glossary/Symbol
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Bracket_notation
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct GetField {
obj: Box<Node>,
field: Box<Node>,
}
impl GetField {
pub fn obj(&self) -> &Node {
&self.obj
}
pub fn field(&self) -> &Node {
&self.field
}
/// Creates a `GetField` AST node.
pub fn new<V, F>(value: V, field: F) -> Self
where
V: Into<Node>,
F: Into<Node>,
{
Self {
obj: Box::new(value.into()),
field: Box::new(field.into()),
}
}
}
impl Executable for GetField {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(interpreter)?;
if obj.get_type() != Type::Object {
obj = Value::Object(obj.to_object(interpreter)?);
}
let field = self.field().run(interpreter)?;
Ok(obj.get_field(field.to_property_key(interpreter)?))
}
}
impl fmt::Display for GetField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}[{}]", self.obj(), self.field())
}
}
impl From<GetField> for Node {
fn from(get_field: GetField) -> Self {
Self::GetField(get_field)
}
}

6
boa/src/syntax/ast/node/field/mod.rs

@ -0,0 +1,6 @@
//! Field nodes
pub mod get_const_field;
pub mod get_field;
pub use self::{get_const_field::GetConstField, get_field::GetField};

12
boa/src/syntax/ast/node/identifier.rs → boa/src/syntax/ast/node/identifier/mod.rs

@ -1,6 +1,6 @@
//! Local identifier node.
use super::Node;
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use std::fmt;
@ -30,6 +30,16 @@ pub struct Identifier {
ident: Box<str>,
}
impl Executable for Identifier {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.realm()
.environment
.get_binding_value(self.as_ref())
.ok_or_else(|| interpreter.construct_reference_error(self.as_ref()))
}
}
impl fmt::Display for Identifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.ident, f)

333
boa/src/syntax/ast/node/iteration.rs

@ -1,333 +0,0 @@
use super::Node;
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `for` statement creates a loop that consists of three optional expressions.
///
/// A `for` loop repeats until a specified condition evaluates to `false`.
/// The JavaScript for loop is similar to the Java and C for loop.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ForLoop {
#[cfg_attr(feature = "serde", serde(flatten))]
inner: Box<InnerForLoop>,
label: Option<Box<str>>,
}
impl ForLoop {
/// Creates a new for loop AST node.
pub(in crate::syntax) fn new<I, C, E, B>(init: I, condition: C, final_expr: E, body: B) -> Self
where
I: Into<Option<Node>>,
C: Into<Option<Node>>,
E: Into<Option<Node>>,
B: Into<Node>,
{
Self {
inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),
label: None,
}
}
/// Gets the initialization node.
pub fn init(&self) -> Option<&Node> {
self.inner.init()
}
/// Gets the loop condition node.
pub fn condition(&self) -> Option<&Node> {
self.inner.condition()
}
/// Gets the final expression node.
pub fn final_expr(&self) -> Option<&Node> {
self.inner.final_expr()
}
/// Gets the body of the for loop.
pub fn body(&self) -> &Node {
self.inner.body()
}
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
f.write_str("for (")?;
if let Some(init) = self.init() {
fmt::Display::fmt(init, f)?;
}
f.write_str(";")?;
if let Some(condition) = self.condition() {
fmt::Display::fmt(condition, f)?;
}
f.write_str(";")?;
if let Some(final_expr) = self.final_expr() {
fmt::Display::fmt(final_expr, f)?;
}
writeln!(f, ") {{")?;
self.inner.body().display(f, indentation + 1)?;
write!(f, "}}")
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
pub fn set_label(&mut self, label: Box<str>) {
self.label = Some(label);
}
}
impl fmt::Display for ForLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<ForLoop> for Node {
fn from(for_loop: ForLoop) -> Self {
Self::ForLoop(for_loop)
}
}
/// Inner structure to avoid multiple indirections in the heap.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
struct InnerForLoop {
init: Option<Node>,
condition: Option<Node>,
final_expr: Option<Node>,
body: Node,
}
impl InnerForLoop {
/// Creates a new inner for loop.
fn new<I, C, E, B>(init: I, condition: C, final_expr: E, body: B) -> Self
where
I: Into<Option<Node>>,
C: Into<Option<Node>>,
E: Into<Option<Node>>,
B: Into<Node>,
{
Self {
init: init.into(),
condition: condition.into(),
final_expr: final_expr.into(),
body: body.into(),
}
}
/// Gets the initialization node.
fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
/// Gets the loop condition node.
fn condition(&self) -> Option<&Node> {
self.condition.as_ref()
}
/// Gets the final expression node.
fn final_expr(&self) -> Option<&Node> {
self.final_expr.as_ref()
}
/// Gets the body of the for loop.
fn body(&self) -> &Node {
&self.body
}
}
/// The `while` statement creates a loop that executes a specified statement as long as the
/// test condition evaluates to `true`.
///
/// The condition is evaluated before executing the statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct WhileLoop {
cond: Box<Node>,
expr: Box<Node>,
label: Option<Box<str>>,
}
impl WhileLoop {
pub fn cond(&self) -> &Node {
&self.cond
}
pub fn expr(&self) -> &Node {
&self.expr
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
/// Creates a `WhileLoop` AST node.
pub fn new<C, B>(condition: C, body: B) -> Self
where
C: Into<Node>,
B: Into<Node>,
{
Self {
cond: Box::new(condition.into()),
expr: Box::new(body.into()),
label: None,
}
}
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
write!(f, "while ({}) ", self.cond())?;
self.expr().display(f, indentation)
}
}
impl fmt::Display for WhileLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<WhileLoop> for Node {
fn from(while_loop: WhileLoop) -> Self {
Self::WhileLoop(while_loop)
}
}
/// The `do...while` statement creates a loop that executes a specified statement until the
/// test condition evaluates to false.
///
/// The condition is evaluated after executing the statement, resulting in the specified
/// statement executing at least once.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct DoWhileLoop {
body: Box<Node>,
cond: Box<Node>,
label: Option<Box<str>>,
}
impl DoWhileLoop {
pub fn body(&self) -> &Node {
&self.body
}
pub fn cond(&self) -> &Node {
&self.cond
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
/// Creates a `DoWhileLoop` AST node.
pub fn new<B, C>(body: B, condition: C) -> Self
where
B: Into<Node>,
C: Into<Node>,
{
Self {
body: Box::new(body.into()),
cond: Box::new(condition.into()),
label: None,
}
}
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
write!(f, "do")?;
self.body().display(f, indentation)?;
write!(f, "while ({})", self.cond())
}
}
impl fmt::Display for DoWhileLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<DoWhileLoop> for Node {
fn from(do_while: DoWhileLoop) -> Self {
Self::DoWhileLoop(do_while)
}
}
/// The `continue` statement terminates execution of the statements in the current iteration of
/// the current or labeled loop, and continues execution of the loop with the next iteration.
///
/// The continue statement can include an optional label that allows the program to jump to the
/// next iteration of a labeled loop statement instead of the current loop. In this case, the
/// continue statement needs to be nested within this labeled statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Continue {
label: Option<Box<str>>,
}
impl Continue {
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
/// Creates a `Continue` AST node.
pub fn new<OL, L>(label: OL) -> Self
where
L: Into<Box<str>>,
OL: Into<Option<L>>,
{
Self {
label: label.into().map(L::into),
}
}
}
impl fmt::Display for Continue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"continue{}",
if let Some(label) = self.label() {
format!(" {}", label)
} else {
String::new()
}
)
}
}
impl From<Continue> for Node {
fn from(cont: Continue) -> Node {
Self::Continue(cont)
}
}

76
boa/src/syntax/ast/node/iteration/continue_node/mod.rs

@ -0,0 +1,76 @@
use crate::{
exec::{Executable, InterpreterState},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `continue` statement terminates execution of the statements in the current iteration of
/// the current or labeled loop, and continues execution of the loop with the next iteration.
///
/// The continue statement can include an optional label that allows the program to jump to the
/// next iteration of a labeled loop statement instead of the current loop. In this case, the
/// continue statement needs to be nested within this labeled statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Continue {
label: Option<Box<str>>,
}
impl Continue {
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
/// Creates a `Continue` AST node.
pub fn new<OL, L>(label: OL) -> Self
where
L: Into<Box<str>>,
OL: Into<Option<L>>,
{
Self {
label: label.into().map(L::into),
}
}
}
impl Executable for Continue {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.executor()
.set_current_state(InterpreterState::Continue(self.label().map(Box::from)));
Ok(Value::undefined())
}
}
impl fmt::Display for Continue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"continue{}",
if let Some(label) = self.label() {
format!(" {}", label)
} else {
String::new()
}
)
}
}
impl From<Continue> for Node {
fn from(cont: Continue) -> Node {
Self::Continue(cont)
}
}

138
boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs

@ -0,0 +1,138 @@
use crate::{
exec::{Executable, InterpreterState},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `do...while` statement creates a loop that executes a specified statement until the
/// test condition evaluates to false.
///
/// The condition is evaluated after executing the statement, resulting in the specified
/// statement executing at least once.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct DoWhileLoop {
body: Box<Node>,
cond: Box<Node>,
label: Option<Box<str>>,
}
impl DoWhileLoop {
pub fn body(&self) -> &Node {
&self.body
}
pub fn cond(&self) -> &Node {
&self.cond
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
/// Creates a `DoWhileLoop` AST node.
pub fn new<B, C>(body: B, condition: C) -> Self
where
B: Into<Node>,
C: Into<Node>,
{
Self {
body: Box::new(body.into()),
cond: Box::new(condition.into()),
label: None,
}
}
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
write!(f, "do")?;
self.body().display(f, indentation)?;
write!(f, "while ({})", self.cond())
}
}
impl Executable for DoWhileLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
return Ok(result);
}
InterpreterState::Continue(_label) => {
// TODO continue to label;
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
while self.cond().run(interpreter)?.to_boolean() {
result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
}
Ok(result)
}
}
impl fmt::Display for DoWhileLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<DoWhileLoop> for Node {
fn from(do_while: DoWhileLoop) -> Self {
Self::DoWhileLoop(do_while)
}
}

210
boa/src/syntax/ast/node/iteration/for_loop/mod.rs

@ -0,0 +1,210 @@
use crate::{
environment::lexical_environment::new_declarative_environment,
exec::{Executable, InterpreterState},
syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `for` statement creates a loop that consists of three optional expressions.
///
/// A `for` loop repeats until a specified condition evaluates to `false`.
/// The JavaScript for loop is similar to the Java and C for loop.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct ForLoop {
#[cfg_attr(feature = "serde", serde(flatten))]
inner: Box<InnerForLoop>,
label: Option<Box<str>>,
}
impl ForLoop {
/// Creates a new for loop AST node.
pub(in crate::syntax) fn new<I, C, E, B>(init: I, condition: C, final_expr: E, body: B) -> Self
where
I: Into<Option<Node>>,
C: Into<Option<Node>>,
E: Into<Option<Node>>,
B: Into<Node>,
{
Self {
inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),
label: None,
}
}
/// Gets the initialization node.
pub fn init(&self) -> Option<&Node> {
self.inner.init()
}
/// Gets the loop condition node.
pub fn condition(&self) -> Option<&Node> {
self.inner.condition()
}
/// Gets the final expression node.
pub fn final_expr(&self) -> Option<&Node> {
self.inner.final_expr()
}
/// Gets the body of the for loop.
pub fn body(&self) -> &Node {
self.inner.body()
}
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
f.write_str("for (")?;
if let Some(init) = self.init() {
fmt::Display::fmt(init, f)?;
}
f.write_str(";")?;
if let Some(condition) = self.condition() {
fmt::Display::fmt(condition, f)?;
}
f.write_str(";")?;
if let Some(final_expr) = self.final_expr() {
fmt::Display::fmt(final_expr, f)?;
}
writeln!(f, ") {{")?;
self.inner.body().display(f, indentation + 1)?;
write!(f, "}}")
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
pub fn set_label(&mut self, label: Box<str>) {
self.label = Some(label);
}
}
impl Executable for ForLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
// Create the block environment.
let _timer = BoaProfiler::global().start_event("ForLoop", "exec");
{
let env = &mut interpreter.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
}
if let Some(init) = self.init() {
init.run(interpreter)?;
}
while self
.condition()
.map(|cond| cond.run(interpreter).map(|v| v.to_boolean()))
.transpose()?
.unwrap_or(true)
{
let result = self.body().run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, interpreter, break);
break;
}
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, interpreter, continue);
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
if let Some(final_expr) = self.final_expr() {
final_expr.run(interpreter)?;
}
}
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
Ok(Value::undefined())
}
}
impl fmt::Display for ForLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<ForLoop> for Node {
fn from(for_loop: ForLoop) -> Self {
Self::ForLoop(for_loop)
}
}
/// Inner structure to avoid multiple indirections in the heap.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
struct InnerForLoop {
init: Option<Node>,
condition: Option<Node>,
final_expr: Option<Node>,
body: Node,
}
impl InnerForLoop {
/// Creates a new inner for loop.
fn new<I, C, E, B>(init: I, condition: C, final_expr: E, body: B) -> Self
where
I: Into<Option<Node>>,
C: Into<Option<Node>>,
E: Into<Option<Node>>,
B: Into<Node>,
{
Self {
init: init.into(),
condition: condition.into(),
final_expr: final_expr.into(),
body: body.into(),
}
}
/// Gets the initialization node.
fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
/// Gets the loop condition node.
fn condition(&self) -> Option<&Node> {
self.condition.as_ref()
}
/// Gets the final expression node.
fn final_expr(&self) -> Option<&Node> {
self.final_expr.as_ref()
}
/// Gets the body of the for loop.
fn body(&self) -> &Node {
&self.body
}
}

35
boa/src/syntax/ast/node/iteration/mod.rs

@ -0,0 +1,35 @@
//! Iteration nodes
pub use self::{
continue_node::Continue, do_while_loop::DoWhileLoop, for_loop::ForLoop, while_loop::WhileLoop,
};
#[cfg(test)]
mod tests;
// Checking labels for break and continue is the same operation for `ForLoop`, `While` and `DoWhile`
#[macro_use]
macro_rules! handle_state_with_labels {
($self:ident, $label:ident, $interpreter:ident, $state:tt) => {{
if let Some(brk_label) = $label {
if let Some(stmt_label) = $self.label() {
// Break from where we are, keeping "continue" set as the state
if stmt_label != brk_label.as_ref() {
break;
}
} else {
// if a label is set but the current block has no label, break
break;
}
}
$interpreter
.executor()
.set_current_state(InterpreterState::Executing);
}};
}
pub mod continue_node;
pub mod do_while_loop;
pub mod for_loop;
pub mod while_loop;

0
boa/src/exec/iteration/tests.rs → boa/src/syntax/ast/node/iteration/tests.rs

102
boa/src/syntax/ast/node/iteration/while_loop/mod.rs

@ -0,0 +1,102 @@
use crate::{
exec::{Executable, InterpreterState},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The `while` statement creates a loop that executes a specified statement as long as the
/// test condition evaluates to `true`.
///
/// The condition is evaluated before executing the statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct WhileLoop {
cond: Box<Node>,
expr: Box<Node>,
label: Option<Box<str>>,
}
impl WhileLoop {
pub fn cond(&self) -> &Node {
&self.cond
}
pub fn expr(&self) -> &Node {
&self.expr
}
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(Box::as_ref)
}
/// Creates a `WhileLoop` AST node.
pub fn new<C, B>(condition: C, body: B) -> Self
where
C: Into<Node>,
B: Into<Node>,
{
Self {
cond: Box::new(condition.into()),
expr: Box::new(body.into()),
label: None,
}
}
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
write!(f, "while ({}) ", self.cond())?;
self.expr().display(f, indentation)
}
}
impl Executable for WhileLoop {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut result = Value::undefined();
while self.cond().run(interpreter)?.to_boolean() {
result = self.expr().run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Break(label) => {
handle_state_with_labels!(self, label, interpreter, break);
break;
}
InterpreterState::Continue(label) => {
handle_state_with_labels!(self, label, interpreter, continue)
}
InterpreterState::Return => {
return Ok(result);
}
InterpreterState::Executing => {
// Continue execution.
}
}
}
Ok(result)
}
}
impl fmt::Display for WhileLoop {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<WhileLoop> for Node {
fn from(while_loop: WhileLoop) -> Self {
Self::WhileLoop(while_loop)
}
}

54
boa/src/syntax/ast/node/mod.rs

@ -41,6 +41,7 @@ pub use self::{
try_node::{Catch, Finally, Try},
};
use super::Const;
use crate::{exec::Executable, BoaProfiler, Context, Result, Value};
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::{
cmp::Ordering,
@ -240,6 +241,59 @@ impl Node {
}
}
impl Executable for Node {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Executable", "exec");
match *self {
Node::Const(Const::Null) => Ok(Value::null()),
Node::Const(Const::Num(num)) => Ok(Value::rational(num)),
Node::Const(Const::Int(num)) => Ok(Value::integer(num)),
Node::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())),
Node::Const(Const::Undefined) => Ok(Value::Undefined),
// we can't move String from Const into value, because const is a garbage collected value
// Which means Drop() get's called on Const, but str will be gone at that point.
// Do Const values need to be garbage collected? We no longer need them once we've generated Values
Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())),
Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)),
Node::Block(ref block) => block.run(interpreter),
Node::Identifier(ref identifier) => identifier.run(interpreter),
Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(interpreter),
Node::GetField(ref get_field) => get_field.run(interpreter),
Node::Call(ref call) => call.run(interpreter),
Node::WhileLoop(ref while_loop) => while_loop.run(interpreter),
Node::DoWhileLoop(ref do_while) => do_while.run(interpreter),
Node::ForLoop(ref for_loop) => for_loop.run(interpreter),
Node::If(ref if_smt) => if_smt.run(interpreter),
Node::ConditionalOp(ref op) => op.run(interpreter),
Node::Switch(ref switch) => switch.run(interpreter),
Node::Object(ref obj) => obj.run(interpreter),
Node::ArrayDecl(ref arr) => arr.run(interpreter),
// <https://tc39.es/ecma262/#sec-createdynamicfunction>
Node::FunctionDecl(ref decl) => decl.run(interpreter),
// <https://tc39.es/ecma262/#sec-createdynamicfunction>
Node::FunctionExpr(ref function_expr) => function_expr.run(interpreter),
Node::ArrowFunctionDecl(ref decl) => decl.run(interpreter),
Node::BinOp(ref op) => op.run(interpreter),
Node::UnaryOp(ref op) => op.run(interpreter),
Node::New(ref call) => call.run(interpreter),
Node::Return(ref ret) => ret.run(interpreter),
Node::Throw(ref throw) => throw.run(interpreter),
Node::Assign(ref op) => op.run(interpreter),
Node::VarDeclList(ref decl) => decl.run(interpreter),
Node::LetDeclList(ref decl) => decl.run(interpreter),
Node::ConstDeclList(ref decl) => decl.run(interpreter),
Node::Spread(ref spread) => spread.run(interpreter),
Node::This => {
// Will either return `this` binding or undefined
Ok(interpreter.realm().environment.get_this_binding())
}
Node::Try(ref try_node) => try_node.run(interpreter),
Node::Break(ref break_node) => break_node.run(interpreter),
Node::Continue(ref continue_node) => continue_node.run(interpreter),
}
}
}
/// Utility to join multiple Nodes into a single string.
fn join_nodes<N>(f: &mut fmt::Formatter<'_>, nodes: &[N]) -> fmt::Result
where

45
boa/src/syntax/ast/node/object.rs → boa/src/syntax/ast/node/object/mod.rs

@ -1,11 +1,13 @@
//! Object node.
use super::Node;
use crate::{
exec::Executable,
syntax::ast::node::{MethodDefinitionKind, Node, PropertyDefinition},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
use crate::syntax::ast::node::PropertyDefinition;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -41,7 +43,11 @@ impl Object {
}
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indent: usize,
) -> fmt::Result {
f.write_str("{\n")?;
for property in self.properties().iter() {
match property {
@ -64,6 +70,37 @@ impl Object {
}
}
impl Executable for Object {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let global_val = &interpreter
.realm()
.environment
.get_global_object()
.expect("Could not get the global object");
let obj = Value::new_object(Some(global_val));
// TODO: Implement the rest of the property types.
for property in self.properties().iter() {
match property {
PropertyDefinition::Property(key, value) => {
obj.set_field(key.clone(), value.run(interpreter)?);
}
PropertyDefinition::MethodDefinition(kind, name, func) => {
if let MethodDefinitionKind::Ordinary = kind {
obj.set_field(name.clone(), func.run(interpreter)?);
} else {
// TODO: Implement other types of MethodDefinitionKinds.
unimplemented!("other types of property method definitions.");
}
}
i => unimplemented!("{:?} type of property", i),
}
}
Ok(obj)
}
}
impl fmt::Display for Object {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)

168
boa/src/syntax/ast/node/operator.rs

@ -1,168 +0,0 @@
use super::Node;
use crate::syntax::ast::op;
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// An assignment operator assigns a value to its left operand based on the value of its right
/// operand.
///
/// Assignment operator (`=`), assigns the value of its right operand to its left operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Assign {
lhs: Box<Node>,
rhs: Box<Node>,
}
impl Assign {
/// Creates an `Assign` AST node.
pub(in crate::syntax) fn new<L, R>(lhs: L, rhs: R) -> Self
where
L: Into<Node>,
R: Into<Node>,
{
Self {
lhs: Box::new(lhs.into()),
rhs: Box::new(rhs.into()),
}
}
/// Gets the left hand side of the assignment operation.
pub fn lhs(&self) -> &Node {
&self.lhs
}
/// Gets the right hand side of the assignment operation.
pub fn rhs(&self) -> &Node {
&self.rhs
}
}
impl fmt::Display for Assign {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} = {}", self.lhs, self.rhs)
}
}
impl From<Assign> for Node {
fn from(op: Assign) -> Self {
Self::Assign(op)
}
}
/// Binary operators requires two operands, one before the operator and one after the operator.
///
/// More information:
/// - [MDN documentation][mdn]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Operators
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct BinOp {
op: op::BinOp,
lhs: Box<Node>,
rhs: Box<Node>,
}
impl BinOp {
/// Creates a `BinOp` AST node.
pub(in crate::syntax) fn new<O, L, R>(op: O, lhs: L, rhs: R) -> Self
where
O: Into<op::BinOp>,
L: Into<Node>,
R: Into<Node>,
{
Self {
op: op.into(),
lhs: Box::new(lhs.into()),
rhs: Box::new(rhs.into()),
}
}
/// Gets the binary operation of the node.
pub fn op(&self) -> op::BinOp {
self.op
}
/// Gets the left hand side of the binary operation.
pub fn lhs(&self) -> &Node {
&self.lhs
}
/// Gets the right hand side of the binary operation.
pub fn rhs(&self) -> &Node {
&self.rhs
}
}
impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.lhs, self.op, self.rhs)
}
}
impl From<BinOp> for Node {
fn from(op: BinOp) -> Self {
Self::BinOp(op)
}
}
/// A unary operation is an operation with only one operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct UnaryOp {
op: op::UnaryOp,
target: Box<Node>,
}
impl UnaryOp {
/// Creates a new `UnaryOp` AST node.
pub(in crate::syntax) fn new<V>(op: op::UnaryOp, target: V) -> Self
where
V: Into<Node>,
{
Self {
op,
target: Box::new(target.into()),
}
}
/// Gets the unary operation of the node.
pub fn op(&self) -> op::UnaryOp {
self.op
}
/// Gets the target of this unary operator.
pub fn target(&self) -> &Node {
self.target.as_ref()
}
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.op, self.target)
}
}
impl From<UnaryOp> for Node {
fn from(op: UnaryOp) -> Self {
Self::UnaryOp(op)
}
}

99
boa/src/syntax/ast/node/operator/assign/mod.rs

@ -0,0 +1,99 @@
use crate::{
environment::lexical_environment::VariableScope, exec::Executable, syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// An assignment operator assigns a value to its left operand based on the value of its right
/// operand.
///
/// Assignment operator (`=`), assigns the value of its right operand to its left operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Assign {
lhs: Box<Node>,
rhs: Box<Node>,
}
impl Assign {
/// Creates an `Assign` AST node.
pub(in crate::syntax) fn new<L, R>(lhs: L, rhs: R) -> Self
where
L: Into<Node>,
R: Into<Node>,
{
Self {
lhs: Box::new(lhs.into()),
rhs: Box::new(rhs.into()),
}
}
/// Gets the left hand side of the assignment operation.
pub fn lhs(&self) -> &Node {
&self.lhs
}
/// Gets the right hand side of the assignment operation.
pub fn rhs(&self) -> &Node {
&self.rhs
}
}
impl Executable for Assign {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Assign", "exec");
let val = self.rhs().run(interpreter)?;
match self.lhs() {
Node::Identifier(ref name) => {
let environment = &mut interpreter.realm_mut().environment;
if environment.has_binding(name.as_ref()) {
// Binding already exists
environment.set_mutable_binding(name.as_ref(), val.clone(), true);
} else {
environment.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
);
environment.initialize_binding(name.as_ref(), val.clone());
}
}
Node::GetConstField(ref get_const_field) => {
let val_obj = get_const_field.obj().run(interpreter)?;
val_obj.set_field(get_const_field.field(), val.clone());
}
Node::GetField(ref get_field) => {
let object = get_field.obj().run(interpreter)?;
let field = get_field.field().run(interpreter)?;
let key = field.to_property_key(interpreter)?;
object.set_field(key, val.clone());
}
_ => (),
}
Ok(val)
}
}
impl fmt::Display for Assign {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} = {}", self.lhs, self.rhs)
}
}
impl From<Assign> for Node {
fn from(op: Assign) -> Self {
Self::Assign(op)
}
}

192
boa/src/exec/operator/mod.rs → boa/src/syntax/ast/node/operator/bin_op/mod.rs

@ -1,50 +1,77 @@
//! Operator execution.
#[cfg(test)]
mod tests;
use super::{Context, Executable};
use crate::{
environment::lexical_environment::VariableScope,
exec::Executable,
syntax::ast::{
node::{Assign, BinOp, Node, UnaryOp},
node::Node,
op::{self, AssignOp, BitOp, CompOp, LogOp, NumOp},
},
BoaProfiler, Result, Value,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
impl Executable for Assign {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Assign", "exec");
let val = self.rhs().run(interpreter)?;
match self.lhs() {
Node::Identifier(ref name) => {
let environment = &mut interpreter.realm_mut().environment;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
if environment.has_binding(name.as_ref()) {
// Binding already exists
environment.set_mutable_binding(name.as_ref(), val.clone(), true);
} else {
environment.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
);
environment.initialize_binding(name.as_ref(), val.clone());
}
}
Node::GetConstField(ref get_const_field) => {
let val_obj = get_const_field.obj().run(interpreter)?;
val_obj.set_field(get_const_field.field(), val.clone());
}
Node::GetField(ref get_field) => {
let object = get_field.obj().run(interpreter)?;
let field = get_field.field().run(interpreter)?;
let key = field.to_property_key(interpreter)?;
object.set_field(key, val.clone());
}
_ => (),
/// Binary operators requires two operands, one before the operator and one after the operator.
///
/// More information:
/// - [MDN documentation][mdn]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Operators
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct BinOp {
op: op::BinOp,
lhs: Box<Node>,
rhs: Box<Node>,
}
impl BinOp {
/// Creates a `BinOp` AST node.
pub(in crate::syntax) fn new<O, L, R>(op: O, lhs: L, rhs: R) -> Self
where
O: Into<op::BinOp>,
L: Into<Node>,
R: Into<Node>,
{
Self {
op: op.into(),
lhs: Box::new(lhs.into()),
rhs: Box::new(rhs.into()),
}
}
/// Gets the binary operation of the node.
pub fn op(&self) -> op::BinOp {
self.op
}
/// Gets the left hand side of the binary operation.
pub fn lhs(&self) -> &Node {
&self.lhs
}
/// Gets the right hand side of the binary operation.
pub fn rhs(&self) -> &Node {
&self.rhs
}
/// Runs the assignment operators.
fn run_assign(op: AssignOp, x: Value, y: Value, interpreter: &mut Context) -> Result<Value> {
match op {
AssignOp::Add => x.add(&y, interpreter),
AssignOp::Sub => x.sub(&y, interpreter),
AssignOp::Mul => x.mul(&y, interpreter),
AssignOp::Exp => x.pow(&y, interpreter),
AssignOp::Div => x.div(&y, interpreter),
AssignOp::Mod => x.rem(&y, interpreter),
AssignOp::And => x.bitand(&y, interpreter),
AssignOp::Or => x.bitor(&y, interpreter),
AssignOp::Xor => x.bitxor(&y, interpreter),
AssignOp::Shl => x.shl(&y, interpreter),
AssignOp::Shr => x.shr(&y, interpreter),
AssignOp::Ushr => x.ushr(&y, interpreter),
}
Ok(val)
}
}
@ -158,89 +185,14 @@ impl Executable for BinOp {
}
}
impl BinOp {
/// Runs the assignment operators.
fn run_assign(op: AssignOp, x: Value, y: Value, interpreter: &mut Context) -> Result<Value> {
match op {
AssignOp::Add => x.add(&y, interpreter),
AssignOp::Sub => x.sub(&y, interpreter),
AssignOp::Mul => x.mul(&y, interpreter),
AssignOp::Exp => x.pow(&y, interpreter),
AssignOp::Div => x.div(&y, interpreter),
AssignOp::Mod => x.rem(&y, interpreter),
AssignOp::And => x.bitand(&y, interpreter),
AssignOp::Or => x.bitor(&y, interpreter),
AssignOp::Xor => x.bitxor(&y, interpreter),
AssignOp::Shl => x.shl(&y, interpreter),
AssignOp::Shr => x.shr(&y, interpreter),
AssignOp::Ushr => x.ushr(&y, interpreter),
}
impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.lhs, self.op, self.rhs)
}
}
impl Executable for UnaryOp {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let x = self.target().run(interpreter)?;
Ok(match self.op() {
op::UnaryOp::Minus => x.neg(interpreter)?,
op::UnaryOp::Plus => Value::from(x.to_number(interpreter)?),
op::UnaryOp::IncrementPost => {
let ret = x.clone();
let result = x.to_number(interpreter)? + 1.0;
interpreter.set_value(self.target(), result.into())?;
ret
}
op::UnaryOp::IncrementPre => {
let result = x.to_number(interpreter)? + 1.0;
interpreter.set_value(self.target(), result.into())?
}
op::UnaryOp::DecrementPost => {
let ret = x.clone();
let result = x.to_number(interpreter)? - 1.0;
interpreter.set_value(self.target(), result.into())?;
ret
}
op::UnaryOp::DecrementPre => {
let result = x.to_number(interpreter)? - 1.0;
interpreter.set_value(self.target(), result.into())?
}
op::UnaryOp::Not => x.not(interpreter)?.into(),
op::UnaryOp::Tilde => {
let num_v_a = x.to_number(interpreter)?;
Value::from(if num_v_a.is_nan() {
-1
} else {
// TODO: this is not spec compliant.
!(num_v_a as i32)
})
}
op::UnaryOp::Void => Value::undefined(),
op::UnaryOp::Delete => match *self.target() {
Node::GetConstField(ref get_const_field) => Value::boolean(
get_const_field
.obj()
.run(interpreter)?
.remove_property(get_const_field.field()),
),
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(interpreter)?;
let field = &get_field.field().run(interpreter)?;
let res = obj.remove_property(field.to_string(interpreter)?.as_str());
return Ok(Value::boolean(res));
}
Node::Identifier(_) => Value::boolean(false),
Node::ArrayDecl(_)
| Node::Block(_)
| Node::Const(_)
| Node::FunctionDecl(_)
| Node::FunctionExpr(_)
| Node::New(_)
| Node::Object(_)
| Node::UnaryOp(_) => Value::boolean(true),
_ => panic!("SyntaxError: wrong delete argument {}", self),
},
op::UnaryOp::TypeOf => Value::from(x.get_type().as_str()),
})
impl From<BinOp> for Node {
fn from(op: BinOp) -> Self {
Self::BinOp(op)
}
}

10
boa/src/syntax/ast/node/operator/mod.rs

@ -0,0 +1,10 @@
//! Operator nodes
pub mod assign;
pub mod bin_op;
pub mod unary_op;
pub use self::{assign::Assign, bin_op::BinOp, unary_op::UnaryOp};
#[cfg(test)]
mod tests;

0
boa/src/exec/operator/tests.rs → boa/src/syntax/ast/node/operator/tests.rs

127
boa/src/syntax/ast/node/operator/unary_op/mod.rs

@ -0,0 +1,127 @@
use crate::{
exec::Executable,
syntax::ast::{node::Node, op},
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A unary operation is an operation with only one operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct UnaryOp {
op: op::UnaryOp,
target: Box<Node>,
}
impl UnaryOp {
/// Creates a new `UnaryOp` AST node.
pub(in crate::syntax) fn new<V>(op: op::UnaryOp, target: V) -> Self
where
V: Into<Node>,
{
Self {
op,
target: Box::new(target.into()),
}
}
/// Gets the unary operation of the node.
pub fn op(&self) -> op::UnaryOp {
self.op
}
/// Gets the target of this unary operator.
pub fn target(&self) -> &Node {
self.target.as_ref()
}
}
impl Executable for UnaryOp {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let x = self.target().run(interpreter)?;
Ok(match self.op() {
op::UnaryOp::Minus => x.neg(interpreter)?,
op::UnaryOp::Plus => Value::from(x.to_number(interpreter)?),
op::UnaryOp::IncrementPost => {
let ret = x.clone();
let result = x.to_number(interpreter)? + 1.0;
interpreter.set_value(self.target(), result.into())?;
ret
}
op::UnaryOp::IncrementPre => {
let result = x.to_number(interpreter)? + 1.0;
interpreter.set_value(self.target(), result.into())?
}
op::UnaryOp::DecrementPost => {
let ret = x.clone();
let result = x.to_number(interpreter)? - 1.0;
interpreter.set_value(self.target(), result.into())?;
ret
}
op::UnaryOp::DecrementPre => {
let result = x.to_number(interpreter)? - 1.0;
interpreter.set_value(self.target(), result.into())?
}
op::UnaryOp::Not => x.not(interpreter)?.into(),
op::UnaryOp::Tilde => {
let num_v_a = x.to_number(interpreter)?;
Value::from(if num_v_a.is_nan() {
-1
} else {
// TODO: this is not spec compliant.
!(num_v_a as i32)
})
}
op::UnaryOp::Void => Value::undefined(),
op::UnaryOp::Delete => match *self.target() {
Node::GetConstField(ref get_const_field) => Value::boolean(
get_const_field
.obj()
.run(interpreter)?
.remove_property(get_const_field.field()),
),
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(interpreter)?;
let field = &get_field.field().run(interpreter)?;
let res = obj.remove_property(field.to_string(interpreter)?.as_str());
return Ok(Value::boolean(res));
}
Node::Identifier(_) => Value::boolean(false),
Node::ArrayDecl(_)
| Node::Block(_)
| Node::Const(_)
| Node::FunctionDecl(_)
| Node::FunctionExpr(_)
| Node::New(_)
| Node::Object(_)
| Node::UnaryOp(_) => Value::boolean(true),
_ => panic!("SyntaxError: wrong delete argument {}", self),
},
op::UnaryOp::TypeOf => Value::from(x.get_type().as_str()),
})
}
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.op, self.target)
}
}
impl From<UnaryOp> for Node {
fn from(op: UnaryOp) -> Self {
Self::UnaryOp(op)
}
}

20
boa/src/syntax/ast/node/return_smt.rs → boa/src/syntax/ast/node/return_smt/mod.rs

@ -1,4 +1,8 @@
use super::Node;
use crate::{
exec::{Executable, InterpreterState},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
@ -53,6 +57,20 @@ impl Return {
}
}
impl Executable for Return {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let result = match self.expr() {
Some(ref v) => v.run(interpreter),
None => Ok(Value::undefined()),
};
// Set flag for return
interpreter
.executor()
.set_current_state(InterpreterState::Return);
result
}
}
impl From<Return> for Node {
fn from(return_smt: Return) -> Node {
Node::Return(return_smt)

9
boa/src/syntax/ast/node/spread.rs → boa/src/syntax/ast/node/spread/mod.rs

@ -1,4 +1,4 @@
use super::Node;
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use std::fmt;
@ -44,6 +44,13 @@ impl Spread {
}
}
impl Executable for Spread {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
// TODO: for now we can do nothing but return the value as-is
self.val().run(interpreter)
}
}
impl fmt::Display for Spread {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "...{}", self.val())

50
boa/src/syntax/ast/node/statement_list.rs → boa/src/syntax/ast/node/statement_list/mod.rs

@ -1,6 +1,10 @@
//! Statement list node.
use super::Node;
use crate::{
exec::{Executable, InterpreterState},
syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt;
use std::ops::Deref;
@ -31,7 +35,11 @@ impl StatementList {
}
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
let indent = " ".repeat(indentation);
// Print statements
for node in self.statements.iter() {
@ -48,6 +56,44 @@ impl StatementList {
}
}
impl Executable for StatementList {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("StatementList", "exec");
// https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
// The return value is uninitialized, which means it defaults to Value::Undefined
let mut obj = Value::default();
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in self.statements().iter().enumerate() {
let val = item.run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
obj = val;
break;
}
InterpreterState::Break(_label) => {
// Early break.
break;
}
InterpreterState::Continue(_label) => {
break;
}
InterpreterState::Executing => {
// Continue execution
}
}
if i + 1 == self.statements().len() {
obj = val;
}
}
Ok(obj)
}
}
impl<T> From<T> for StatementList
where
T: Into<Box<[Node]>>,

93
boa/src/syntax/ast/node/switch.rs → boa/src/syntax/ast/node/switch/mod.rs

@ -1,6 +1,10 @@
//! Switch node.
//!
use super::Node;
use crate::{
exec::{Executable, InterpreterState},
syntax::ast::node::Node,
Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
@ -9,6 +13,9 @@ use crate::syntax::ast::node::StatementList;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct Case {
@ -95,7 +102,11 @@ impl Switch {
}
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indent: usize,
) -> fmt::Result {
writeln!(f, "switch ({}) {{", self.val())?;
for e in self.cases().iter() {
writeln!(f, "{}case {}:", indent, e.condition())?;
@ -110,6 +121,84 @@ impl Switch {
}
}
impl Executable for Switch {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let val = self.val().run(interpreter)?;
let mut result = Value::null();
let mut matched = false;
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// If a case block does not end with a break statement then subsequent cases will be run without
// checking their conditions until a break is encountered.
let mut fall_through: bool = false;
for case in self.cases().iter() {
let cond = case.condition();
let block = case.body();
if fall_through || val.strict_equals(&cond.run(interpreter)?) {
matched = true;
let result = block.run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
return Ok(result);
}
InterpreterState::Break(_label) => {
// TODO, break to a label.
// Break statement encountered so therefore end switch statement.
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO, continue to a label.
break;
}
InterpreterState::Executing => {
// Continuing execution / falling through to next case statement(s).
fall_through = true;
}
}
}
}
if !matched {
if let Some(default) = self.default() {
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in default.iter().enumerate() {
let val = item.run(interpreter)?;
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
result = val;
break;
}
InterpreterState::Break(_label) => {
// TODO, break to a label.
// Early break.
break;
}
_ => {
// Continue execution
}
}
if i == default.len() - 1 {
result = val;
}
}
}
}
Ok(result)
}
}
impl fmt::Display for Switch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)

0
boa/src/exec/switch/tests.rs → boa/src/syntax/ast/node/switch/tests.rs

9
boa/src/syntax/ast/node/throw.rs → boa/src/syntax/ast/node/throw/mod.rs

@ -1,4 +1,4 @@
use super::Node;
use crate::{exec::Executable, syntax::ast::node::Node, Context, Result, Value};
use gc::{Finalize, Trace};
use std::fmt;
@ -41,6 +41,13 @@ impl Throw {
}
}
impl Executable for Throw {
#[inline]
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Err(self.expr().run(interpreter)?)
}
}
impl fmt::Display for Throw {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "throw {}", self.expr)

60
boa/src/syntax/ast/node/try_node.rs → boa/src/syntax/ast/node/try_node/mod.rs

@ -1,10 +1,18 @@
use super::{Block, Identifier, Node};
use crate::{
environment::lexical_environment::{new_declarative_environment, VariableScope},
exec::Executable,
syntax::ast::node::{Block, Identifier, Node},
BoaProfiler, Context, Result, Value,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// The `try...catch` statement marks a block of statements to try and specifies a response
/// should an exception be thrown.
///
@ -64,7 +72,11 @@ impl Try {
}
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
write!(f, "{}try ", " ".repeat(indentation))?;
self.block.display(f, indentation)?;
@ -79,6 +91,50 @@ impl Try {
}
}
impl Executable for Try {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Try", "exec");
let res = self.block().run(interpreter).map_or_else(
|err| {
if let Some(catch) = self.catch() {
{
let env = &mut interpreter.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
if let Some(param) = catch.parameter() {
env.create_mutable_binding(
param.to_owned(),
false,
VariableScope::Block,
);
env.initialize_binding(param, err);
}
}
let res = catch.block().run(interpreter);
// pop the block env
let _ = interpreter.realm_mut().environment.pop();
res
} else {
Err(err)
}
},
Ok,
);
if let Some(finally) = self.finally() {
finally.run(interpreter)?;
}
res
}
}
impl fmt::Display for Try {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)

0
boa/src/exec/try_node/tests.rs → boa/src/syntax/ast/node/try_node/tests.rs

Loading…
Cancel
Save