From d8b8066a5818bddb52590bce65007e28fae993ed Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 18 Jul 2024 20:16:32 -0700 Subject: [PATCH] Add a js_error! macro to create opaque errors (#3920) * Add a js_error! macro to create opaque errors And as a bonus since I needed it, simplify comparison of strings and add PartialEq<&str> for JsString. * Add PartialEq for JsStr. PartialEq<&str> uses the other * Cargo clippy * Remove conversion of non-string literals and remove From for JsValue --- core/engine/src/error.rs | 44 +++++++++++++++++++++++++++++++++++++++- core/string/src/lib.rs | 21 ++++++++----------- core/string/src/str.rs | 17 ++++++++++++++++ 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/core/engine/src/error.rs b/core/engine/src/error.rs index 4ad9c62293..a22780bd85 100644 --- a/core/engine/src/error.rs +++ b/core/engine/src/error.rs @@ -14,6 +14,48 @@ use boa_gc::{custom_trace, Finalize, Trace}; use boa_macros::js_str; use thiserror::Error; +/// Create an opaque error object from a value or string literal. +/// +/// Can be used with an expression that converts into `JsValue` or a format +/// string with arguments. +/// +/// # Examples +/// +/// ``` +/// # use boa_engine::{js_str, Context, JsValue}; +/// use boa_engine::{js_error}; +/// let context = &mut Context::default(); +/// +/// let error = js_error!("error!"); +/// assert!(error.as_opaque().is_some()); +/// assert_eq!(error.as_opaque().unwrap().to_string(context).unwrap(), "error!"); +/// +/// let error = js_error!("error: {}", 5); +/// assert_eq!(error.as_opaque().unwrap().to_string(context).unwrap(), "error: 5"); +/// +/// // Non-string literals must be used as an expression. +/// let error = js_error!({ true }); +/// assert_eq!(error.as_opaque().unwrap(), &JsValue::from(true)); +/// ``` +#[macro_export] +macro_rules! js_error { + ($value: literal) => { + $crate::JsError::from_opaque($crate::JsValue::from( + $crate::js_string!($value) + )) + }; + ($value: expr) => { + $crate::JsError::from_opaque( + $crate::JsValue::from($value) + ) + }; + ($value: literal $(, $args: tt)* $(,)?) => { + $crate::JsError::from_opaque($crate::JsValue::from( + $crate::JsString::from(format!($value $(, $args)*)) + )) + }; +} + /// The error type returned by all operations related /// to the execution of Javascript code. /// @@ -352,7 +394,7 @@ impl JsError { /// /// assert!(error.as_native().is_some()); /// - /// let error = JsError::from_opaque(JsValue::undefined().into()); + /// let error = JsError::from_opaque(JsValue::undefined()); /// /// assert!(error.as_native().is_none()); /// ``` diff --git a/core/string/src/lib.rs b/core/string/src/lib.rs index 01a571cbab..a93cd29872 100644 --- a/core/string/src/lib.rs +++ b/core/string/src/lib.rs @@ -1073,19 +1073,14 @@ impl PartialEq<[u16; N]> for JsString { impl PartialEq for JsString { #[inline] fn eq(&self, other: &str) -> bool { - let utf16 = self.code_points(); - let mut utf8 = other.chars(); - - for lhs in utf16 { - if let Some(rhs) = utf8.next() { - match lhs { - CodePoint::Unicode(lhs) if lhs == rhs => continue, - _ => return false, - } - } - return false; - } - utf8.next().is_none() + self.as_str() == other + } +} + +impl PartialEq<&str> for JsString { + #[inline] + fn eq(&self, other: &&str) -> bool { + self.as_str() == *other } } diff --git a/core/string/src/str.rs b/core/string/src/str.rs index 5a1f24efd3..d851307dba 100644 --- a/core/string/src/str.rs +++ b/core/string/src/str.rs @@ -291,6 +291,23 @@ impl PartialEq for JsStr<'_> { } } +impl PartialEq for JsStr<'_> { + #[inline] + fn eq(&self, other: &str) -> bool { + match self.variant() { + JsStrVariant::Latin1(v) => v == other.as_bytes(), + JsStrVariant::Utf16(v) => other.encode_utf16().zip(v).all(|(a, b)| a == *b), + } + } +} + +impl PartialEq<&str> for JsStr<'_> { + #[inline] + fn eq(&self, other: &&str) -> bool { + self == *other + } +} + impl<'a> PartialEq> for [u16] { #[inline] fn eq(&self, other: &JsStr<'a>) -> bool {