Skip to content

Commit

Permalink
Implement Error.isError (#4114)
Browse files Browse the repository at this point in the history
  • Loading branch information
jedel1043 authored Jan 8, 2025
1 parent 103c66e commit ce6cfa8
Show file tree
Hide file tree
Showing 13 changed files with 87 additions and 74 deletions.
4 changes: 2 additions & 2 deletions core/engine/src/builtins/error/aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
};
use boa_profiler::Profiler;

use super::{Error, ErrorObject};
use super::Error;

#[derive(Debug, Clone, Copy)]
pub(crate) struct AggregateError;
Expand Down Expand Up @@ -91,7 +91,7 @@ impl BuiltInConstructor for AggregateError {
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ErrorObject::Aggregate,
Error::Aggregate,
);

// 3. If message is not undefined, then
Expand Down
4 changes: 2 additions & 2 deletions core/engine/src/builtins/error/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
};
use boa_profiler::Profiler;

use super::{Error, ErrorObject};
use super::Error;

/// JavaScript `EvalError` implementation.
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -86,7 +86,7 @@ impl BuiltInConstructor for EvalError {
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ErrorObject::Eval,
Error::Eval,
);

// 3. If message is not undefined, then
Expand Down
44 changes: 33 additions & 11 deletions core/engine/src/builtins/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub(crate) use self::uri::UriError;

use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};

/// A `NativeError` object, per the [ECMAScript spec][spec].
/// A built-in `Error` object, per the [ECMAScript spec][spec].
///
/// This is used internally to convert between [`JsObject`] and
/// [`JsNativeError`] correctly, but it can also be used to manually create `Error`
Expand All @@ -61,7 +61,7 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Trace, Finalize, JsData)]
#[boa_gc(empty_trace)]
#[non_exhaustive]
pub enum ErrorObject {
pub enum Error {
/// The `AggregateError` object type.
///
/// More information:
Expand Down Expand Up @@ -127,20 +127,20 @@ pub enum ErrorObject {
Uri,
}

/// Built-in `Error` object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct Error;

impl IntrinsicObject for Error {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");

let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm)
let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)
.property(js_string!("name"), Self::NAME, attribute)
.property(js_string!("message"), js_string!(), attribute)
.method(Self::to_string, js_string!("toString"), 0)
.build();
.method(Self::to_string, js_string!("toString"), 0);

#[cfg(feature = "experimental")]
let builder = builder.static_method(Error::is_error, js_string!("isError"), 1);

builder.build();
}

fn get(intrinsics: &Intrinsics) -> JsObject {
Expand All @@ -162,7 +162,7 @@ impl BuiltInConstructor for Error {

/// `Error( message [ , options ] )`
///
/// Create a new error object.
/// Creates a new error object.
fn constructor(
new_target: &JsValue,
args: &[JsValue],
Expand All @@ -184,7 +184,7 @@ impl BuiltInConstructor for Error {
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ErrorObject::Error,
Error::Error,
);

// 3. If message is not undefined, then
Expand Down Expand Up @@ -280,4 +280,26 @@ impl Error {
// the code unit 0x0020 (SPACE), and msg.
Ok(js_string!(&name, js_str!(": "), &msg).into())
}

/// [`Error.isError`][spec].
///
/// Returns a boolean indicating whether the argument is a built-in Error instance or not.
///
/// [spec]: https://tc39.es/proposal-is-error/#sec-error.iserror
#[cfg(feature = "experimental")]
#[allow(clippy::unnecessary_wraps)]
fn is_error(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
// 1. Return IsError(arg).

// https://tc39.es/proposal-is-error/#sec-iserror

// 1. If argument is not an Object, return false.
// 2. If argument has an [[ErrorData]] internal slot, return true.
// 3. Return false.
Ok(args
.get_or_undefined(0)
.as_object()
.is_some_and(|o| o.is::<Self>())
.into())
}
}
4 changes: 2 additions & 2 deletions core/engine/src/builtins/error/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
};
use boa_profiler::Profiler;

use super::{Error, ErrorObject};
use super::Error;

/// JavaScript `RangeError` implementation.
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -84,7 +84,7 @@ impl BuiltInConstructor for RangeError {
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ErrorObject::Range,
Error::Range,
);

// 3. If message is not undefined, then
Expand Down
4 changes: 2 additions & 2 deletions core/engine/src/builtins/error/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
};
use boa_profiler::Profiler;

use super::{Error, ErrorObject};
use super::Error;

#[derive(Debug, Clone, Copy)]
pub(crate) struct ReferenceError;
Expand Down Expand Up @@ -86,7 +86,7 @@ impl BuiltInConstructor for ReferenceError {
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ErrorObject::Reference,
Error::Reference,
);

// 3. If message is not undefined, then
Expand Down
4 changes: 2 additions & 2 deletions core/engine/src/builtins/error/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
};
use boa_profiler::Profiler;

use super::{Error, ErrorObject};
use super::Error;

/// JavaScript `SyntaxError` implementation.
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -89,7 +89,7 @@ impl BuiltInConstructor for SyntaxError {
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ErrorObject::Syntax,
Error::Syntax,
);

// 3. If message is not undefined, then
Expand Down
4 changes: 2 additions & 2 deletions core/engine/src/builtins/error/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::{
};
use boa_profiler::Profiler;

use super::{Error, ErrorObject};
use super::Error;

/// JavaScript `TypeError` implementation.
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -92,7 +92,7 @@ impl BuiltInConstructor for TypeError {
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ErrorObject::Type,
Error::Type,
);

// 3. If message is not undefined, then
Expand Down
4 changes: 2 additions & 2 deletions core/engine/src/builtins/error/uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
};
use boa_profiler::Profiler;

use super::{Error, ErrorObject};
use super::Error;

/// JavaScript `URIError` implementation.
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -85,7 +85,7 @@ impl BuiltInConstructor for UriError {
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ErrorObject::Uri,
Error::Uri,
);

// 3. If message is not undefined, then
Expand Down
4 changes: 2 additions & 2 deletions core/engine/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
use super::{
error::ErrorObject, Array, BuiltInBuilder, BuiltInConstructor, Date, IntrinsicObject, RegExp,
error::Error, Array, BuiltInBuilder, BuiltInConstructor, Date, IntrinsicObject, RegExp,
};
use crate::value::JsVariant;
use crate::{
Expand Down Expand Up @@ -850,7 +850,7 @@ impl OrdinaryObject {
} else if o.is_callable() {
// 7. Else if O has a [[Call]] internal method, let builtinTag be "Function".
js_str!("Function")
} else if o.is::<ErrorObject>() {
} else if o.is::<Error>() {
// 8. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
js_str!("Error")
} else if o.is::<bool>() {
Expand Down
74 changes: 34 additions & 40 deletions core/engine/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Error-related types and conversions.
use crate::{
builtins::{error::ErrorObject, Array},
builtins::{error::Error, Array},
js_string,
object::JsObject,
property::PropertyDescriptor,
Expand Down Expand Up @@ -332,12 +332,12 @@ impl JsError {
///
/// ```rust
/// # use boa_engine::{Context, JsError, JsNativeError};
/// # use boa_engine::builtins::error::ErrorObject;
/// # use boa_engine::builtins::error::Error;
/// let context = &mut Context::default();
/// let error: JsError = JsNativeError::eval().with_message("invalid script").into();
/// let error_val = error.to_opaque(context);
///
/// assert!(error_val.as_object().unwrap().is::<ErrorObject>());
/// assert!(error_val.as_object().unwrap().is::<Error>());
/// ```
pub fn to_opaque(&self, context: &mut Context) -> JsValue {
match &self.inner {
Expand Down Expand Up @@ -392,7 +392,7 @@ impl JsError {
.as_object()
.ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?;
let error = *obj
.downcast_ref::<ErrorObject>()
.downcast_ref::<Error>()
.ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?;

let try_get_property = |key: JsString, name, context: &mut Context| {
Expand Down Expand Up @@ -420,14 +420,14 @@ impl JsError {
let cause = try_get_property(js_string!("cause"), "cause", context)?;

let kind = match error {
ErrorObject::Error => JsNativeErrorKind::Error,
ErrorObject::Eval => JsNativeErrorKind::Eval,
ErrorObject::Type => JsNativeErrorKind::Type,
ErrorObject::Range => JsNativeErrorKind::Range,
ErrorObject::Reference => JsNativeErrorKind::Reference,
ErrorObject::Syntax => JsNativeErrorKind::Syntax,
ErrorObject::Uri => JsNativeErrorKind::Uri,
ErrorObject::Aggregate => {
Error::Error => JsNativeErrorKind::Error,
Error::Eval => JsNativeErrorKind::Eval,
Error::Type => JsNativeErrorKind::Type,
Error::Range => JsNativeErrorKind::Range,
Error::Reference => JsNativeErrorKind::Reference,
Error::Syntax => JsNativeErrorKind::Syntax,
Error::Uri => JsNativeErrorKind::Uri,
Error::Aggregate => {
let errors = obj.get(js_string!("errors"), context).map_err(|e| {
TryNativeError::InaccessibleProperty {
property: "errors",
Expand Down Expand Up @@ -1066,13 +1066,13 @@ impl JsNativeError {
///
/// ```rust
/// # use boa_engine::{Context, JsError, JsNativeError, js_string};
/// # use boa_engine::builtins::error::ErrorObject;
/// # use boa_engine::builtins::error::Error;
/// let context = &mut Context::default();
///
/// let error = JsNativeError::error().with_message("error!");
/// let error_obj = error.to_opaque(context);
///
/// assert!(error_obj.is::<ErrorObject>());
/// assert!(error_obj.is::<Error>());
/// assert_eq!(
/// error_obj.get(js_string!("message"), context).unwrap(),
/// js_string!("error!").into()
Expand All @@ -1095,24 +1095,18 @@ impl JsNativeError {
|realm| realm.intrinsics().constructors(),
);
let (prototype, tag) = match kind {
JsNativeErrorKind::Aggregate(_) => (
constructors.aggregate_error().prototype(),
ErrorObject::Aggregate,
),
JsNativeErrorKind::Error => (constructors.error().prototype(), ErrorObject::Error),
JsNativeErrorKind::Eval => (constructors.eval_error().prototype(), ErrorObject::Eval),
JsNativeErrorKind::Range => {
(constructors.range_error().prototype(), ErrorObject::Range)
JsNativeErrorKind::Aggregate(_) => {
(constructors.aggregate_error().prototype(), Error::Aggregate)
}
JsNativeErrorKind::Reference => (
constructors.reference_error().prototype(),
ErrorObject::Reference,
),
JsNativeErrorKind::Syntax => {
(constructors.syntax_error().prototype(), ErrorObject::Syntax)
JsNativeErrorKind::Error => (constructors.error().prototype(), Error::Error),
JsNativeErrorKind::Eval => (constructors.eval_error().prototype(), Error::Eval),
JsNativeErrorKind::Range => (constructors.range_error().prototype(), Error::Range),
JsNativeErrorKind::Reference => {
(constructors.reference_error().prototype(), Error::Reference)
}
JsNativeErrorKind::Type => (constructors.type_error().prototype(), ErrorObject::Type),
JsNativeErrorKind::Uri => (constructors.uri_error().prototype(), ErrorObject::Uri),
JsNativeErrorKind::Syntax => (constructors.syntax_error().prototype(), Error::Syntax),
JsNativeErrorKind::Type => (constructors.type_error().prototype(), Error::Type),
JsNativeErrorKind::Uri => (constructors.uri_error().prototype(), Error::Uri),
#[cfg(feature = "fuzz")]
JsNativeErrorKind::NoInstructionsRemain => {
unreachable!(
Expand Down Expand Up @@ -1318,18 +1312,18 @@ impl JsNativeErrorKind {
}
}

impl PartialEq<ErrorObject> for JsNativeErrorKind {
fn eq(&self, other: &ErrorObject) -> bool {
impl PartialEq<Error> for JsNativeErrorKind {
fn eq(&self, other: &Error) -> bool {
matches!(
(self, other),
(Self::Aggregate(_), ErrorObject::Aggregate)
| (Self::Error, ErrorObject::Error)
| (Self::Eval, ErrorObject::Eval)
| (Self::Range, ErrorObject::Range)
| (Self::Reference, ErrorObject::Reference)
| (Self::Syntax, ErrorObject::Syntax)
| (Self::Type, ErrorObject::Type)
| (Self::Uri, ErrorObject::Uri)
(Self::Aggregate(_), Error::Aggregate)
| (Self::Error, Error::Error)
| (Self::Eval, Error::Eval)
| (Self::Range, Error::Range)
| (Self::Reference, Error::Reference)
| (Self::Syntax, Error::Syntax)
| (Self::Type, Error::Type)
| (Self::Uri, Error::Uri)
)
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/engine/src/value/display.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{fmt, Display, HashSet, JsValue, JsVariant};
use crate::{
builtins::{
error::ErrorObject, map::ordered_map::OrderedMap, promise::PromiseState,
error::Error, map::ordered_map::OrderedMap, promise::PromiseState,
set::ordered_set::OrderedSet, Array, Promise,
},
js_string,
Expand Down Expand Up @@ -191,7 +191,7 @@ pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children
} else {
format!("Set({size})")
}
} else if v_bor.is::<ErrorObject>() {
} else if v_bor.is::<Error>() {
drop(v_bor);
let name: Cow<'static, str> = v
.get_property(&js_string!("name").into())
Expand Down
2 changes: 1 addition & 1 deletion core/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ pub(crate) mod test {
},
AssertNativeError {
source: Cow<'static, str>,
kind: builtins::error::ErrorObject,
kind: builtins::error::Error,
message: &'static str,
},
AssertContext {
Expand Down
Loading

0 comments on commit ce6cfa8

Please sign in to comment.