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::{ |
||||||
use crate::{exec::Executable, syntax::ast::node::Break}; |
exec::{Executable, InterpreterState}, |
||||||
|
syntax::ast::node::Break, |
||||||
|
Context, |
||||||
|
}; |
||||||
|
|
||||||
#[test] |
#[test] |
||||||
fn check_post_state() { |
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