mirror of https://github.com/boa-dev/boa.git
George Roman
4 years ago
committed by
GitHub
64 changed files with 2474 additions and 2316 deletions
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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()) |
||||
} |
||||
} |
@ -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 |
||||
} |
||||
} |
@ -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)? |
||||
}) |
||||
} |
||||
} |
@ -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, |
||||
)) |
||||
} |
||||
} |
@ -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)?)) |
||||
} |
||||
} |
@ -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())) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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()), |
||||
} |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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 |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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)?) |
||||
} |
||||
} |
@ -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 |
||||
} |
||||
} |
@ -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() { |
@ -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) |
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
//! Conditional nodes
|
||||
|
||||
pub mod conditional_op; |
||||
pub mod if_node; |
||||
|
||||
pub use self::{conditional_op::ConditionalOp, if_node::If}; |
@ -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() |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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 |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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() |
||||
} |
||||
} |
@ -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}, |
||||
}; |
@ -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() |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
//! Expression nodes
|
||||
|
||||
pub mod call; |
||||
pub mod new; |
||||
|
||||
pub use self::{call::Call, new::New}; |
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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}; |
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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 |
||||
} |
||||
} |
@ -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,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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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,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) |
||||
} |
||||
} |
Loading…
Reference in new issue