Skip to content

Commit

Permalink
[ReferenceError] complete solution (#488)
Browse files Browse the repository at this point in the history
  • Loading branch information
croraf authored Jun 16, 2020
1 parent b43e92a commit 4ae939a
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 31 deletions.
3 changes: 2 additions & 1 deletion boa/src/builtins/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ use crate::{

// mod eval;
pub(crate) mod range;
// mod reference;
pub(crate) mod reference;
// mod syntax;
pub(crate) mod r#type;
// mod uri;

pub(crate) use self::r#type::TypeError;
pub(crate) use self::range::RangeError;
pub(crate) use self::reference::ReferenceError;

/// Built-in `Error` object.
#[derive(Debug, Clone, Copy)]
Expand Down
91 changes: 91 additions & 0 deletions boa/src/builtins/error/reference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! This module implements the global `ReferenceError` object.
//!
//! Indicates an error that occurs when de-referencing an invalid reference
//!
//! More information:
//! - [MDN documentation][mdn]
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError
use crate::{
builtins::{
function::make_builtin_fn,
function::make_constructor_fn,
object::ObjectData,
value::{ResultValue, Value},
},
exec::Interpreter,
profiler::BoaProfiler,
};

#[derive(Debug, Clone, Copy)]
pub(crate) struct ReferenceError;

impl ReferenceError {
/// The name of the object.
pub(crate) const NAME: &'static str = "ReferenceError";

/// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1;

/// Create a new error object.
pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() {
this.set_field(
"message",
Value::from(
args.get(0)
.expect("failed getting error message")
.to_string(),
),
);
}
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::Error);
Err(this.clone())
}

/// `Error.prototype.toString()`
///
/// The toString() method returns a string representing the specified Error object.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name");
let message = this.get_field("message");
Ok(Value::from(format!("{}: {}", name, message)))
}

/// Create a new `ReferenceError` object.
pub(crate) fn create(global: &Value) -> Value {
let prototype = Value::new_object(Some(global));
prototype.set_field("message", Value::from(""));

make_builtin_fn(Self::to_string, "toString", &prototype, 0);

make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_error,
global,
prototype,
true,
)
}

/// Initialise the global object with the `ReferenceError` object.
pub(crate) fn init(global: &Value) -> (&str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");

(Self::NAME, Self::create(global))
}
}
3 changes: 2 additions & 1 deletion boa/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub(crate) use self::{
array::Array,
bigint::BigInt,
boolean::Boolean,
error::{Error, RangeError, TypeError},
error::{Error, RangeError, ReferenceError, TypeError},
global_this::GlobalThis,
infinity::Infinity,
json::Json,
Expand Down Expand Up @@ -56,6 +56,7 @@ pub fn init(global: &Value) {
// Global error types.
Error::init(global),
RangeError::init(global),
ReferenceError::init(global),
TypeError::init(global),
// Global properties.
NaN::init(global),
Expand Down
20 changes: 15 additions & 5 deletions boa/src/environment/lexical_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,15 @@ mod tests {
{
let bar = "bar";
}
bar == undefined;
try{
bar;
} catch (err) {
err.message
}
"#;

assert_eq!(&exec(scenario), "true");
assert_eq!(&exec(scenario), "bar is not defined");
}

#[test]
Expand All @@ -307,10 +312,15 @@ mod tests {
{
const bar = "bar";
}
bar == undefined;
try{
bar;
} catch (err) {
err.message
}
"#;

assert_eq!(&exec(scenario), "true");
// awaiting agreement on error throw testing
assert_eq!(&exec(scenario), "bar is not defined");
}

#[test]
Expand Down
9 changes: 7 additions & 2 deletions boa/src/exec/exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,16 @@ impl Interpreter {
}

/// Constructs a `ReferenceError` with the specified message.
pub fn construct_reference_error<M>(&mut self, _message: M) -> Value
pub fn construct_reference_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
unimplemented!("ReferenceError: is not implemented");
New::from(Call::new(
Identifier::from("ReferenceError"),
vec![Const::from(message.into() + " is not defined").into()],
))
.run(self)
.expect_err("ReferenceError should always throw")
}

/// Throws a `ReferenceError` with the specified message.
Expand Down
21 changes: 21 additions & 0 deletions boa/src/exec/identifier/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use super::{Executable, Interpreter};
use crate::{
builtins::value::{ResultValue, Value, ValueData},
syntax::ast::node::identifier::Identifier,
};

impl Executable for Identifier {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let reference = resolve_binding(interpreter, self.as_ref());
match reference.data() {
ValueData::Undefined => Err(interpreter
.throw_reference_error(self.as_ref())
.expect_err("throw_reference_error() must return an error")),
_ => Ok(reference),
}
}
}

pub(crate) fn resolve_binding(interpreter: &mut Interpreter, name: &str) -> Value {
interpreter.realm().environment.get_binding_value(name)
}
9 changes: 2 additions & 7 deletions boa/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod declaration;
mod exception;
mod expression;
mod field;
mod identifier;
mod iteration;
mod object;
mod operator;
Expand Down Expand Up @@ -582,13 +583,7 @@ impl Executable for Node {
Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())),
Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)),
Node::Block(ref block) => block.run(interpreter),
Node::Identifier(ref name) => {
let val = interpreter
.realm()
.environment
.get_binding_value(name.as_ref());
Ok(val)
}
Node::Identifier(ref identifier) => identifier.run(interpreter),
Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(interpreter),
Node::GetField(ref get_field) => get_field.run(interpreter),
Node::Call(ref expr) => expr.run(interpreter),
Expand Down
Loading

0 comments on commit 4ae939a

Please sign in to comment.