diff --git a/boa/src/builtins/error/eval.rs b/boa/src/builtins/error/eval.rs new file mode 100644 index 0000000000..6845e8a9d6 --- /dev/null +++ b/boa/src/builtins/error/eval.rs @@ -0,0 +1,69 @@ +//! This module implements the global `EvalError` object. +//! +//! Indicates an error regarding the global `eval()` function. +//! This exception is not thrown by JavaScript anymore, however +//! the `EvalError` object remains for compatibility. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError + +use crate::{ + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, + profiler::BoaProfiler, + property::Attribute, + Context, Result, Value, +}; + +/// JavaScript `EvalError` impleentation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct EvalError; + +impl BuiltIn for EvalError { + const NAME: &'static str = "EvalError"; + + fn attribute() -> Attribute { + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE + } + + fn init(context: &mut Context) -> (&'static str, Value, Attribute) { + let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + + let error_prototype = context.standard_objects().error_object().prototype(); + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + let eval_error_object = ConstructorBuilder::with_standard_object( + context, + Self::constructor, + context.standard_objects().eval_error_object().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype.into()) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build(); + + (Self::NAME, eval_error_object.into(), Self::attribute()) + } +} + +impl EvalError { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// Create a new error object. + pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result { + if let Some(message) = args.get(0) { + this.set_field("message", message.to_string(ctx)?); + } + + // 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); + Ok(this.clone()) + } +} diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index ee819458af..e094d7b0ab 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -18,21 +18,21 @@ use crate::{ Context, Result, Value, }; +pub(crate) mod eval; pub(crate) mod range; pub(crate) mod reference; pub(crate) mod syntax; pub(crate) mod r#type; -// pub(crate) mod eval; // pub(crate) mod uri; #[cfg(test)] mod tests; +pub(crate) use self::eval::EvalError; pub(crate) use self::r#type::TypeError; pub(crate) use self::range::RangeError; pub(crate) use self::reference::ReferenceError; pub(crate) use self::syntax::SyntaxError; -// pub(crate) use self::eval::EvalError; // pub(crate) use self::uri::UriError; /// Built-in `Error` object. diff --git a/boa/src/builtins/error/tests.rs b/boa/src/builtins/error/tests.rs index c0c1d4c997..5596ce4d50 100644 --- a/boa/src/builtins/error/tests.rs +++ b/boa/src/builtins/error/tests.rs @@ -28,3 +28,28 @@ fn error_to_string() { ); assert_eq!(forward(&mut ctx, "type_e.toString()"), "\"TypeError: 5\""); } + +#[test] +fn eval_error_name() { + let mut ctx = Context::new(); + assert_eq!(forward(&mut ctx, "EvalError.name"), "\"EvalError\""); +} + +#[test] +fn eval_error_length() { + let mut ctx = Context::new(); + assert_eq!(forward(&mut ctx, "EvalError.length"), "1"); +} + +#[test] +fn eval_error_to_string() { + let mut ctx = Context::new(); + assert_eq!( + forward(&mut ctx, "new EvalError('hello').toString()"), + "\"EvalError: hello\"" + ); + assert_eq!( + forward(&mut ctx, "new EvalError().toString()"), + "\"EvalError\"" + ); +} diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index d3fe1038f1..751877cfd2 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -27,7 +27,7 @@ pub(crate) use self::{ bigint::BigInt, boolean::Boolean, date::Date, - error::{Error, RangeError, ReferenceError, SyntaxError, TypeError}, + error::{Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError}, function::BuiltInFunctionObject, global_this::GlobalThis, infinity::Infinity, @@ -82,6 +82,7 @@ pub fn init(context: &mut Context) { ReferenceError::init, TypeError::init, SyntaxError::init, + EvalError::init, #[cfg(feature = "console")] console::Console::init, ]; diff --git a/boa/src/context.rs b/boa/src/context.rs index f75cf43835..9e9bd788e0 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -81,6 +81,7 @@ pub struct StandardObjects { referece_error: StandardConstructor, range_error: StandardConstructor, syntax_error: StandardConstructor, + eval_error: StandardConstructor, } impl StandardObjects { @@ -153,6 +154,11 @@ impl StandardObjects { pub fn syntax_error_object(&self) -> &StandardConstructor { &self.syntax_error } + + #[inline] + pub fn eval_error_object(&self) -> &StandardConstructor { + &self.eval_error + } } /// Javascript context. It is the primary way to interact with the runtime. @@ -374,6 +380,27 @@ impl Context { Err(self.construct_syntax_error(message)) } + /// Constructs a `EvalError` with the specified message. + pub fn construct_eval_error(&mut self, message: M) -> Value + where + M: Into, + { + New::from(Call::new( + Identifier::from("EvalError"), + vec![Const::from(message.into()).into()], + )) + .run(self) + .expect("Into used as message") + } + + /// Throws a `EvalError` with the specified message. + pub fn throw_eval_error(&mut self, message: M) -> Result + where + M: Into, + { + Err(self.construct_eval_error(message)) + } + /// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions pub(crate) fn create_function( &mut self,