Browse Source

Some optimizations on `Error` (#4020)

* chore: mark `JsNativeError`'s constructors const

* chore: add associated constants

* chore: replace const method calls in `js_error` with corresponding constants

* fix: use correct method

* perf: change error's `message` type to `Cow<'static str>`

* chore: fix lint
pull/4027/head
CrazyboyQCD 3 weeks ago committed by GitHub
parent
commit
4983471ff6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      core/engine/src/builtins/temporal/error.rs
  2. 133
      core/engine/src/error.rs
  3. 2
      core/gc/src/trace.rs

8
core/engine/src/builtins/temporal/error.rs

@ -5,10 +5,10 @@ use crate::{JsError, JsNativeError};
impl From<TemporalError> for JsNativeError { impl From<TemporalError> for JsNativeError {
fn from(value: TemporalError) -> Self { fn from(value: TemporalError) -> Self {
match value.kind() { match value.kind() {
ErrorKind::Range => JsNativeError::range().with_message(value.message()), ErrorKind::Range => JsNativeError::range().with_message(value.message().to_owned()),
ErrorKind::Type => JsNativeError::typ().with_message(value.message()), ErrorKind::Type => JsNativeError::typ().with_message(value.message().to_owned()),
ErrorKind::Generic => JsNativeError::error().with_message(value.message()), ErrorKind::Generic => JsNativeError::error().with_message(value.message().to_owned()),
ErrorKind::Syntax => JsNativeError::syntax().with_message(value.message()), ErrorKind::Syntax => JsNativeError::syntax().with_message(value.message().to_owned()),
ErrorKind::Assert => JsNativeError::error().with_message("internal engine error"), ErrorKind::Assert => JsNativeError::error().with_message("internal engine error"),
} }
} }

133
core/engine/src/error.rs

@ -10,7 +10,7 @@ use crate::{
}; };
use boa_gc::{custom_trace, Finalize, Trace}; use boa_gc::{custom_trace, Finalize, Trace};
use boa_macros::js_str; use boa_macros::js_str;
use std::{error, fmt}; use std::{borrow::Cow, error, fmt};
use thiserror::Error; use thiserror::Error;
/// Create an error object from a value or string literal. Optionally the /// Create an error object from a value or string literal. Optionally the
@ -59,80 +59,80 @@ use thiserror::Error;
macro_rules! js_error { macro_rules! js_error {
(Error: $value: literal) => { (Error: $value: literal) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::error().with_message($value) $crate::JsNativeError::ERROR.with_message($value)
) )
}; };
(Error: $value: literal $(, $args: expr)* $(,)?) => { (Error: $value: literal $(, $args: expr)* $(,)?) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::error() $crate::JsNativeError::ERROR
.with_message(format!($value $(, $args)*)) .with_message(format!($value $(, $args)*))
) )
}; };
(TypeError: $value: literal) => { (TypeError: $value: literal) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::typ().with_message($value) $crate::JsNativeError::TYP.with_message($value)
) )
}; };
(TypeError: $value: literal $(, $args: expr)* $(,)?) => { (TypeError: $value: literal $(, $args: expr)* $(,)?) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::typ() $crate::JsNativeError::TYP
.with_message(format!($value $(, $args)*)) .with_message(format!($value $(, $args)*))
) )
}; };
(SyntaxError: $value: literal) => { (SyntaxError: $value: literal) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::syntax().with_message($value) $crate::JsNativeError::SYNTAX.with_message($value)
) )
}; };
(SyntaxError: $value: literal $(, $args: expr)* $(,)?) => { (SyntaxError: $value: literal $(, $args: expr)* $(,)?) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::syntax().with_message(format!($value $(, $args)*)) $crate::JsNativeError::SYNTAX.with_message(format!($value $(, $args)*))
) )
}; };
(RangeError: $value: literal) => { (RangeError: $value: literal) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::range().with_message($value) $crate::JsNativeError::RANGE.with_message($value)
) )
}; };
(RangeError: $value: literal $(, $args: expr)* $(,)?) => { (RangeError: $value: literal $(, $args: expr)* $(,)?) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::range().with_message(format!($value $(, $args)*)) $crate::JsNativeError::RANGE.with_message(format!($value $(, $args)*))
) )
}; };
(EvalError: $value: literal) => { (EvalError: $value: literal) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::eval().with_message($value) $crate::JsNativeError::EVAL.with_message($value)
) )
}; };
(EvalError: $value: literal $(, $args: expr)* $(,)?) => { (EvalError: $value: literal $(, $args: expr)* $(,)?) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::eval().with_message(format!($value $(, $args)*)) $crate::JsNativeError::EVAL.with_message(format!($value $(, $args)*))
) )
}; };
(ReferenceError: $value: literal) => { (ReferenceError: $value: literal) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::reference().with_message($value) $crate::JsNativeError::REFERENCE.with_message($value)
) )
}; };
(ReferenceError: $value: literal $(, $args: expr)* $(,)?) => { (ReferenceError: $value: literal $(, $args: expr)* $(,)?) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::reference().with_message(format!($value $(, $args)*)) $crate::JsNativeError::REFERENCE.with_message(format!($value $(, $args)*))
) )
}; };
(URIError: $value: literal) => { (URIError: $value: literal) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::uri().with_message($value) $crate::JsNativeError::URI.with_message($value)
) )
}; };
(URIError: $value: literal $(, $args: expr)* $(,)?) => { (URIError: $value: literal $(, $args: expr)* $(,)?) => {
$crate::JsError::from_native( $crate::JsError::from_native(
$crate::JsNativeError::uri().with_message(format!($value $(, $args)*)) $crate::JsNativeError::URI.with_message(format!($value $(, $args)*))
) )
}; };
@ -407,14 +407,15 @@ impl JsError {
let message = if let Some(msg) = let message = if let Some(msg) =
try_get_property(js_string!("message"), "message", context)? try_get_property(js_string!("message"), "message", context)?
{ {
msg.as_string() Cow::Owned(
.map(JsString::to_std_string) msg.as_string()
.transpose() .map(JsString::to_std_string)
.map_err(|_| TryNativeError::InvalidMessageEncoding)? .transpose()
.ok_or(TryNativeError::InvalidPropertyType("message"))? .map_err(|_| TryNativeError::InvalidMessageEncoding)?
.into() .ok_or(TryNativeError::InvalidPropertyType("message"))?,
)
} else { } else {
Box::default() Cow::Borrowed("")
}; };
let cause = try_get_property(js_string!("cause"), "cause", context)?; let cause = try_get_property(js_string!("cause"), "cause", context)?;
@ -559,7 +560,7 @@ impl JsError {
pub fn into_erased(self, context: &mut Context) -> JsErasedError { pub fn into_erased(self, context: &mut Context) -> JsErasedError {
let Ok(native) = self.try_native(context) else { let Ok(native) = self.try_native(context) else {
return JsErasedError { return JsErasedError {
inner: ErasedRepr::Opaque(self.to_string().into_boxed_str()), inner: ErasedRepr::Opaque(Cow::Owned(self.to_string())),
}; };
}; };
@ -671,7 +672,7 @@ impl fmt::Display for JsError {
pub struct JsNativeError { pub struct JsNativeError {
/// The kind of native error (e.g. `TypeError`, `SyntaxError`, etc.) /// The kind of native error (e.g. `TypeError`, `SyntaxError`, etc.)
pub kind: JsNativeErrorKind, pub kind: JsNativeErrorKind,
message: Box<str>, message: Cow<'static, str>,
#[source] #[source]
cause: Option<Box<JsError>>, cause: Option<Box<JsError>>,
realm: Option<Realm>, realm: Option<Realm>,
@ -710,8 +711,34 @@ impl fmt::Debug for JsNativeError {
} }
impl JsNativeError { impl JsNativeError {
/// Default `AggregateError` kind `JsNativeError`.
pub const AGGREGATE: Self = Self::aggregate(Vec::new());
/// Default `Error` kind `JsNativeError`.
pub const ERROR: Self = Self::error();
/// Default `EvalError` kind `JsNativeError`.
pub const EVAL: Self = Self::eval();
/// Default `RangeError` kind `JsNativeError`.
pub const RANGE: Self = Self::range();
/// Default `ReferenceError` kind `JsNativeError`.
pub const REFERENCE: Self = Self::reference();
/// Default `SyntaxError` kind `JsNativeError`.
pub const SYNTAX: Self = Self::syntax();
/// Default `error` kind `JsNativeError`.
pub const TYP: Self = Self::typ();
/// Default `UriError` kind `JsNativeError`.
pub const URI: Self = Self::uri();
#[cfg(feature = "fuzz")]
/// Default `NoInstructionsRemain` kind `JsNativeError`.
pub const NO_INSTRUCTIONS_REMAIN: Self = Self::no_instructions_remain();
/// Default `error` kind `JsNativeError`.
pub const RUNTIME_LIMIT: Self = Self::runtime_limit();
/// Creates a new `JsNativeError` from its `kind`, `message` and (optionally) its `cause`. /// Creates a new `JsNativeError` from its `kind`, `message` and (optionally) its `cause`.
fn new(kind: JsNativeErrorKind, message: Box<str>, cause: Option<Box<JsError>>) -> Self { const fn new(
kind: JsNativeErrorKind,
message: Cow<'static, str>,
cause: Option<Box<JsError>>,
) -> Self {
Self { Self {
kind, kind,
message, message,
@ -740,8 +767,12 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn aggregate(errors: Vec<JsError>) -> Self { pub const fn aggregate(errors: Vec<JsError>) -> Self {
Self::new(JsNativeErrorKind::Aggregate(errors), Box::default(), None) Self::new(
JsNativeErrorKind::Aggregate(errors),
Cow::Borrowed(""),
None,
)
} }
/// Check if it's a [`JsNativeErrorKind::Aggregate`]. /// Check if it's a [`JsNativeErrorKind::Aggregate`].
@ -763,8 +794,8 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn error() -> Self { pub const fn error() -> Self {
Self::new(JsNativeErrorKind::Error, Box::default(), None) Self::new(JsNativeErrorKind::Error, Cow::Borrowed(""), None)
} }
/// Check if it's a [`JsNativeErrorKind::Error`]. /// Check if it's a [`JsNativeErrorKind::Error`].
@ -786,8 +817,8 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn eval() -> Self { pub const fn eval() -> Self {
Self::new(JsNativeErrorKind::Eval, Box::default(), None) Self::new(JsNativeErrorKind::Eval, Cow::Borrowed(""), None)
} }
/// Check if it's a [`JsNativeErrorKind::Eval`]. /// Check if it's a [`JsNativeErrorKind::Eval`].
@ -809,8 +840,8 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn range() -> Self { pub const fn range() -> Self {
Self::new(JsNativeErrorKind::Range, Box::default(), None) Self::new(JsNativeErrorKind::Range, Cow::Borrowed(""), None)
} }
/// Check if it's a [`JsNativeErrorKind::Range`]. /// Check if it's a [`JsNativeErrorKind::Range`].
@ -832,8 +863,8 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn reference() -> Self { pub const fn reference() -> Self {
Self::new(JsNativeErrorKind::Reference, Box::default(), None) Self::new(JsNativeErrorKind::Reference, Cow::Borrowed(""), None)
} }
/// Check if it's a [`JsNativeErrorKind::Reference`]. /// Check if it's a [`JsNativeErrorKind::Reference`].
@ -855,8 +886,8 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn syntax() -> Self { pub const fn syntax() -> Self {
Self::new(JsNativeErrorKind::Syntax, Box::default(), None) Self::new(JsNativeErrorKind::Syntax, Cow::Borrowed(""), None)
} }
/// Check if it's a [`JsNativeErrorKind::Syntax`]. /// Check if it's a [`JsNativeErrorKind::Syntax`].
@ -878,8 +909,8 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn typ() -> Self { pub const fn typ() -> Self {
Self::new(JsNativeErrorKind::Type, Box::default(), None) Self::new(JsNativeErrorKind::Type, Cow::Borrowed(""), None)
} }
/// Check if it's a [`JsNativeErrorKind::Type`]. /// Check if it's a [`JsNativeErrorKind::Type`].
@ -901,8 +932,8 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn uri() -> Self { pub const fn uri() -> Self {
Self::new(JsNativeErrorKind::Uri, Box::default(), None) Self::new(JsNativeErrorKind::Uri, Cow::Borrowed(""), None)
} }
/// Check if it's a [`JsNativeErrorKind::Uri`]. /// Check if it's a [`JsNativeErrorKind::Uri`].
@ -916,10 +947,10 @@ impl JsNativeError {
/// is only used in a fuzzing context. /// is only used in a fuzzing context.
#[cfg(feature = "fuzz")] #[cfg(feature = "fuzz")]
#[must_use] #[must_use]
pub fn no_instructions_remain() -> Self { pub const fn no_instructions_remain() -> Self {
Self::new( Self::new(
JsNativeErrorKind::NoInstructionsRemain, JsNativeErrorKind::NoInstructionsRemain,
Box::default(), Cow::Borrowed(""),
None, None,
) )
} }
@ -935,8 +966,8 @@ impl JsNativeError {
/// Creates a new `JsNativeError` that indicates that the context exceeded the runtime limits. /// Creates a new `JsNativeError` that indicates that the context exceeded the runtime limits.
#[must_use] #[must_use]
#[inline] #[inline]
pub fn runtime_limit() -> Self { pub const fn runtime_limit() -> Self {
Self::new(JsNativeErrorKind::RuntimeLimit, Box::default(), None) Self::new(JsNativeErrorKind::RuntimeLimit, Cow::Borrowed(""), None)
} }
/// Check if it's a [`JsNativeErrorKind::RuntimeLimit`]. /// Check if it's a [`JsNativeErrorKind::RuntimeLimit`].
@ -960,7 +991,7 @@ impl JsNativeError {
#[inline] #[inline]
pub fn with_message<S>(mut self, message: S) -> Self pub fn with_message<S>(mut self, message: S) -> Self
where where
S: Into<Box<str>>, S: Into<Cow<'static, str>>,
{ {
self.message = message.into(); self.message = message.into();
self self
@ -1004,7 +1035,7 @@ impl JsNativeError {
/// ``` /// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub const fn message(&self) -> &str { pub fn message(&self) -> &str {
&self.message &self.message
} }
@ -1099,7 +1130,7 @@ impl JsNativeError {
o.create_non_enumerable_data_property_or_throw( o.create_non_enumerable_data_property_or_throw(
js_str!("message"), js_str!("message"),
js_string!(&**message), js_string!(message.as_ref()),
context, context,
); );
@ -1340,7 +1371,7 @@ pub struct JsErasedError {
#[derive(Debug, Clone, Trace, Finalize, PartialEq, Eq)] #[derive(Debug, Clone, Trace, Finalize, PartialEq, Eq)]
enum ErasedRepr { enum ErasedRepr {
Native(JsErasedNativeError), Native(JsErasedNativeError),
Opaque(Box<str>), Opaque(Cow<'static, str>),
} }
impl fmt::Display for JsErasedError { impl fmt::Display for JsErasedError {
@ -1365,7 +1396,7 @@ impl JsErasedError {
/// Gets the inner [`str`] if the error is an opaque error, /// Gets the inner [`str`] if the error is an opaque error,
/// or `None` otherwise. /// or `None` otherwise.
#[must_use] #[must_use]
pub const fn as_opaque(&self) -> Option<&str> { pub fn as_opaque(&self) -> Option<&str> {
match self.inner { match self.inner {
ErasedRepr::Native(_) => None, ErasedRepr::Native(_) => None,
ErasedRepr::Opaque(ref v) => Some(v), ErasedRepr::Opaque(ref v) => Some(v),
@ -1388,7 +1419,7 @@ impl JsErasedError {
pub struct JsErasedNativeError { pub struct JsErasedNativeError {
/// The kind of native error (e.g. `TypeError`, `SyntaxError`, etc.) /// The kind of native error (e.g. `TypeError`, `SyntaxError`, etc.)
pub kind: JsErasedNativeErrorKind, pub kind: JsErasedNativeErrorKind,
message: Box<str>, message: Cow<'static, str>,
#[source] #[source]
cause: Option<Box<JsErasedError>>, cause: Option<Box<JsErasedError>>,
} }

2
core/gc/src/trace.rs

@ -179,7 +179,7 @@ simple_empty_finalize_trace![
char, char,
TypeId, TypeId,
String, String,
Box<str>, str,
Rc<str>, Rc<str>,
Path, Path,
PathBuf, PathBuf,

Loading…
Cancel
Save