From ce6cfa8b2f7c35f77a5d87b718bd8a439ef2cb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Wed, 8 Jan 2025 10:02:02 -0600 Subject: [PATCH] Implement `Error.isError` (#4114) --- core/engine/src/builtins/error/aggregate.rs | 4 +- core/engine/src/builtins/error/eval.rs | 4 +- core/engine/src/builtins/error/mod.rs | 44 +++++++++--- core/engine/src/builtins/error/range.rs | 4 +- core/engine/src/builtins/error/reference.rs | 4 +- core/engine/src/builtins/error/syntax.rs | 4 +- core/engine/src/builtins/error/type.rs | 4 +- core/engine/src/builtins/error/uri.rs | 4 +- core/engine/src/builtins/object/mod.rs | 4 +- core/engine/src/error.rs | 74 ++++++++++----------- core/engine/src/value/display.rs | 4 +- core/runtime/src/lib.rs | 2 +- test262_config.toml | 5 +- 13 files changed, 87 insertions(+), 74 deletions(-) diff --git a/core/engine/src/builtins/error/aggregate.rs b/core/engine/src/builtins/error/aggregate.rs index 892bba7f4d0..0f09f11f558 100644 --- a/core/engine/src/builtins/error/aggregate.rs +++ b/core/engine/src/builtins/error/aggregate.rs @@ -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; @@ -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 diff --git a/core/engine/src/builtins/error/eval.rs b/core/engine/src/builtins/error/eval.rs index df2715bf7fe..3a11cd73ae8 100644 --- a/core/engine/src/builtins/error/eval.rs +++ b/core/engine/src/builtins/error/eval.rs @@ -23,7 +23,7 @@ use crate::{ }; use boa_profiler::Profiler; -use super::{Error, ErrorObject}; +use super::Error; /// JavaScript `EvalError` implementation. #[derive(Debug, Clone, Copy)] @@ -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 diff --git a/core/engine/src/builtins/error/mod.rs b/core/engine/src/builtins/error/mod.rs index 7eb85b9c646..995cf52013d 100644 --- a/core/engine/src/builtins/error/mod.rs +++ b/core/engine/src/builtins/error/mod.rs @@ -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` @@ -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: @@ -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::(), "init"); let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - BuiltInBuilder::from_standard_constructor::(realm) + let builder = BuiltInBuilder::from_standard_constructor::(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 { @@ -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], @@ -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 @@ -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 { + // 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::()) + .into()) + } } diff --git a/core/engine/src/builtins/error/range.rs b/core/engine/src/builtins/error/range.rs index 59f6ab4eae7..1683adff198 100644 --- a/core/engine/src/builtins/error/range.rs +++ b/core/engine/src/builtins/error/range.rs @@ -21,7 +21,7 @@ use crate::{ }; use boa_profiler::Profiler; -use super::{Error, ErrorObject}; +use super::Error; /// JavaScript `RangeError` implementation. #[derive(Debug, Clone, Copy)] @@ -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 diff --git a/core/engine/src/builtins/error/reference.rs b/core/engine/src/builtins/error/reference.rs index b10e1ef761c..866f418067c 100644 --- a/core/engine/src/builtins/error/reference.rs +++ b/core/engine/src/builtins/error/reference.rs @@ -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; @@ -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 diff --git a/core/engine/src/builtins/error/syntax.rs b/core/engine/src/builtins/error/syntax.rs index fddc1e558e5..9fec4dfa37a 100644 --- a/core/engine/src/builtins/error/syntax.rs +++ b/core/engine/src/builtins/error/syntax.rs @@ -23,7 +23,7 @@ use crate::{ }; use boa_profiler::Profiler; -use super::{Error, ErrorObject}; +use super::Error; /// JavaScript `SyntaxError` implementation. #[derive(Debug, Clone, Copy)] @@ -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 diff --git a/core/engine/src/builtins/error/type.rs b/core/engine/src/builtins/error/type.rs index 1e7fbd3befa..e105d73af18 100644 --- a/core/engine/src/builtins/error/type.rs +++ b/core/engine/src/builtins/error/type.rs @@ -29,7 +29,7 @@ use crate::{ }; use boa_profiler::Profiler; -use super::{Error, ErrorObject}; +use super::Error; /// JavaScript `TypeError` implementation. #[derive(Debug, Clone, Copy)] @@ -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 diff --git a/core/engine/src/builtins/error/uri.rs b/core/engine/src/builtins/error/uri.rs index 6f1eaed5a2b..43fd8ce6695 100644 --- a/core/engine/src/builtins/error/uri.rs +++ b/core/engine/src/builtins/error/uri.rs @@ -22,7 +22,7 @@ use crate::{ }; use boa_profiler::Profiler; -use super::{Error, ErrorObject}; +use super::Error; /// JavaScript `URIError` implementation. #[derive(Debug, Clone, Copy)] @@ -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 diff --git a/core/engine/src/builtins/object/mod.rs b/core/engine/src/builtins/object/mod.rs index d820da17b08..c46792c3b46 100644 --- a/core/engine/src/builtins/object/mod.rs +++ b/core/engine/src/builtins/object/mod.rs @@ -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::{ @@ -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::() { + } else if o.is::() { // 8. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error". js_str!("Error") } else if o.is::() { diff --git a/core/engine/src/error.rs b/core/engine/src/error.rs index c88ac2d6c20..c3f64d783e2 100644 --- a/core/engine/src/error.rs +++ b/core/engine/src/error.rs @@ -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, @@ -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::()); + /// assert!(error_val.as_object().unwrap().is::()); /// ``` pub fn to_opaque(&self, context: &mut Context) -> JsValue { match &self.inner { @@ -392,7 +392,7 @@ impl JsError { .as_object() .ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?; let error = *obj - .downcast_ref::() + .downcast_ref::() .ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?; let try_get_property = |key: JsString, name, context: &mut Context| { @@ -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", @@ -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::()); + /// assert!(error_obj.is::()); /// assert_eq!( /// error_obj.get(js_string!("message"), context).unwrap(), /// js_string!("error!").into() @@ -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!( @@ -1318,18 +1312,18 @@ impl JsNativeErrorKind { } } -impl PartialEq for JsNativeErrorKind { - fn eq(&self, other: &ErrorObject) -> bool { +impl PartialEq 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) ) } } diff --git a/core/engine/src/value/display.rs b/core/engine/src/value/display.rs index 82d48a4d53c..a83048c1b8c 100644 --- a/core/engine/src/value/display.rs +++ b/core/engine/src/value/display.rs @@ -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, @@ -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::() { + } else if v_bor.is::() { drop(v_bor); let name: Cow<'static, str> = v .get_property(&js_string!("name").into()) diff --git a/core/runtime/src/lib.rs b/core/runtime/src/lib.rs index f5f501dc138..96a35626b7f 100644 --- a/core/runtime/src/lib.rs +++ b/core/runtime/src/lib.rs @@ -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 { diff --git a/test262_config.toml b/test262_config.toml index b983d776fed..a9555448817 100644 --- a/test262_config.toml +++ b/test262_config.toml @@ -16,9 +16,6 @@ features = [ ### Pending proposals - # https://github.com/tc39/proposal-is-error - "Error.isError", - # https://github.com/tc39/proposal-intl-locale-info "Intl.Locale-info", @@ -32,7 +29,7 @@ features = [ # https://github.com/tc39/proposal-import-attributes "import-assertions", - # https://tc39.es/proposal-defer-import-eval + # https://github.com/tc39/proposal-defer-import-eval "import-defer", # https://github.com/tc39/proposal-iterator-sequencing