use crate::{
builtins::BuiltInObject,
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::Attribute,
realm::Realm,
string::utf16,
Context, JsArgs, JsResult, JsValue,
};
use boa_profiler::Profiler;
pub(crate) mod aggregate;
pub(crate) mod eval;
pub(crate) mod range;
pub(crate) mod reference;
pub(crate) mod syntax;
pub(crate) mod r#type;
pub(crate) mod uri;
#[cfg(test)]
mod tests;
pub(crate) use self::aggregate::AggregateError;
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::uri::UriError;
use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ErrorKind {
Aggregate,
Error,
Eval,
Type,
Range,
Reference,
Syntax,
Uri,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Error;
impl IntrinsicObject for Error {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(utf16!("name"), Self::NAME, attribute)
.property(utf16!("message"), "", attribute)
.method(Self::to_string, "toString", 0)
.build();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
}
}
impl BuiltInObject for Error {
const NAME: &'static str = "Error";
}
impl BuiltInConstructor for Error {
const LENGTH: usize = 1;
const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
StandardConstructors::error;
fn constructor(
new_target: &JsValue,
args: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
let new_target = &if new_target.is_undefined() {
context
.vm
.active_function
.clone()
.unwrap_or_else(|| context.intrinsics().constructors().error().constructor())
.into()
} else {
new_target.clone()
};
let prototype =
get_prototype_from_constructor(new_target, StandardConstructors::error, context)?;
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::error(ErrorKind::Error),
);
let message = args.get_or_undefined(0);
if !message.is_undefined() {
let msg = message.to_string(context)?;
o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context);
}
Self::install_error_cause(&o, args.get_or_undefined(1), context)?;
Ok(o.into())
}
}
impl Error {
pub(crate) fn install_error_cause(
o: &JsObject,
options: &JsValue,
context: &mut Context<'_>,
) -> JsResult<()> {
if let Some(options) = options.as_object() {
if options.has_property(utf16!("cause"), context)? {
let cause = options.get(utf16!("cause"), context)?;
o.create_non_enumerable_data_property_or_throw(utf16!("cause"), cause, context);
}
}
Ok(())
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(
this: &JsValue,
_: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
let o = this
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("'this' is not an Object"))?;
let name = o.get(js_string!("name"), context)?;
let name = if name.is_undefined() {
js_string!("Error")
} else {
name.to_string(context)?
};
let msg = o.get(js_string!("message"), context)?;
let msg = if msg.is_undefined() {
js_string!()
} else {
msg.to_string(context)?
};
if name.is_empty() {
return Ok(msg.into());
}
if msg.is_empty() {
return Ok(name.into());
}
Ok(js_string!(&name, utf16!(": "), &msg).into())
}
}