diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index e094d7b0ab..4cb9c7c5ae 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -23,7 +23,7 @@ pub(crate) mod range; pub(crate) mod reference; pub(crate) mod syntax; pub(crate) mod r#type; -// pub(crate) mod uri; +pub(crate) mod uri; #[cfg(test)] mod tests; @@ -33,7 +33,7 @@ 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::uri::UriError; +pub(crate) use self::uri::UriError; /// Built-in `Error` object. #[derive(Debug, Clone, Copy)] diff --git a/boa/src/builtins/error/tests.rs b/boa/src/builtins/error/tests.rs index 5596ce4d50..5332f706b0 100644 --- a/boa/src/builtins/error/tests.rs +++ b/boa/src/builtins/error/tests.rs @@ -53,3 +53,28 @@ fn eval_error_to_string() { "\"EvalError\"" ); } + +#[test] +fn uri_error_name() { + let mut ctx = Context::new(); + assert_eq!(forward(&mut ctx, "URIError.name"), "\"URIError\""); +} + +#[test] +fn uri_error_length() { + let mut ctx = Context::new(); + assert_eq!(forward(&mut ctx, "URIError.length"), "1"); +} + +#[test] +fn uri_error_to_string() { + let mut ctx = Context::new(); + assert_eq!( + forward(&mut ctx, "new URIError('hello').toString()"), + "\"URIError: hello\"" + ); + assert_eq!( + forward(&mut ctx, "new URIError().toString()"), + "\"URIError\"" + ); +} diff --git a/boa/src/builtins/error/uri.rs b/boa/src/builtins/error/uri.rs new file mode 100644 index 0000000000..ca8c860328 --- /dev/null +++ b/boa/src/builtins/error/uri.rs @@ -0,0 +1,68 @@ +//! This module implements the global `URIError` object. +//! +//! The `URIError` object represents an error when a global URI handling +//! function was used in a wrong way. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError + +use crate::{ + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, + profiler::BoaProfiler, + property::Attribute, + Context, Result, Value, +}; + +/// JavaScript `URIError` impleentation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct UriError; + +impl BuiltIn for UriError { + const NAME: &'static str = "URIError"; + + 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 uri_error_object = ConstructorBuilder::with_standard_object( + context, + Self::constructor, + context.standard_objects().uri_error_object().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype.into()) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build(); + + (Self::NAME, uri_error_object.into(), Self::attribute()) + } +} + +impl UriError { + /// 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/mod.rs b/boa/src/builtins/mod.rs index 751877cfd2..594b54f8f2 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, EvalError, RangeError, ReferenceError, SyntaxError, TypeError}, + error::{Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, UriError}, function::BuiltInFunctionObject, global_this::GlobalThis, infinity::Infinity, @@ -83,6 +83,7 @@ pub fn init(context: &mut Context) { TypeError::init, SyntaxError::init, EvalError::init, + UriError::init, #[cfg(feature = "console")] console::Console::init, ]; diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 2152ed2495..011fc2d607 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -156,7 +156,7 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties pub fn define_properties(_: &Value, args: &[Value], ctx: &mut Context) -> Result { - let arg = args.get(0).cloned().unwrap_or(Value::undefined()); + let arg = args.get(0).cloned().unwrap_or_default(); let arg_obj = arg.as_object_mut(); if let Some(mut obj) = arg_obj { let props = args.get(1).cloned().unwrap_or_else(Value::undefined); diff --git a/boa/src/context.rs b/boa/src/context.rs index 9e9bd788e0..314c37da4a 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -82,6 +82,7 @@ pub struct StandardObjects { range_error: StandardConstructor, syntax_error: StandardConstructor, eval_error: StandardConstructor, + uri_error: StandardConstructor, } impl StandardObjects { @@ -159,6 +160,10 @@ impl StandardObjects { pub fn eval_error_object(&self) -> &StandardConstructor { &self.eval_error } + + pub fn uri_error_object(&self) -> &StandardConstructor { + &self.uri_error + } } /// Javascript context. It is the primary way to interact with the runtime. @@ -393,6 +398,19 @@ impl Context { .expect("Into used as message") } + /// Constructs a `URIError` with the specified message. + pub fn construct_uri_error(&mut self, message: M) -> Value + where + M: Into, + { + New::from(Call::new( + Identifier::from("URIError"), + 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 @@ -401,6 +419,14 @@ impl Context { Err(self.construct_eval_error(message)) } + /// Throws a `URIError` with the specified message. + pub fn throw_uri_error(&mut self, message: M) -> Result + where + M: Into, + { + Err(self.construct_uri_error(message)) + } + /// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions pub(crate) fn create_function( &mut self,