Skip to content

Commit

Permalink
Added RangeError generation and modularized New and Call nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
Razican committed May 23, 2020
1 parent 2d94859 commit 005f9b1
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 177 deletions.
2 changes: 1 addition & 1 deletion boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl Array {
}
}
1 if args[0].is_double() => {
return Err(RangeError::run_new("invalid array length", ctx));
return Err(RangeError::run_new("invalid array length", ctx)?);
}
_ => {
for (n, value) in args.iter().enumerate() {
Expand Down
16 changes: 14 additions & 2 deletions boa/src/builtins/error/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,23 @@ impl RangeError {
}

/// Runs a `new RangeError(message)`.
pub(crate) fn run_new<M>(message: M, interpreter: &mut Interpreter) -> Value
pub(crate) fn run_new<M>(message: M, interpreter: &mut Interpreter) -> ResultValue
where
M: Into<String>,
{
unimplemented!()
use crate::{
exec::Executable,
syntax::ast::{
node::{Call, Identifier, New},
Const,
},
};

New::from(Call::new(
Identifier::from("RangeError"),
vec![Const::from(message.into()).into()],
))
.run(interpreter)
}

/// Initialise the global object with the `RangeError` object.
Expand Down
5 changes: 0 additions & 5 deletions boa/src/builtins/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
//! Builtins live here, such as Object, String, Math etc
use crate::builtins::{
function::{Function, FunctionBody, NativeFunctionData},
object::Object,
};

pub mod array;
pub mod bigint;
pub mod boolean;
Expand Down
81 changes: 81 additions & 0 deletions boa/src/exec/expression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Expression execution.
use super::{Executable, Interpreter};
use crate::{
builtins::{
object::{INSTANCE_PROTOTYPE, PROTOTYPE},
value::{ResultValue, Value, ValueData},
},
syntax::ast::node::{Call, New, Node},
};

impl Executable for Call {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let (mut this, func) = match self.expr() {
Node::GetConstField(ref obj, ref field) => {
let mut obj = obj.run(interpreter)?;
if obj.get_type() != "object" || obj.get_type() != "symbol" {
obj = interpreter
.to_object(&obj)
.expect("failed to convert to object");
}
(obj.clone(), obj.get_field(field))
}
Node::GetField(ref obj, ref field) => {
let obj = obj.run(interpreter)?;
let field = field.run(interpreter)?;
(obj.clone(), obj.get_field(field.to_string()))
}
_ => (
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, &mut this, &v_args);

// unset the early return flag
interpreter.is_return = false;

fnct_result
}
}

impl Executable for New {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
// let (callee, args) = match call.as_ref() {
// Node::Call(callee, args) => (callee, args),
// _ => unreachable!("Node::New(ref call): 'call' must only be Node::Call type."),
// };

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)?);
}
let mut this = Value::new_object(None);
// Create a blank object, then set its __proto__ property to the [Constructor].prototype
this.set_internal_slot(INSTANCE_PROTOTYPE, func_object.get_field(PROTOTYPE));

match func_object.data() {
ValueData::Object(ref o) => o.clone().borrow_mut().func.as_ref().unwrap().construct(
&mut func_object.clone(),
&v_args,
interpreter,
&mut this,
),
_ => Ok(Value::undefined()),
}
}
}
73 changes: 3 additions & 70 deletions boa/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod array;
mod block;
mod declaration;
mod expression;
mod iteration;
mod operator;
mod statement_list;
Expand Down Expand Up @@ -391,49 +392,7 @@ impl Executable for Node {
let val_field = field.run(interpreter)?;
Ok(val_obj.borrow().get_field(val_field.borrow().to_string()))
}
Node::Call(ref callee, ref args) => {
let (mut this, func) = match callee.deref() {
Node::GetConstField(ref obj, ref field) => {
let mut obj = obj.run(interpreter)?;
if obj.get_type() != "object" || obj.get_type() != "symbol" {
obj = interpreter
.to_object(&obj)
.expect("failed to convert to object");
}
(obj.clone(), obj.borrow().get_field(field))
}
Node::GetField(ref obj, ref field) => {
let obj = obj.run(interpreter)?;
let field = field.run(interpreter)?;
(
obj.clone(),
obj.borrow().get_field(field.borrow().to_string()),
)
}
_ => (
interpreter.realm().global_obj.clone(),
callee.run(interpreter)?,
), // 'this' binding should come from the function's self-contained environment
};
let mut v_args = Vec::with_capacity(args.len());
for arg in args.iter() {
if let Node::Spread(ref x) = arg.deref() {
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, &mut this, &v_args);

// unset the early return flag
interpreter.is_return = false;

fnct_result
}
Node::Call(ref expr) => expr.run(interpreter),
Node::WhileLoop(ref cond, ref expr) => {
let mut result = Value::undefined();
while cond.run(interpreter)?.borrow().is_true() {
Expand Down Expand Up @@ -526,33 +485,7 @@ impl Executable for Node {
Node::ArrowFunctionDecl(ref decl) => decl.run(interpreter),
Node::BinOp(ref op) => op.run(interpreter),
Node::UnaryOp(ref op) => op.run(interpreter),
Node::New(ref call) => {
let (callee, args) = match call.as_ref() {
Node::Call(callee, args) => (callee, args),
_ => unreachable!("Node::New(ref call): 'call' must only be Node::Call type."),
};

let func_object = callee.run(interpreter)?;
let mut v_args = Vec::with_capacity(args.len());
for arg in args.iter() {
v_args.push(arg.run(interpreter)?);
}
let mut this = Value::new_object(None);
// Create a blank object, then set its __proto__ property to the [Constructor].prototype
this.borrow().set_internal_slot(
INSTANCE_PROTOTYPE,
func_object.borrow().get_field(PROTOTYPE),
);

match *(func_object.borrow()).deref() {
ValueData::Object(ref o) => (*o.deref().clone().borrow_mut())
.func
.as_ref()
.unwrap()
.construct(&mut func_object.clone(), &v_args, interpreter, &mut this),
_ => Ok(Value::undefined()),
}
}
Node::New(ref call) => call.run(interpreter),
Node::Return(ref ret) => {
let result = match *ret {
Some(ref v) => v.run(interpreter),
Expand Down
118 changes: 118 additions & 0 deletions boa/src/syntax/ast/node/expression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//! 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)
}
}
Loading

0 comments on commit 005f9b1

Please sign in to comment.